added / fixed some types

This commit is contained in:
mrfry 2022-03-14 19:35:42 +01:00
parent 5f12284bb8
commit bc5c293539
41 changed files with 4378 additions and 8304 deletions

View file

@ -24,7 +24,6 @@ module.exports = {
'warn', 'warn',
{ allowArgumentsExplicitlyTypedAsAny: true }, { allowArgumentsExplicitlyTypedAsAny: true },
], ],
'no-unused-vars': 'warn',
'no-prototype-builtins': 'off', 'no-prototype-builtins': 'off',
'@typescript-eslint/ban-types': 'off', '@typescript-eslint/ban-types': 'off',
'id-length': [ 'id-length': [

9925
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,35 +2,39 @@
"name": "node-ejs", "name": "node-ejs",
"main": "src/server.js", "main": "src/server.js",
"dependencies": { "dependencies": {
"@types/express": "^4.17.9", "@types/express": "^4.17.13",
"@types/node": "^15.0.1", "better-sqlite3": "^7.5.0",
"better-sqlite3": "^7.1.5", "cookie-parser": "^1.4.6",
"connect-busboy": "0.0.2",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5", "cors": "^2.8.5",
"ejs": "^3.1.6", "ejs": "^3.1.6",
"express": "^4.6.1", "express": "^4.17.3",
"express-fileupload": "^1.2.1", "express-fileupload": "^1.3.1",
"socket.io": "^4.1.2", "socket.io": "^4.4.1",
"ts-node": "^9.0.0", "ts-node": "^10.7.0",
"typescript": "^4.1.2", "typescript": "^4.6.2",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"vhost": "^3.0.2" "vhost": "^3.0.2"
}, },
"scripts": { "scripts": {
"start": "node ./dist/server.js", "start": "node ./dist/server.js",
"dev": "npm run build && NS_DEVEL=1 NS_NOUSER=1 NS_LOGLEVEL=1 node ./dist/server.js", "dev": "npm run build && NS_THREAD_COUNT=2 NS_DEVEL=1 NS_NOUSER=1 NS_LOGLEVEL=1 node --inspect ./dist/server.js",
"build": "tsc && bash -c './scripts/postBuild.sh'", "build": "tsc && bash -c './scripts/postBuild.sh'",
"export": "tsc && bash -c './scripts/postBuild.sh'", "export": "tsc && bash -c './scripts/postBuild.sh'",
"test": "jest --detectOpenHandles" "test": "jest --detectOpenHandles"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^26.0.23", "@types/better-sqlite3": "^7.5.0",
"@typescript-eslint/eslint-plugin": "^4.8.1", "@types/cookie-parser": "^1.4.2",
"@typescript-eslint/parser": "^4.22.0", "@types/express-fileupload": "^1.2.2",
"eslint": "^7.14.0", "@types/jest": "^27.4.1",
"jest": "^26.6.3", "@types/node": "^17.0.21",
"ts-jest": "^26.5.5" "@types/uuid": "^8.3.4",
"@types/vhost": "^3.0.4",
"@typescript-eslint/eslint-plugin": "^5.15.0",
"@typescript-eslint/parser": "^5.15.0",
"eslint": "^8.11.0",
"jest": "^27.5.1",
"ts-jest": "^27.1.3"
}, },
"jest": { "jest": {
"preset": "ts-jest" "preset": "ts-jest"

View file

@ -1,3 +1,7 @@
import type { Response, NextFunction } from 'express'
import type { Request } from '../types/basicTypes'
import type { Database } from 'better-sqlite3'
import logger from '../utils/logger' import logger from '../utils/logger'
import utils from '../utils/utils' import utils from '../utils/utils'
import dbtools from '../utils/dbtools' import dbtools from '../utils/dbtools'
@ -18,8 +22,8 @@ export const testUser = {
createdBy: 1, createdBy: 1,
} }
function renderLogin(req, res, jsonResponse) { function renderLogin(_req: Request, res: Response, jsonResponse: boolean) {
res.status('401') // Unauthorized res.status(401) // Unauthorized
if (jsonResponse) { if (jsonResponse) {
res.json({ res.json({
result: 'nouser', result: 'nouser',
@ -33,9 +37,17 @@ function renderLogin(req, res, jsonResponse) {
} }
export default function (options: Options): any { export default function (options: Options): any {
const { userDB, jsonResponse, exceptions } = options const {
userDB,
jsonResponse,
exceptions,
}: {
userDB: Database
jsonResponse: boolean
exceptions: string[]
} = options
return function (req, res, next) { return function (req: Request, res: Response, next: NextFunction) {
const sessionID = req.cookies.sessionID const sessionID = req.cookies.sessionID
const isException = exceptions.some((exc) => { const isException = exceptions.some((exc) => {
return req.url.split('?')[0] === exc return req.url.split('?')[0] === exc
@ -44,7 +56,7 @@ export default function (options: Options): any {
if (process.env.NS_NOUSER) { if (process.env.NS_NOUSER) {
req.session = { req.session = {
user: testUser, user: testUser,
sessionID: sessionID || 111111111111111111, sessionID: sessionID || 11111111111,
isException: false, isException: false,
} }
next() next()

View file

@ -1,4 +1,6 @@
import logger from '../utils/logger' import logger from '../utils/logger'
import type { Response, NextFunction } from 'express'
import type { Request } from '../types/basicTypes'
interface Options { interface Options {
loggableKeywords: Array<string> loggableKeywords: Array<string>
@ -13,7 +15,7 @@ export default function (options: Options): any {
const exceptions = options.exceptions || [] const exceptions = options.exceptions || []
const excludeFromStats = options.excludeFromStats || [] const excludeFromStats = options.excludeFromStats || []
return function (req, res, next) { return function (req: Request, res: Response, next: NextFunction) {
res.on('finish', function () { res.on('finish', function () {
// TODO: test this // TODO: test this
const isException = exceptions.some((ex) => { const isException = exceptions.some((ex) => {
@ -24,7 +26,6 @@ export default function (options: Options): any {
return return
} }
const ip = req.headers['cf-connecting-ip'] || req.connection.remoteAddress
let hostname = 'NOHOST' let hostname = 'NOHOST'
if (req.hostname) { if (req.hostname) {
hostname = req.hostname.replace('www.', '').split('.')[0] hostname = req.hostname.replace('www.', '').split('.')[0]
@ -59,7 +60,6 @@ export default function (options: Options): any {
if (res.statusCode !== 404 && shouldLogStat) { if (res.statusCode !== 404 && shouldLogStat) {
logger.LogStat( logger.LogStat(
req.url, req.url,
ip,
hostname, hostname,
req.session && req.session.user ? req.session.user.id : 'NOUSER' req.session && req.session.user ? req.session.user.id : 'NOUSER'
) )

View file

@ -1,6 +1,8 @@
import cookie from 'cookie'
import logger from '../utils/logger' import logger from '../utils/logger'
import dbtools from '../utils/dbtools' import dbtools from '../utils/dbtools'
import cookie from 'cookie' import { Socket } from '../types/basicTypes'
import { testUser } from './auth.middleware' import { testUser } from './auth.middleware'
@ -11,7 +13,7 @@ interface Options {
export default function SocketAuth(options: Options): any { export default function SocketAuth(options: Options): any {
const { userDB } = options const { userDB } = options
return (socket, next) => { return (socket: Socket, next: (arg0?: any) => void) => {
try { try {
const cookies = cookie.parse(socket.handshake.headers.cookie || '') const cookies = cookie.parse(socket.handshake.headers.cookie || '')
const sessionID = cookies.sessionID const sessionID = cookies.sessionID

View file

@ -19,16 +19,18 @@
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
// package requires // package requires
import express from 'express' import express, { RequestHandler } from 'express'
import bodyParser from 'body-parser'
import fileUpload from 'express-fileupload' import fileUpload from 'express-fileupload'
import type { Database } from 'better-sqlite3'
import http from 'http'
import https from 'https'
// other requires // other requires
import logger from '../../utils/logger' import logger from '../../utils/logger'
import utils from '../../utils/utils' import utils from '../../utils/utils'
import auth from '../../middlewares/auth.middleware' import auth from '../../middlewares/auth.middleware'
import { SetupData } from '../../server' import { SetupData } from '../../server'
import { ModuleType, Request } from '../../types/basicTypes' import { ModuleType, Request, Submodule } from '../../types/basicTypes'
// files // files
const rootRedirectToFile = 'data/apiRootRedirectTo' const rootRedirectToFile = 'data/apiRootRedirectTo'
@ -37,11 +39,11 @@ const rootRedirectToFile = 'data/apiRootRedirectTo'
const moduleName = 'API' const moduleName = 'API'
// stuff gotten from server.js // stuff gotten from server.js
let userDB let userDB: Database
let url let url: string
let publicdirs = [] let publicdirs: string[] = []
let httpServer let httpServer: http.Server
let httpsServer let httpsServer: https.Server
function GetApp(): ModuleType { function GetApp(): ModuleType {
const app = express() const app = express()
@ -51,7 +53,7 @@ function GetApp(): ModuleType {
throw new Error(`No public dir! ( API )`) throw new Error(`No public dir! ( API )`)
} }
let domain = url.split('.') // [ "https://api", "frylabs", "net" ] let domain: any = url.split('.') // [ "https://api", "frylabs", "net" ]
domain.shift() // [ "frylabs", "net" ] domain.shift() // [ "frylabs", "net" ]
domain = domain.join('.') // "frylabs.net" domain = domain.join('.') // "frylabs.net"
logger.DebugLog(`Cookie domain: ${domain}`, 'cookie', 1) logger.DebugLog(`Cookie domain: ${domain}`, 'cookie', 1)
@ -59,15 +61,15 @@ function GetApp(): ModuleType {
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
app.use( app.use(
bodyParser.urlencoded({ express.urlencoded({
limit: '10mb', limit: '10mb',
extended: true, extended: true,
}) }) as RequestHandler
) )
app.use( app.use(
bodyParser.json({ express.json({
limit: '10mb', limit: '10mb',
}) }) as RequestHandler
) )
app.set('view engine', 'ejs') app.set('view engine', 'ejs')
app.set('views', ['./src/modules/api/views', './src/sharedViews']) app.set('views', ['./src/modules/api/views', './src/sharedViews'])
@ -151,11 +153,11 @@ function GetApp(): ModuleType {
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
app.get('*', function (req: Request, res: any) { app.get('*', function (_req: Request, res: any) {
res.status(404).render('404') res.status(404).render('404')
}) })
app.post('*', function (req: Request, res: any) { app.post('*', function (_req: Request, res: any) {
res.status(404).render('404') res.status(404).render('404')
}) })
@ -182,14 +184,14 @@ function GetApp(): ModuleType {
function setupSubModules( function setupSubModules(
parentApp: express.Application, parentApp: express.Application,
moduleSpecificData?: any moduleSpecificData?: any
): any { ): Submodule[] {
const submoduleDir = './submodules/' const submoduleDir = './submodules/'
const absolutePath = __dirname + '/' + submoduleDir const absolutePath = __dirname + '/' + submoduleDir
if (!utils.FileExists(absolutePath)) { if (!utils.FileExists(absolutePath)) {
return return null
} }
const files = utils.ReadDir(absolutePath) const files = utils.ReadDir(absolutePath)
const moduleDatas = [] const moduleDatas: Submodule[] = []
files.forEach((file) => { files.forEach((file) => {
if (!file.endsWith('.js')) { if (!file.endsWith('.js')) {
return return

View file

@ -1,32 +0,0 @@
{
"msgs": {
"tableStruct": {
"id": {
"type": "integer",
"primary": true,
"autoIncrement": true
},
"sender": {
"type": "integer",
"notNull": true
},
"reciever": {
"type": "integer",
"notNull": true
},
"msg": {
"type": "text"
},
"type": {
"type": "text"
},
"date": {
"type": "integer"
},
"unread": {
"type": "integer",
"defaultZero": true
}
}
}
}

View file

@ -0,0 +1,34 @@
const DbStruct = {
msgs: {
tableStruct: {
id: {
type: 'integer',
primary: true,
autoIncrement: true,
},
sender: {
type: 'integer',
notNull: true,
},
reciever: {
type: 'integer',
notNull: true,
},
msg: {
type: 'text',
},
type: {
type: 'text',
},
date: {
type: 'integer',
},
unread: {
type: 'integer',
defaultZero: true,
},
},
},
}
export default DbStruct

View file

@ -13,6 +13,16 @@ interface ExtendedSocket extends Socket {
user: User user: User
} }
interface Message {
id: number
sender: number
reciever: number
msg: string
type: string
date: number
unread: number
}
function setup(data: SubmoduleData): void { function setup(data: SubmoduleData): void {
const { app, httpServer, httpsServer, userDB, publicdirs } = data const { app, httpServer, httpsServer, userDB, publicdirs } = data
const msgDB = dbtools.GetDB(msgDbPath) const msgDB = dbtools.GetDB(msgDbPath)
@ -28,8 +38,13 @@ function setup(data: SubmoduleData): void {
}, },
}) })
function chatMessageRead(data) { function chatMessageRead({
const { sender, reciever } = data sender,
reciever,
}: {
sender: number
reciever: number
}) {
dbtools.runStatement( dbtools.runStatement(
msgDB, msgDB,
`update msgs `update msgs
@ -50,7 +65,7 @@ function setup(data: SubmoduleData): void {
socket.on('join', function (/*data*/) { socket.on('join', function (/*data*/) {
socket.join(userid.toString()) socket.join(userid.toString())
const groups = dbtools const groups: number[] = dbtools
.runStatement( .runStatement(
msgDB, msgDB,
`select * from `select * from
@ -65,14 +80,14 @@ function setup(data: SubmoduleData): void {
)t )t
order by t.a asc` order by t.a asc`
) )
.reduce((acc, x) => { .reduce((acc: number[], x: { a: number }) => {
if (x.a !== userid) acc.push(x.a) if (x.a !== userid) acc.push(x.a)
return acc return acc
}, []) }, [])
socket.emit('prev messages', { socket.emit('prev messages', {
prevMsgs: groups.map((to) => { prevMsgs: groups.map((to) => {
const first = dbtools.runStatement( const first: Message = dbtools.runStatement(
msgDB, msgDB,
`select * from msgs `select * from msgs
where sender = ${userid} and reciever = ${to} or where sender = ${userid} and reciever = ${to} or
@ -147,7 +162,7 @@ function setup(data: SubmoduleData): void {
// socket.on('close', () => {}) // socket.on('close', () => {})
}) })
app.post('/postchatfile', function (req: Request, res: any) { app.post('/postchatfile', function (req: Request, res) {
logger.LogReq(req) logger.LogReq(req)
utils utils
.uploadFile(req, uloadFiles) .uploadFile(req, uloadFiles)
@ -189,12 +204,12 @@ function setup(data: SubmoduleData): void {
)t )t
order by t.a asc` order by t.a asc`
) )
.reduce((acc, x) => { .reduce((acc: number[], x: { a: number }) => {
if (x.a !== userid) acc.push(x.a) if (x.a !== userid) acc.push(x.a)
return acc return acc
}, []) }, [])
const prevMsgs = groups.map((to) => { const prevMsgs = groups.map((to: number) => {
const first = dbtools.runStatement( const first = dbtools.runStatement(
msgDB, msgDB,
`select * from msgs `select * from msgs
@ -207,7 +222,7 @@ function setup(data: SubmoduleData): void {
}) })
res.json({ res.json({
unreads: prevMsgs.reduce((acc, msg) => { unreads: prevMsgs.reduce((acc: number[], msg: Message) => {
if (msg && msg.unread === 1 && msg.sender !== userid) { if (msg && msg.unread === 1 && msg.sender !== userid) {
acc.push(msg.sender) acc.push(msg.sender)
} }

View file

@ -1,3 +1,5 @@
import { Response } from 'express'
import logger from '../../../utils/logger' import logger from '../../../utils/logger'
import utils from '../../../utils/utils' import utils from '../../../utils/utils'
import { Request, SubmoduleData, User } from '../../../types/basicTypes' import { Request, SubmoduleData, User } from '../../../types/basicTypes'
@ -8,7 +10,7 @@ const uloadFiles = 'data/f'
function setup(data: SubmoduleData): void { function setup(data: SubmoduleData): void {
const { app /* userDB, url, publicdirs, moduleSpecificData */ } = data const { app /* userDB, url, publicdirs, moduleSpecificData */ } = data
app.post('/postfeedbackfile', function (req: Request, res: any) { app.post('/postfeedbackfile', function (req: Request, res: Response) {
utils utils
.uploadFile(req, uloadFiles) .uploadFile(req, uloadFiles)
.then(() => { .then(() => {
@ -23,7 +25,7 @@ function setup(data: SubmoduleData): void {
logger.Log('New feedback file', logger.GetColor('bluebg')) logger.Log('New feedback file', logger.GetColor('bluebg'))
}) })
app.post('/postfeedback', function (req: Request, res: any) { app.post('/postfeedback', function (req: Request, res: Response) {
logger.LogReq(req) logger.LogReq(req)
if (req.body.fromLogin) { if (req.body.fromLogin) {
logger.Log( logger.Log(
@ -55,7 +57,7 @@ function setup(data: SubmoduleData): void {
res.json({ success: true }) res.json({ success: true })
}) })
app.route('/fosuploader').post(function (req: Request, res: any) { app.route('/fosuploader').post(function (req: Request, res: Response) {
utils.uploadFile(req, uloadFiles).then(({ fileName }) => { utils.uploadFile(req, uloadFiles).then(({ fileName }) => {
res.redirect('/f/' + fileName) res.redirect('/f/' + fileName)
}) })

View file

@ -4,10 +4,40 @@ import logger from '../../../utils/logger'
import utils from '../../../utils/utils' import utils from '../../../utils/utils'
import { Request, SubmoduleData, User } from '../../../types/basicTypes' import { Request, SubmoduleData, User } from '../../../types/basicTypes'
interface Comment {
date: string
user: number
content: string
admin: boolean
subComments?: Comment[]
reacts?: {
[key: string]: number[]
}
}
interface ForumEntry {
date: string
user: number
title: string
content: string
admin: boolean
comments: Comment[]
reacts: {
[key: string]: number[]
}
}
interface ForumContents {
user: number
title: string
admin: boolean
commentCount: number
}
const adminUsersFile = 'data/admins.json' const adminUsersFile = 'data/admins.json'
const forumContentsFileName = '.contents.json' const forumContentsFileName = '.contents.json'
function countComments(comments) { function countComments(comments: Comment[]) {
return comments.reduce((acc, comment) => { return comments.reduce((acc, comment) => {
if (comment.subComments) { if (comment.subComments) {
acc += countComments(comment.subComments) + 1 acc += countComments(comment.subComments) + 1
@ -18,7 +48,7 @@ function countComments(comments) {
}, 0) }, 0)
} }
function addComment(obj, path, comment) { function addComment(obj: Comment[], path: number[], comment: Comment) {
if (path.length === 0) { if (path.length === 0) {
obj.push(comment) obj.push(comment)
} else { } else {
@ -30,7 +60,11 @@ function addComment(obj, path, comment) {
} }
} }
function deleteComment(obj: any, path: Array<number>, userid: number): boolean { function deleteComment(
obj: Comment[],
path: number[],
userid: number
): boolean {
if (path.length === 1) { if (path.length === 1) {
if (obj[path[0]].user === userid) { if (obj[path[0]].user === userid) {
obj.splice(path[0], 1) obj.splice(path[0], 1)
@ -45,7 +79,15 @@ function deleteComment(obj: any, path: Array<number>, userid: number): boolean {
} }
} }
function addReaction(obj, path, { reaction, isDelete, uid }) { function addReaction(
obj: Comment[],
path: number[],
{
reaction,
isDelete,
uid,
}: { reaction: string; isDelete: boolean; uid: number }
) {
if (path.length === 1) { if (path.length === 1) {
const index = path[0] const index = path[0]
if (!obj[index].reacts) { if (!obj[index].reacts) {
@ -54,8 +96,8 @@ function addReaction(obj, path, { reaction, isDelete, uid }) {
if (isDelete) { if (isDelete) {
if (obj[index].reacts[reaction]) { if (obj[index].reacts[reaction]) {
obj[index].reacts[reaction] = obj[index].reacts[reaction].filter( obj[index].reacts[reaction] = obj[index].reacts[reaction].filter(
(uid) => { (currUid: number) => {
return uid !== uid return uid !== currUid
} }
) )
if (obj[index].reacts[reaction].length === 0) { if (obj[index].reacts[reaction].length === 0) {
@ -84,7 +126,7 @@ function addReaction(obj, path, { reaction, isDelete, uid }) {
function getForumData( function getForumData(
forumName: string, forumName: string,
forumDir: string forumDir: string
): { forumPath: string; contentFilePath: string; contents: any } { ): { forumPath: string; contentFilePath: string; contents: ForumContents[] } {
const safeForumName = forumName.replace(/\./g, '').replace(/\/+/g, '') const safeForumName = forumName.replace(/\./g, '').replace(/\/+/g, '')
const forumPath = forumDir + '/' + safeForumName const forumPath = forumDir + '/' + safeForumName
const contentFilePath = forumPath + '/' + forumContentsFileName const contentFilePath = forumPath + '/' + forumContentsFileName
@ -96,7 +138,7 @@ function getForumData(
if (!utils.FileExists(contentFilePath)) { if (!utils.FileExists(contentFilePath)) {
utils.WriteFile('{}', contentFilePath) utils.WriteFile('{}', contentFilePath)
} }
const contents = utils.ReadJSON(contentFilePath) const contents: ForumContents[] = utils.ReadJSON(contentFilePath)
return { return {
forumPath: forumPath, forumPath: forumPath,
contentFilePath: contentFilePath, contentFilePath: contentFilePath,
@ -104,11 +146,18 @@ function getForumData(
} }
} }
function getPostData(postKey, contents, forumPath) { function getPostData(
postKey: string,
contents: ForumContents[],
forumPath: string
): {
postData: ForumEntry
postPath: string
} {
const safePostKey = postKey.replace(/\./g, '').replace(/\/+/g, '') const safePostKey = postKey.replace(/\./g, '').replace(/\/+/g, '')
const postPath = forumPath + '/' + safePostKey const postPath = forumPath + '/' + safePostKey
if (!contents[postKey] || !utils.FileExists(postPath)) { if (!contents[postKey] || !utils.FileExists(postPath)) {
return return null
} }
return { postData: utils.ReadJSON(postPath), postPath: postPath } return { postData: utils.ReadJSON(postPath), postPath: postPath }
} }
@ -167,6 +216,7 @@ function setup(data: SubmoduleData): void {
if (passed) { if (passed) {
i++ i++
} }
return false
}) })
res.json({ res.json({
@ -175,11 +225,20 @@ function setup(data: SubmoduleData): void {
}) })
}) })
app.post('/addPost', (req: Request, res) => { app.post(
'/addPost',
(
req: Request<{
forumName: string
content: string
title: string
}>,
res
) => {
logger.LogReq(req) logger.LogReq(req)
const { title, content } = req.body const { title, content } = req.body
const forumName: any = req.body.forumName const forumName = req.body.forumName
if (!forumName) { if (!forumName) {
res.json({ res.json({
success: false, success: false,
@ -217,9 +276,18 @@ function setup(data: SubmoduleData): void {
newPostKey: newPostKey, newPostKey: newPostKey,
newEntry: { ...postData, content: content }, newEntry: { ...postData, content: content },
}) })
}) }
)
app.post('/rmPost', (req: Request, res) => { app.post(
'/rmPost',
(
req: Request<{
postKey: string
forumName: string
}>,
res
) => {
logger.LogReq(req) logger.LogReq(req)
const { postKey } = req.body const { postKey } = req.body
const forumName: any = req.body.forumName const forumName: any = req.body.forumName
@ -245,14 +313,25 @@ function setup(data: SubmoduleData): void {
} else { } else {
res.json({ res.json({
success: false, success: false,
msg: msg: 'Entry does not exists, or you are not authorized to delete this post',
'Entry does not exists, or you are not authorized to delete this post',
}) })
return return
} }
}) }
)
app.post('/comment', (req: Request, res) => { app.post(
'/comment',
(
req: Request<{
type: string
path: number[]
postKey: string
forumName: string
content: string
}>,
res
) => {
logger.LogReq(req) logger.LogReq(req)
const forumName: any = req.body.forumName const forumName: any = req.body.forumName
@ -273,7 +352,10 @@ function setup(data: SubmoduleData): void {
: [] : []
const { type, path, postKey } = req.body const { type, path, postKey } = req.body
if (!postKey || !type || !path) { if (!postKey || !type || !path) {
res.json({ success: false, msg: 'type or path or postKey is undefined' }) res.json({
success: false,
msg: 'type or path or postKey is undefined',
})
return return
} }
@ -319,9 +401,21 @@ function setup(data: SubmoduleData): void {
utils.WriteFile(JSON.stringify(postData, null, 2), postPath) utils.WriteFile(JSON.stringify(postData, null, 2), postPath)
utils.WriteFile(JSON.stringify(contents, null, 2), contentFilePath) utils.WriteFile(JSON.stringify(contents, null, 2), contentFilePath)
res.json({ success: true, postKey: postKey, postData: postData }) res.json({ success: true, postKey: postKey, postData: postData })
}) }
)
app.post('/react', (req: Request, res) => { app.post(
'/react',
(
req: Request<{
forumName: string
postKey: string
path: number[]
reaction: string
isDelete: boolean
}>,
res
) => {
logger.LogReq(req) logger.LogReq(req)
const forumName: any = req.body.forumName const forumName: any = req.body.forumName
@ -386,7 +480,8 @@ function setup(data: SubmoduleData): void {
utils.WriteFile(JSON.stringify(postData, null, 2), postPath) utils.WriteFile(JSON.stringify(postData, null, 2), postPath)
res.json({ success: true, postData: postData }) res.json({ success: true, postData: postData })
}) }
)
} }
export default { export default {

View file

@ -1,5 +1,7 @@
import fs from 'fs' import fs from 'fs'
import { fork } from 'child_process' import { Response } from 'express'
import { fork, ChildProcess } from 'child_process'
import type { Database } from 'better-sqlite3'
import logger from '../../../utils/logger' import logger from '../../../utils/logger'
import utils from '../../../utils/utils' import utils from '../../../utils/utils'
@ -9,6 +11,11 @@ import {
Request, Request,
QuestionDb, QuestionDb,
SubmoduleData, SubmoduleData,
Question,
QuestionFromScript,
DbSearchResult,
RegisteredUserEntry,
Submodule,
} from '../../../types/basicTypes' } from '../../../types/basicTypes'
import { import {
processIncomingRequest, processIncomingRequest,
@ -24,6 +31,7 @@ import {
import { import {
dataToString, dataToString,
getSubjNameWithoutYear, getSubjNameWithoutYear,
WorkerResult,
// compareQuestionObj, // compareQuestionObj,
} from '../../../utils/classes' } from '../../../utils/classes'
import { import {
@ -33,6 +41,22 @@ import {
} from '../../../utils/workerPool' } from '../../../utils/workerPool'
import dbtools from '../../../utils/dbtools' import dbtools from '../../../utils/dbtools'
interface SavedQuestionData {
fname: string
subj: string
userid: number
testUrl: string
date: string | Date
}
// interface SavedQuestion {
// Questions: Question[]
// subj: string
// userid: number
// testUrl: string
// date: string
// }
const line = '====================================================' // lol const line = '====================================================' // lol
const registeredScriptsFile = 'stats/registeredScripts.json' const registeredScriptsFile = 'stats/registeredScripts.json'
const testUsersFile = 'data/testUsers.json' const testUsersFile = 'data/testUsers.json'
@ -43,13 +67,13 @@ const oldMotdFile = 'publicDirs/qminingPublic/oldMotd'
const dailyDataCountFile = 'stats/dailyDataCount' const dailyDataCountFile = 'stats/dailyDataCount'
const dataEditsLog = 'stats/dataEdits' const dataEditsLog = 'stats/dataEdits'
function getSubjCount(qdbs) { function getSubjCount(qdbs: QuestionDb[]): number {
return qdbs.reduce((acc, qdb) => { return qdbs.reduce((acc, qdb) => {
return acc + qdb.data.length return acc + qdb.data.length
}, 0) }, 0)
} }
function getQuestionCount(qdbs) { function getQuestionCount(qdbs: QuestionDb[]): number {
return qdbs.reduce((acc, qdb) => { return qdbs.reduce((acc, qdb) => {
return ( return (
acc + acc +
@ -60,7 +84,7 @@ function getQuestionCount(qdbs) {
}, 0) }, 0)
} }
function ExportDailyDataCount(questionDbs, userDB) { function ExportDailyDataCount(questionDbs: QuestionDb[], userDB: Database) {
logger.Log('Saving daily data count ...') logger.Log('Saving daily data count ...')
utils.AppendToFile( utils.AppendToFile(
JSON.stringify({ JSON.stringify({
@ -76,9 +100,9 @@ function ExportDailyDataCount(questionDbs, userDB) {
function getDbIndexesToSearchIn( function getDbIndexesToSearchIn(
testUrl: string, testUrl: string,
questionDbs, questionDbs: Array<QuestionDb>,
trueIfAlways?: boolean trueIfAlways?: boolean
) { ): number[] {
return testUrl return testUrl
? questionDbs.reduce((acc, qdb, i) => { ? questionDbs.reduce((acc, qdb, i) => {
if (shouldSearchDataFile(qdb, testUrl, trueIfAlways)) { if (shouldSearchDataFile(qdb, testUrl, trueIfAlways)) {
@ -89,14 +113,17 @@ function getDbIndexesToSearchIn(
: [] : []
} }
function getSimplreRes(questionDbs) { function getSimplreRes(questionDbs: QuestionDb[]): {
subjects: number
questions: number
} {
return { return {
subjects: getSubjCount(questionDbs), subjects: getSubjCount(questionDbs),
questions: getQuestionCount(questionDbs), questions: getQuestionCount(questionDbs),
} }
} }
function getDetailedRes(questionDbs) { function getDetailedRes(questionDbs: QuestionDb[]) {
return questionDbs.map((qdb) => { return questionDbs.map((qdb) => {
return { return {
dbName: qdb.name, dbName: qdb.name,
@ -110,7 +137,7 @@ function getDetailedRes(questionDbs) {
}) })
} }
function getMotd(version, motd) { function getMotd(version: any, motd: string) {
if (version) { if (version) {
if (version.startsWith('2.0.')) { if (version.startsWith('2.0.')) {
if (utils.FileExists(oldMotdFile)) { if (utils.FileExists(oldMotdFile)) {
@ -122,13 +149,11 @@ function getMotd(version, motd) {
} }
function searchInDbs( function searchInDbs(
question, question: Question,
subj, subj: string,
recData, searchIn: number[],
recievedData, testUrl?: string
searchIn, ): Promise<DbSearchResult> {
testUrl?
) {
// searchIn could be [0], [1], ... to search every db in different thread. Put this into a // searchIn could be [0], [1], ... to search every db in different thread. Put this into a
// forEach(qdbs) to achieve this // forEach(qdbs) to achieve this
return new Promise((resolve) => { return new Promise((resolve) => {
@ -139,17 +164,11 @@ function searchInDbs(
testUrl: testUrl, testUrl: testUrl,
question: question, question: question,
subjName: subj, subjName: subj,
questionData: recData,
searchInAllIfNoResult: true, searchInAllIfNoResult: true,
}, },
}) })
.then((taskResult) => { .then((taskResult: WorkerResult) => {
try { try {
logger.DebugLog(
`Question result length: ${taskResult.length}`,
'ask',
1
)
logger.DebugLog(taskResult, 'ask', 2) logger.DebugLog(taskResult, 'ask', 2)
resolve({ resolve({
question: question, question: question,
@ -168,22 +187,27 @@ function searchInDbs(
logger.Log('Search Data error!', logger.GetColor('redbg')) logger.Log('Search Data error!', logger.GetColor('redbg'))
console.error(err) console.error(err)
resolve({ resolve({
mesage: `There was an error processing the question: ${err.message}`, question: question,
message: `There was an error processing the question: ${err.message}`,
result: [], result: [],
recievedData: JSON.stringify(recievedData),
success: false, success: false,
}) })
}) })
}) })
} }
function getResult(data) { function getResult(data: {
const { question, subj, recData, recievedData, questionDbs, testUrl } = data question: Question
subj: string
questionDbs: Array<QuestionDb>
testUrl: string
}): Promise<DbSearchResult> {
const { question, subj, questionDbs, testUrl } = data
return new Promise((resolve) => { return new Promise((resolve) => {
const searchIn = getDbIndexesToSearchIn(testUrl, questionDbs, false) const searchIn = getDbIndexesToSearchIn(testUrl, questionDbs, false)
searchInDbs(question, subj, recData, recievedData, searchIn, testUrl).then( searchInDbs(question, subj, searchIn, testUrl).then(
(res: any) => { (res: DbSearchResult) => {
if (res.result.length === 0) { if (res.result.length === 0) {
logger.DebugLog( logger.DebugLog(
`No result while searching specific question db ${testUrl}`, `No result while searching specific question db ${testUrl}`,
@ -197,14 +221,7 @@ function getResult(data) {
).filter((x) => { ).filter((x) => {
return !searchIn.includes(x) return !searchIn.includes(x)
}) })
searchInDbs( searchInDbs(question, subj, searchInMore, testUrl).then((res) => {
question,
subj,
recData,
recievedData,
searchInMore,
testUrl
).then((res) => {
resolve(res) resolve(res)
}) })
} else { } else {
@ -215,13 +232,13 @@ function getResult(data) {
}) })
} }
function dbExists(location, qdbs: Array<QuestionDb>) { function dbExists(location: string, qdbs: Array<QuestionDb>) {
return qdbs.some((qdb) => { return qdbs.some((qdb) => {
return qdb.name === location return qdb.name === location
}) })
} }
function writeAskData(body) { function writeAskData(body: QuestionFromScript) {
try { try {
let towrite = utils.GetDateString() + '\n' let towrite = utils.GetDateString() + '\n'
towrite += towrite +=
@ -236,7 +253,13 @@ function writeAskData(body) {
} }
} }
function saveQuestion(questions, subj, testUrl, userid, savedQuestionsDir) { function saveQuestion(
questions: Question[],
subj: string,
testUrl: string,
userid: number,
savedQuestionsDir: string
) {
// TODO: clear folder every now and then, check if saved questions exist // TODO: clear folder every now and then, check if saved questions exist
if (!subj) { if (!subj) {
logger.Log('No subj name to save test question') logger.Log('No subj name to save test question')
@ -259,7 +282,9 @@ function saveQuestion(questions, subj, testUrl, userid, savedQuestionsDir) {
utils.WriteFile('[]', savedSubjQuestionsFilePath) utils.WriteFile('[]', savedSubjQuestionsFilePath)
} }
const savedQuestions = utils.ReadJSON(savedSubjQuestionsFilePath) const savedQuestions: SavedQuestionData[] = utils.ReadJSON(
savedSubjQuestionsFilePath
)
const testExists = false const testExists = false
// TODO: do this on another thread? // TODO: do this on another thread?
@ -327,7 +352,7 @@ function loadSupportedSites() {
function LoadVersion() { function LoadVersion() {
const scriptContent = utils.ReadFile(userScriptFile) const scriptContent = utils.ReadFile(userScriptFile)
let temp: any = scriptContent.split('\n').find((x) => { let temp: string | string[] = scriptContent.split('\n').find((x) => {
return x.includes('@version') return x.includes('@version')
}) })
temp = temp.split(' ') temp = temp.split(' ')
@ -336,19 +361,10 @@ function LoadVersion() {
return temp return temp
} }
function LoadMOTD(motdFile) { function LoadMOTD(motdFile: string) {
return utils.ReadFile(motdFile) return utils.ReadFile(motdFile)
} }
function LoadUserSpecificMOTD(userSpecificMotdFile) {
try {
return utils.ReadJSON(userSpecificMotdFile)
} catch (err) {
logger.Log('Couldnt parse user specific motd!', logger.GetColor('redbg'))
console.error(err)
}
}
function LoadTestUsers() { function LoadTestUsers() {
let testUsers = utils.ReadJSON(testUsersFile) let testUsers = utils.ReadJSON(testUsersFile)
if (testUsers) { if (testUsers) {
@ -357,7 +373,13 @@ function LoadTestUsers() {
return testUsers return testUsers
} }
function getNewQdb(location, maxIndex, dbsFile, publicDir, questionDbs) { function getNewQdb(
location: string,
maxIndex: number,
dbsFile: string,
publicDir: string,
questionDbs: QuestionDb[]
) {
logger.Log( logger.Log(
`No suitable questiondbs found for ${location}, creating a new one...` `No suitable questiondbs found for ${location}, creating a new one...`
) )
@ -399,40 +421,31 @@ function getNewQdb(location, maxIndex, dbsFile, publicDir, questionDbs) {
questionDbs.push(loadedNewDb) questionDbs.push(loadedNewDb)
msgAllWorker({ msgAllWorker({
newdb: loadedNewDb, data: loadedNewDb,
type: 'newdb', type: 'newdb',
}) })
return loadedNewDb return loadedNewDb
} }
function setup(data: SubmoduleData): any { function setup(data: SubmoduleData): Submodule {
const { app, userDB, /* url */ publicdirs /* moduleSpecificData */ } = data const { app, userDB, /* url */ publicdirs /* moduleSpecificData */ } = data
const publicDir = publicdirs[0] const publicDir = publicdirs[0]
const motdFile = publicDir + 'motd' const motdFile = publicDir + 'motd'
const userSpecificMotdFile = publicDir + 'userSpecificMotd.json'
const dbsFile = publicDir + 'questionDbs.json' const dbsFile = publicDir + 'questionDbs.json'
const savedQuestionsDir = publicDir + 'savedQuestions' const savedQuestionsDir = publicDir + 'savedQuestions'
let version = LoadVersion() let version = LoadVersion()
let supportedSites = loadSupportedSites() let supportedSites = loadSupportedSites()
let motd = LoadMOTD(motdFile) let motd = LoadMOTD(motdFile)
let userSpecificMotd = LoadUserSpecificMOTD(userSpecificMotdFile) let testUsers: number[] = LoadTestUsers()
let testUsers: any = LoadTestUsers()
const dataFiles: Array<DataFile> = utils.ReadJSON(dbsFile) const dataFiles: Array<DataFile> = utils.ReadJSON(dbsFile)
const questionDbs: Array<QuestionDb> = loadJSON(dataFiles, publicDir) const questionDbs: Array<QuestionDb> = loadJSON(dataFiles, publicDir)
initWorkerPool(questionDbs) initWorkerPool(questionDbs)
const filesToWatch = [ const filesToWatch = [
{
fname: userSpecificMotdFile,
logMsg: 'User Specific Motd updated',
action: () => {
userSpecificMotd = LoadUserSpecificMOTD(userSpecificMotdFile)
},
},
{ {
fname: motdFile, fname: motdFile,
logMsg: 'Motd updated', logMsg: 'Motd updated',
@ -457,7 +470,7 @@ function setup(data: SubmoduleData): any {
}, },
] ]
app.get('/getDbs', (req: Request, res: any) => { app.get('/getDbs', (req: Request, res: Response) => {
logger.LogReq(req) logger.LogReq(req)
res.json( res.json(
questionDbs.map((qdb) => { questionDbs.map((qdb) => {
@ -470,7 +483,7 @@ function setup(data: SubmoduleData): any {
) )
}) })
app.get('/allqr.txt', function (req: Request, res: any) { app.get('/allqr.txt', function (req: Request, res: Response) {
logger.LogReq(req) logger.LogReq(req)
const db: any = req.query.db const db: any = req.query.db
let stringifiedData = '' let stringifiedData = ''
@ -508,7 +521,7 @@ function setup(data: SubmoduleData): any {
res.end(stringifiedData) res.end(stringifiedData)
}) })
app.post('/isAdding', function (req: Request, res: any) { app.post('/isAdding', function (req: Request, res: Response) {
logger.LogReq(req) logger.LogReq(req)
const user: User = req.session.user const user: User = req.session.user
const dryRun = testUsers.includes(user.id) const dryRun = testUsers.includes(user.id)
@ -596,39 +609,39 @@ function setup(data: SubmoduleData): any {
} }
}) })
app.post('/ask', function (req: Request, res) { app.post('/ask', function (req: Request<QuestionFromScript>, res) {
const user: User = req.session.user const user: User = req.session.user
if (!req.body.questions) { if (!req.body.questions) {
res.json({ res.json({
message: `ask something! { questions:'' ,subject:'', location:'' }`, message: `ask something! { questions:'' ,subject:'', location:'' }`,
result: [], result: [],
recievedData: JSON.stringify(req.body),
success: false, success: false,
}) })
return return
} }
const subj: any = req.body.subj || '' const subj: string = req.body.subj || ''
// TODO: test if testUrl is undefined (it shouldnt) // TODO: test if testUrl is undefined (it shouldnt)
const testUrl = req.body.testUrl const testUrl: string = req.body.testUrl
? req.body.testUrl.split('/')[2] ? req.body.testUrl.split('/')[2]
: undefined : undefined
writeAskData(req.body) writeAskData(req.body)
// every question in a different thread // every question in a different thread
const resultPromises = req.body.questions.map((question) => { const resultPromises: Promise<DbSearchResult>[] = req.body.questions.map(
(question: Question) => {
return getResult({ return getResult({
question: question, question: question,
subj: subj, subj: subj,
recData: req.body,
testUrl: testUrl, testUrl: testUrl,
questionDbs: questionDbs, questionDbs: questionDbs,
}) })
}) }
)
Promise.all(resultPromises).then((results) => { Promise.all(resultPromises).then((results: DbSearchResult[]) => {
const response = results.map((result: any) => { const response = results.map((result: DbSearchResult) => {
return { return {
answers: result.result, answers: result.result,
question: result.question, question: result.question,
@ -659,50 +672,13 @@ function setup(data: SubmoduleData): any {
}) })
}) })
app.get('/ask', function (req: Request, res) { app.get('/supportedSites', function (req: Request, res: Response) {
if (Object.keys(req.query).length === 0) {
logger.DebugLog(`No query params /ask GET`, 'ask', 1)
res.json({
message:
'ask something! ?q=[question]&subj=[subject]&data=[question data]. "subj" is optimal for faster result',
result: [],
recievedData: JSON.stringify(req.query),
success: false,
})
return
}
if (req.query.q && req.query.data) {
const subj: any = req.query.subj || ''
const question = req.query.q
const recData: any = req.query.data
getResult({
question: question,
subj: subj,
recData: recData,
recievedData: req.query,
questionDbs: questionDbs,
}).then((result) => {
res.json(result)
})
} else {
logger.DebugLog(`Invalid question`, 'ask', 1)
res.json({
message: `Invalid question :(`,
result: [],
recievedData: JSON.stringify(req.query),
success: false,
})
}
})
app.get('/supportedSites', function (req: Request, res: any) {
logger.LogReq(req) logger.LogReq(req)
res.json(supportedSites) res.json(supportedSites)
}) })
app.get('/datacount', function (req: Request, res: any) { app.get('/datacount', function (req: Request, res: Response) {
logger.LogReq(req) logger.LogReq(req)
if (req.query.detailed === 'all') { if (req.query.detailed === 'all') {
res.json({ res.json({
@ -719,7 +695,16 @@ function setup(data: SubmoduleData): any {
app.get('/infos', function (req: Request, res) { app.get('/infos', function (req: Request, res) {
const user: User = req.session.user const user: User = req.session.user
const result: any = { const result: {
result: string
uid: number
version?: string
subjinfo?: {
subjects: number
questions: number
}
motd?: string
} = {
result: 'success', result: 'success',
uid: user.id, uid: user.id,
} }
@ -732,34 +717,9 @@ function setup(data: SubmoduleData): any {
} }
if (req.query.motd) { if (req.query.motd) {
result.motd = getMotd(req.query.cversion, motd) result.motd = getMotd(req.query.cversion, motd)
if (userSpecificMotd[user.id]) {
result.userSpecificMotd = {
msg: userSpecificMotd[user.id].msg,
seen: userSpecificMotd[user.id].seen,
}
}
} }
res.json(result) res.json(result)
}) })
app.post('/infos', (req: Request, res) => {
const user: User = req.session.user
if (req.body.userSpecificMotdSeen && !userSpecificMotd[user.id].seen) {
userSpecificMotd[user.id].seen = true
logger.Log(
`User #${user.id}'s user specific motd is now seen:`,
logger.GetColor('bluebg')
)
logger.Log(userSpecificMotd[user.id].msg)
utils.WriteFile(
JSON.stringify(userSpecificMotd, null, 2),
userSpecificMotdFile
)
}
res.json({ msg: 'done' })
})
app.post('/registerscript', function (req: Request, res) { app.post('/registerscript', function (req: Request, res) {
logger.LogReq(req) logger.LogReq(req)
@ -769,7 +729,9 @@ function setup(data: SubmoduleData): any {
} }
const ua: any = req.headers['user-agent'] const ua: any = req.headers['user-agent']
const registeredScripts = utils.ReadJSON(registeredScriptsFile) const registeredScripts: RegisteredUserEntry[] = utils.ReadJSON(
registeredScriptsFile
)
const { cid, uid, version, installSource, date } = req.body const { cid, uid, version, installSource, date } = req.body
const index = registeredScripts.findIndex((registration) => { const index = registeredScripts.findIndex((registration) => {
@ -816,7 +778,7 @@ function setup(data: SubmoduleData): any {
res.json({ msg: 'done' }) res.json({ msg: 'done' })
}) })
app.get('/possibleAnswers', (req: Request, res: any) => { app.get('/possibleAnswers', (req: Request, res: Response) => {
logger.LogReq(req) logger.LogReq(req)
const files = utils.ReadDir(savedQuestionsDir) const files = utils.ReadDir(savedQuestionsDir)
@ -838,16 +800,16 @@ function setup(data: SubmoduleData): any {
}) })
}) })
app.post('/rmPossibleAnswer', (req: Request, res: any) => { app.post('/rmPossibleAnswer', (req: Request, res: Response) => {
logger.LogReq(req) logger.LogReq(req)
const user: User = req.session.user const user: User = req.session.user
const subj = req.body.subj const subj = req.body.subj
const file = req.body.file const file = req.body.file
const savedQuestionsPath = `${savedQuestionsDir}/${subj}/${savedQuestionsFileName}` const savedQuestionsPath = `${savedQuestionsDir}/${subj}/${savedQuestionsFileName}`
const savedQuestions = utils.ReadJSON(savedQuestionsPath) const savedQuestions: SavedQuestionData[] =
utils.ReadJSON(savedQuestionsPath)
let path = `${savedQuestionsDir}/${subj}/${file}` let path = `${savedQuestionsDir}/${subj}/${file}`
// to prevent deleting ../../../ ... /etc/shadow
while (path.includes('..')) { while (path.includes('..')) {
path = path.replace(/\.\./g, '.') path = path.replace(/\.\./g, '.')
} }
@ -1007,7 +969,7 @@ function setup(data: SubmoduleData): any {
}) })
}) })
let questionCleaner = null let questionCleaner: ChildProcess = null
app.get('/clearQuestions', (req: Request, res) => { app.get('/clearQuestions', (req: Request, res) => {
// TODO: dont allow multiple instances // TODO: dont allow multiple instances
// TODO: get status of it cleaning // TODO: get status of it cleaning
@ -1046,7 +1008,7 @@ function setup(data: SubmoduleData): any {
`${process.cwd()}/src/standaloneUtils/rmDuplicates.js`, `${process.cwd()}/src/standaloneUtils/rmDuplicates.js`,
['-s', `${process.cwd()}/${questionDbs[0].path}`] ['-s', `${process.cwd()}/${questionDbs[0].path}`]
) )
questionCleaner.on('exit', function (code) { questionCleaner.on('exit', function (code: number) {
console.log('EXIT', code) console.log('EXIT', code)
questionCleaner = null questionCleaner = null
}) })

View file

@ -4,12 +4,24 @@ import { Request, SubmoduleData, User } from '../../../types/basicTypes'
const quickVoteResultsDir = 'stats/qvote' const quickVoteResultsDir = 'stats/qvote'
const quickVotes = 'stats/qvote/votes.json' const quickVotes = 'stats/qvote/votes.json'
interface QuickVotes {
voteNames?: string[]
}
interface QuickVote {
votes: {
[key: string]: string
}
sum: {
[key: string]: number
}
}
function setup(data: SubmoduleData): void { function setup(data: SubmoduleData): void {
const { app /* userDB, url, publicdirs, moduleSpecificData */ } = data const { app /* userDB, url, publicdirs, moduleSpecificData */ } = data
app.get('/quickvote', (req: Request, res: any) => { app.get('/quickvote', (req: Request, res: any) => {
const key = req.query.key const key = req.query.key.toString()
const val: any = req.query.val const val: any = req.query.val
const user: User = req.session.user const user: User = req.session.user
@ -22,7 +34,7 @@ function setup(data: SubmoduleData): void {
} }
// FIXME: check vote type in file // FIXME: check vote type in file
let votes: any = {} let votes: QuickVotes = {}
if (utils.FileExists(quickVotes)) { if (utils.FileExists(quickVotes)) {
votes = utils.ReadJSON(quickVotes) votes = utils.ReadJSON(quickVotes)
} else { } else {
@ -49,7 +61,7 @@ function setup(data: SubmoduleData): void {
const voteFile = quickVoteResultsDir + '/' + key + '.json' const voteFile = quickVoteResultsDir + '/' + key + '.json'
let voteData = { let voteData: QuickVote = {
votes: {}, votes: {},
sum: {}, sum: {},
} }

View file

@ -2,10 +2,29 @@ import logger from '../../../utils/logger'
import utils from '../../../utils/utils' import utils from '../../../utils/utils'
import { Request, SubmoduleData, User } from '../../../types/basicTypes' import { Request, SubmoduleData, User } from '../../../types/basicTypes'
interface Subjects {
[key: string]: number
}
interface IdStat {
count: number
newQuestions: number
allQuestions: number
subjs: Subjects
}
interface IdStats {
[key: string]: IdStat
}
interface IdStatWithUID extends IdStat {
userId: number
}
const idStatFile = 'stats/idstats' const idStatFile = 'stats/idstats'
const idvStatFile = 'stats/idvstats' const idvStatFile = 'stats/idvstats'
function mergeObjSum(a, b) { function mergeObjSum(a: Subjects, b: Subjects) {
const res = { ...b } const res = { ...b }
Object.keys(a).forEach((key) => { Object.keys(a).forEach((key) => {
if (res[key]) { if (res[key]) {
@ -23,7 +42,7 @@ function setup(data: SubmoduleData): void {
app.get('/ranklist', (req: Request, res: any) => { app.get('/ranklist', (req: Request, res: any) => {
logger.LogReq(req) logger.LogReq(req)
let result let result: IdStats
const querySince: any = req.query.since const querySince: any = req.query.since
const user: User = req.session.user const user: User = req.session.user
@ -66,7 +85,7 @@ function setup(data: SubmoduleData): void {
} }
} }
const list = [] const list: Array<IdStatWithUID> = []
const sum = { const sum = {
count: 0, count: 0,
newQuestions: 0, newQuestions: 0,

View file

@ -1,17 +1,66 @@
import { Response } from 'express'
import logger from '../../../utils/logger' import logger from '../../../utils/logger'
import utils from '../../../utils/utils' import utils from '../../../utils/utils'
import { Request, SubmoduleData } from '../../../types/basicTypes' import { Request, SubmoduleData } from '../../../types/basicTypes'
interface Categories {
[key: string]: {
name: string
color: string
}
}
enum CardState {
TODO = 'todo',
INPROGRESS = 'inprogress',
TESTING = 'testing',
DONE = 'done',
INPROD = 'inprod',
NOTPOSSIBLE = 'notpossible',
}
interface Card {
id: number
name: string
description: string
category: string
points: number
state: CardState
votes: number[]
}
type Columns = {
[key in CardState]: {
name: string
clickable: boolean
}
}
interface Groups {
[key: string]: {
name: string
description: string
}
}
interface Todos {
categories: Categories
cards: Card[]
columns: Columns
groups: Groups
}
const todosFile = 'data/todos.json' const todosFile = 'data/todos.json'
function setup(data: SubmoduleData): void { function setup(data: SubmoduleData): void {
const { app /* userDB, url, publicdirs, moduleSpecificData */ } = data const { app /* userDB, url, publicdirs, moduleSpecificData */ } = data
app.get('/voteTodo', (req: Request, res: any) => { app.get('/voteTodo', (req: Request, res: Response) => {
logger.LogReq(req) logger.LogReq(req)
const userId = req.session.user.id const userId = req.session.user.id
const id: any = req.query.id const id: any = req.query.id
const todos = utils.ReadJSON(todosFile) const todos: Todos = utils.ReadJSON(todosFile)
if (!id) { if (!id) {
res.json({ res.json({
@ -47,7 +96,7 @@ function setup(data: SubmoduleData): void {
}) })
}) })
app.get('/todos', (req: Request, res: any) => { app.get('/todos', (req: Request, res: Response) => {
logger.LogReq(req) logger.LogReq(req)
const userId = req.session.user.id const userId = req.session.user.id
const todos = utils.ReadJSON(todosFile) const todos = utils.ReadJSON(todosFile)

View file

@ -6,7 +6,7 @@ import { Request, SubmoduleData, User } from '../../../types/basicTypes'
const dataFileName = '.data.json' const dataFileName = '.data.json'
function listDir(publicDir, subdir, userFilesDir) { function listDir(publicDir: string, subdir: string, userFilesDir: string) {
const safeSubdir = subdir.replace(/\.+/g, '').replace(/\/+/g, '') const safeSubdir = subdir.replace(/\.+/g, '').replace(/\/+/g, '')
const dir = userFilesDir + '/' + safeSubdir const dir = userFilesDir + '/' + safeSubdir
const usersFile = dir + '/' + dataFileName const usersFile = dir + '/' + dataFileName
@ -16,7 +16,6 @@ function listDir(publicDir, subdir, userFilesDir) {
success: false, success: false,
msg: `Directory ${subdir} does not exists`, msg: `Directory ${subdir} does not exists`,
} }
return
} }
if (!utils.FileExists(usersFile)) { if (!utils.FileExists(usersFile)) {
utils.WriteFile('{}', usersFile) utils.WriteFile('{}', usersFile)
@ -28,7 +27,6 @@ function listDir(publicDir, subdir, userFilesDir) {
success: false, success: false,
msg: `Path '${safeSubdir}' does not exists`, msg: `Path '${safeSubdir}' does not exists`,
} }
return
} }
return { return {
@ -65,7 +63,7 @@ function listDir(publicDir, subdir, userFilesDir) {
function setup(data: SubmoduleData): void { function setup(data: SubmoduleData): void {
const { app, /* userDB, url, */ publicdirs /* moduleSpecificData */ } = data const { app, /* userDB, url, */ publicdirs /* moduleSpecificData */ } = data
app.use((req: Request, res, next) => { app.use((req: Request, _res, next) => {
// /userFiles/test/2021-04-28_10-59.png // /userFiles/test/2021-04-28_10-59.png
try { try {
if (req.url.includes('/userFiles/')) { if (req.url.includes('/userFiles/')) {
@ -264,7 +262,7 @@ function setup(data: SubmoduleData): void {
data[fname].downvotes = [] data[fname].downvotes = []
} }
const removeVote = (from, uid) => { const removeVote = (from: number[], uid: number) => {
if (!from.includes(uid)) { if (!from.includes(uid)) {
return from return from
} }

View file

@ -1,4 +1,5 @@
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import type { Database } from 'better-sqlite3'
import logger from '../../../utils/logger' import logger from '../../../utils/logger'
import utils from '../../../utils/utils' import utils from '../../../utils/utils'
@ -11,7 +12,15 @@ const usersDbBackupPath = 'data/dbs/backup'
const maxPWCount = 3 const maxPWCount = 3
const daysAfterUserGetsPWs = 7 // days after user gets pw-s const daysAfterUserGetsPWs = 7 // days after user gets pw-s
function BackupDB(usersDbBackupPath, userDB) { interface Session {
id: string
userId: number
createDate: string
lastAccess: string
isScript: number
}
function BackupDB(usersDbBackupPath: string, userDB: Database) {
logger.Log('Backing up auth DB ...') logger.Log('Backing up auth DB ...')
utils.CreatePath(usersDbBackupPath, true) utils.CreatePath(usersDbBackupPath, true)
userDB userDB
@ -23,7 +32,7 @@ function BackupDB(usersDbBackupPath, userDB) {
.then(() => { .then(() => {
logger.Log('Auth DB backup complete!') logger.Log('Auth DB backup complete!')
}) })
.catch((err) => { .catch((err: Error) => {
logger.Log('Auth DB backup failed!', logger.GetColor('redbg')) logger.Log('Auth DB backup failed!', logger.GetColor('redbg'))
console.error(err) console.error(err)
}) })
@ -62,8 +71,7 @@ function setup(data: SubmoduleData): any {
res.json({ res.json({
result: 'error', result: 'error',
success: false, success: false,
msg: msg: 'Too many passwords requested or cant request password yet, try later',
'Too many passwords requested or cant request password yet, try later',
}) })
logger.Log( logger.Log(
`User #${requestingUser.id} requested too much passwords`, `User #${requestingUser.id} requested too much passwords`,
@ -128,8 +136,10 @@ function setup(data: SubmoduleData): any {
userID: user.id, userID: user.id,
isScript: isScript ? 1 : 0, isScript: isScript ? 1 : 0,
}) })
.sort((a, b) => { .sort((a: Session, b: Session) => {
return new Date(a).getTime() - new Date(b).getTime() return (
new Date(a.lastAccess).getTime() - new Date(b.lastAccess).getTime()
)
}) })
const diff = existingSessions.length - minimumAlowwedSessions const diff = existingSessions.length - minimumAlowwedSessions
@ -238,7 +248,7 @@ function setup(data: SubmoduleData): any {
}) })
}) })
function getDayDiff(dateString) { function getDayDiff(dateString: string | Date) {
const msdiff = new Date().getTime() - new Date(dateString).getTime() const msdiff = new Date().getTime() - new Date(dateString).getTime()
return Math.floor(msdiff / (1000 * 3600 * 24)) return Math.floor(msdiff / (1000 * 3600 * 24))
} }

View file

@ -1,79 +0,0 @@
{
"users": {
"tableStruct": {
"id": {
"type": "integer",
"primary": true,
"autoIncrement": true
},
"pw": {
"type": "text",
"notNull": true,
"unique": true
},
"notes": {
"type": "text"
},
"loginCount": {
"type": "number",
"defaultZero": true
},
"created": {
"type": "text",
"notNull": true
},
"lastLogin": {
"type": "text"
},
"lastAccess": {
"type": "text"
},
"avaiblePWRequests": {
"type": "number",
"defaultZero": true
},
"pwRequestCount": {
"type": "number",
"defaultZero": true
},
"createdBy": {
"type": "number"
}
}
},
"sessions": {
"foreignKey": [
{
"keysFrom": [
"userID"
],
"table": "users",
"keysTo": [
"id"
]
}
],
"tableStruct": {
"id": {
"type": "text",
"primary": true,
"notNull": true
},
"userID": {
"type": "number",
"notNull": true
},
"createDate": {
"type": "text",
"notNull": true
},
"lastAccess": {
"type": "text"
},
"isScript": {
"type": "number",
"notNull": true
}
}
}
}

View file

@ -0,0 +1,78 @@
export interface Users {}
const DBStruct = {
users: {
tableStruct: {
id: {
type: 'integer',
primary: true,
autoIncrement: true,
},
pw: {
type: 'text',
notNull: true,
unique: true,
},
notes: {
type: 'text',
},
loginCount: {
type: 'number',
defaultZero: true,
},
created: {
type: 'text',
notNull: true,
},
lastLogin: {
type: 'text',
},
lastAccess: {
type: 'text',
},
avaiblePWRequests: {
type: 'number',
defaultZero: true,
},
pwRequestCount: {
type: 'number',
defaultZero: true,
},
createdBy: {
type: 'number',
},
},
},
sessions: {
foreignKey: [
{
keysFrom: ['userID'],
table: 'users',
keysTo: ['id'],
},
],
tableStruct: {
id: {
type: 'text',
primary: true,
notNull: true,
},
userID: {
type: 'number',
notNull: true,
},
createDate: {
type: 'text',
notNull: true,
},
lastAccess: {
type: 'text',
},
isScript: {
type: 'number',
notNull: true,
},
},
},
}
export default DBStruct

View file

@ -19,9 +19,8 @@
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
// package requires // package requires
import express from 'express' import express, { RequestHandler } from 'express'
import bodyParser from 'body-parser' import type { Database } from 'better-sqlite3'
import busboy from 'connect-busboy'
const app = express() const app = express()
// other requires // other requires
@ -32,21 +31,21 @@ import { SetupData } from '../../server'
import { ModuleType, Request } from '../../types/basicTypes' import { ModuleType, Request } from '../../types/basicTypes'
// stuff gotten from server.js // stuff gotten from server.js
let userDB let userDB: Database
let publicdirs = [] let publicdirs: string[] = []
let nextdir = '' let nextdir = ''
function GetApp(): ModuleType { function GetApp(): ModuleType {
app.use( app.use(
bodyParser.urlencoded({ express.urlencoded({
limit: '5mb', limit: '5mb',
extended: true, extended: true,
}) }) as RequestHandler
) )
app.use( app.use(
bodyParser.json({ express.json({
limit: '5mb', limit: '5mb',
}) }) as RequestHandler
) )
app.set('view engine', 'ejs') app.set('view engine', 'ejs')
app.set('views', ['./src/modules/dataEditor/views', './src/sharedViews']) app.set('views', ['./src/modules/dataEditor/views', './src/sharedViews'])
@ -57,7 +56,7 @@ function GetApp(): ModuleType {
exceptions: ['/favicon.ico'], exceptions: ['/favicon.ico'],
}) })
) )
app.use((req: Request, res, next) => { app.use((req: Request, _res, next) => {
const url = req.url.split('?')[0] const url = req.url.split('?')[0]
if (url.includes('.html') || url === '/') { if (url.includes('.html') || url === '/') {
logger.LogReq(req) logger.LogReq(req)
@ -69,17 +68,10 @@ function GetApp(): ModuleType {
app.use(express.static(pdir)) app.use(express.static(pdir))
}) })
app.use(express.static(nextdir)) app.use(express.static(nextdir))
app.use(
busboy({
limits: {
fileSize: 10000 * 1024 * 1024,
},
})
)
// -------------------------------------------------------------- // --------------------------------------------------------------
function AddHtmlRoutes(files) { function AddHtmlRoutes(files: string[]) {
const routes = files.reduce((acc, file) => { const routes = files.reduce((acc, file) => {
if (file.includes('html')) { if (file.includes('html')) {
acc.push(file.split('.')[0]) acc.push(file.split('.')[0])
@ -90,7 +82,7 @@ function GetApp(): ModuleType {
routes.forEach((route) => { routes.forEach((route) => {
logger.DebugLog(`Added route /${route}`, 'DataEditor routes', 1) logger.DebugLog(`Added route /${route}`, 'DataEditor routes', 1)
app.get(`/${route}`, function (req: Request, res) { app.get(`/${route}`, function (_req: Request, res) {
res.redirect(`${route}.html`) res.redirect(`${route}.html`)
}) })
}) })
@ -104,11 +96,11 @@ function GetApp(): ModuleType {
logger.LogReq(req) logger.LogReq(req)
}) })
app.get('*', function (req: Request, res) { app.get('*', function (_req: Request, res) {
res.status(404).render('404') res.status(404).render('404')
}) })
app.post('*', function (req: Request, res) { app.post('*', function (_req: Request, res) {
res.status(404).render('404') res.status(404).render('404')
}) })

View file

@ -19,9 +19,7 @@
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
// package requires // package requires
import express from 'express' import express, { RequestHandler } from 'express'
import bodyParser from 'body-parser'
import busboy from 'connect-busboy'
const app = express() const app = express()
// other requires // other requires
@ -30,7 +28,7 @@ import { SetupData } from '../../server'
import { ModuleType } from '../../types/basicTypes' import { ModuleType } from '../../types/basicTypes'
// stuff gotten from server.js // stuff gotten from server.js
let publicdirs = [] let publicdirs: string[] = []
let url = '' // http(s)//asd.basd let url = '' // http(s)//asd.basd
function GetApp(): ModuleType { function GetApp(): ModuleType {
@ -40,39 +38,28 @@ function GetApp(): ModuleType {
logger.Log(`Using public dir: ${pdir}`) logger.Log(`Using public dir: ${pdir}`)
app.use(express.static(pdir)) app.use(express.static(pdir))
}) })
app.use(express.json() as RequestHandler)
app.use( app.use(
busboy({ express.urlencoded({
limits: {
fileSize: 10000 * 1024 * 1024,
},
})
)
app.use(bodyParser.json())
app.use(
bodyParser.urlencoded({
limit: '5mb', limit: '5mb',
extended: true, extended: true,
}) }) as RequestHandler
)
app.use(
bodyParser.json({
limit: '5mb',
})
) )
// -------------------------------------------------------------- // --------------------------------------------------------------
app.get('/', function(req, res) { app.get('/', function (_req, res) {
res.render('main', { res.render('main', {
siteurl: url, siteurl: url,
}) })
}) })
app.get('*', function(req, res) { app.get('*', function (_req, res) {
res.status(404).render('404') res.status(404).render('404')
}) })
app.post('*', function(req, res) { app.post('*', function (_req, res) {
res.status(404).render('404') res.status(404).render('404')
}) })

View file

@ -19,9 +19,8 @@
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
// package requires // package requires
import express from 'express' import express, { RequestHandler } from 'express'
import bodyParser from 'body-parser' import type { Database } from 'better-sqlite3'
import busboy from 'connect-busboy'
const app = express() const app = express()
// other requires // other requires
@ -32,21 +31,21 @@ import { SetupData } from '../../server'
import { ModuleType, Request } from '../../types/basicTypes' import { ModuleType, Request } from '../../types/basicTypes'
// stuff gotten from server.js // stuff gotten from server.js
let publicdirs = [] let publicdirs: string[] = []
let userDB let userDB: Database
let nextdir = '' let nextdir = ''
function GetApp(): ModuleType { function GetApp(): ModuleType {
app.use( app.use(
bodyParser.urlencoded({ express.urlencoded({
limit: '5mb', limit: '5mb',
extended: true, extended: true,
}) }) as RequestHandler
) )
app.use( app.use(
bodyParser.json({ express.json({
limit: '5mb', limit: '5mb',
}) }) as RequestHandler
) )
app.set('view engine', 'ejs') app.set('view engine', 'ejs')
app.set('views', ['./src/modules/qmining/views', './src/sharedViews']) app.set('views', ['./src/modules/qmining/views', './src/sharedViews'])
@ -57,7 +56,7 @@ function GetApp(): ModuleType {
exceptions: ['/favicon.ico', '/img/frylabs-logo_large_transparent.png'], exceptions: ['/favicon.ico', '/img/frylabs-logo_large_transparent.png'],
}) })
) )
app.use((req: Request, res, next) => { app.use((req: Request, _res, next) => {
const url = req.url.split('?')[0] const url = req.url.split('?')[0]
if (url.includes('.html') || url === '/') { if (url.includes('.html') || url === '/') {
logger.LogReq(req) logger.LogReq(req)
@ -69,13 +68,6 @@ function GetApp(): ModuleType {
app.use(express.static(pdir)) app.use(express.static(pdir))
}) })
app.use(express.static(nextdir)) app.use(express.static(nextdir))
app.use(
busboy({
limits: {
fileSize: 10000 * 1024 * 1024,
},
})
)
const linksFile = 'data/links.json' const linksFile = 'data/links.json'
let links: any = {} let links: any = {}
@ -91,7 +83,7 @@ function GetApp(): ModuleType {
loadDonateURL() loadDonateURL()
if (utils.FileExists(linksFile)) { if (utils.FileExists(linksFile)) {
utils.WatchFile(linksFile, (newData) => { utils.WatchFile(linksFile, (newData: string) => {
logger.Log(`Donate URL changed: ${newData.replace(/\/n/g, '')}`) logger.Log(`Donate URL changed: ${newData.replace(/\/n/g, '')}`)
loadDonateURL() loadDonateURL()
}) })
@ -175,7 +167,7 @@ function GetApp(): ModuleType {
}, },
{ {
from: '/irc', from: '/irc',
to: links.irc, to: '/chat',
}, },
{ {
from: '/patreon', from: '/patreon',
@ -201,7 +193,7 @@ function GetApp(): ModuleType {
// -------------------------------------------------------------- // --------------------------------------------------------------
function AddHtmlRoutes(files) { function AddHtmlRoutes(files: string[]) {
const routes = files.reduce((acc, file) => { const routes = files.reduce((acc, file) => {
if (file.includes('html')) { if (file.includes('html')) {
acc.push(file.split('.')[0]) acc.push(file.split('.')[0])
@ -210,7 +202,7 @@ function GetApp(): ModuleType {
return acc return acc
}, []) }, [])
routes.forEach((route) => { routes.forEach((route: string) => {
logger.DebugLog(`Added route /${route}`, 'Qmining routes', 1) logger.DebugLog(`Added route /${route}`, 'Qmining routes', 1)
app.get(`/${route}`, function (req: Request, res) { app.get(`/${route}`, function (req: Request, res) {
res.redirect( res.redirect(
@ -231,11 +223,11 @@ function GetApp(): ModuleType {
logger.LogReq(req) logger.LogReq(req)
}) })
app.get('*', function (req: Request, res) { app.get('*', function (_req: Request, res) {
res.status(404).render('404') res.status(404).render('404')
}) })
app.post('*', function (req: Request, res) { app.post('*', function (_req: Request, res) {
res.status(404).render('404') res.status(404).render('404')
}) })

View file

@ -1,135 +0,0 @@
/* ----------------------------------------------------------------------------
Question Server
GitLab: <https://gitlab.com/MrFry/mrfrys-node-server>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
------------------------------------------------------------------------- */
// package requires
import express from 'express'
import bodyParser from 'body-parser'
import busboy from 'connect-busboy'
import fs from 'fs'
const app = express()
// other requires
import logger from '../../utils/logger'
import utils from '../../utils/utils'
import { SetupData } from '../../server'
import { ModuleType } from '../../types/basicTypes'
// stuff gotten from server.js
let publicdirs = []
function GetApp(): ModuleType {
const publicdir = publicdirs[0]
if (!publicdir) {
throw new Error(`No public dir! ( SIO )`)
}
// files in public dirs
const uloadFiles = publicdir + 'f'
app.set('view engine', 'ejs')
app.set('views', ['./src/modules/sio/views', './src/sharedViews'])
publicdirs.forEach((pdir) => {
logger.Log(`Using public dir: ${pdir}`)
app.use(express.static(pdir))
})
app.use(
busboy({
limits: {
fileSize: 10000 * 1024 * 1024,
},
})
)
app.use(bodyParser.json())
app.use(
bodyParser.urlencoded({
limit: '5mb',
extended: true,
})
)
app.use(
bodyParser.json({
limit: '5mb',
})
)
// --------------------------------------------------------------
app.get('/', function(req, res) {
res.render('uload')
res.end()
})
function UploadFile(req, res, path, next) {
req.pipe(req.busboy)
req.busboy.on('file', function(fieldname, file, filename) {
logger.Log('Uploading: ' + filename, logger.GetColor('blue'))
utils.CreatePath(path, true)
const date = new Date()
const fn =
date.getHours() +
'' +
date.getMinutes() +
'' +
date.getSeconds() +
'_' +
filename
const fstream = fs.createWriteStream(path + '/' + fn)
file.pipe(fstream)
fstream.on('close', function() {
logger.Log(
'Upload Finished of ' + path + '/' + fn,
logger.GetColor('blue')
)
next(fn)
})
fstream.on('error', function(err) {
console.log(err)
res.end('something bad happened :s')
})
})
}
app.route('/fosuploader').post(function(req, res) {
UploadFile(req, res, uloadFiles, (fn) => {
res.redirect('/f/' + fn)
})
})
app.get('*', function(req, res) {
res.status(404).render('404')
})
app.post('*', function(req, res) {
res.status(404).render('404')
})
return {
app: app,
}
}
export default {
name: 'Sio',
getApp: GetApp,
setup: (data: SetupData): void => {
publicdirs = data.publicdirs
},
}

View file

@ -1,39 +0,0 @@
<html>
<body bgcolor="#212127">
<head>
<title>Shit uploader</title>
<meta charset="UTF-8">
<style>
body {
font: normal 14px Verdana;
color: #999999;
}
td {
vertical-align: top
}
textarea {
font: normal 14px Verdana;
color: #999999;
background-color: #212127;
width: 100%;
height: 700;
}
a {
color: #9999ff;
}
</style>
</head>
<form action="/fosuploader" enctype=multipart/form-data method="post">
<input type="file" name="dasfile" />
<input type="submit" value="Upload" />
</form>
</body>
</html>

View file

@ -1,253 +0,0 @@
/* ----------------------------------------------------------------------------
Question Server
GitLab: <https://gitlab.com/MrFry/mrfrys-node-server>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
------------------------------------------------------------------------- */
// package requires
import express from 'express'
import bodyParser from 'body-parser'
import busboy from 'connect-busboy'
import fs from 'fs'
const app = express()
// other requires
import logger from '../../utils/logger'
import { SetupData } from '../../server'
import { ModuleType, Request } from '../../types/basicTypes'
// stuff gotten from server.js
let publicdirs = []
let url = ''
function GetApp(): ModuleType {
const publicDir = publicdirs[0]
if (!publicDir) {
throw new Error(`No public dir! ( Stuff )`)
}
// files in public dirs
const listedFiles = './' + publicDir + 'files'
app.set('view engine', 'ejs')
app.set('views', ['./src/modules/stuff/views', './src/sharedViews'])
publicdirs.forEach((pdir) => {
logger.Log(`Using public dir: ${pdir}`)
app.use(express.static(pdir))
})
app.use(
busboy({
limits: {
fileSize: 10000 * 1024 * 1024,
},
})
)
app.use(bodyParser.json())
app.use(
bodyParser.urlencoded({
limit: '5mb',
extended: true,
})
)
app.use(
bodyParser.json({
limit: '5mb',
})
)
// --------------------------------------------------------------
// app, '/*.mp4', 'video/mp4', 'stuff/video'
function appGetFileType(app, wildcard, contentType, pageToRender) {
app.get(wildcard, function(req, res) {
const path = decodeURIComponent(req.url)
let fp: any = path
if (path.includes('?')) {
fp = path.split('?')
fp.pop()
fp = fp.join('/')
}
const fpath = listedFiles + fp
if (!fs.existsSync(fpath)) {
res.render('nofile', {
missingFile: fp,
url,
})
return
}
if (req.query.stream || !pageToRender) {
const stat = fs.statSync(fpath)
const fileSize = stat.size
const range = req.headers.range
if (range) {
const parts = range.replace(/bytes=/, '').split('-')
const start = parseInt(parts[0], 10)
const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1
const chunksize = end - start + 1
const file = fs.createReadStream(fpath, { start, end })
const head = {
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': contentType,
}
res.writeHead(206, head)
file.pipe(res)
} else {
const head = {
'Content-Length': fileSize,
'Content-Type': contentType,
}
res.writeHead(200, head)
fs.createReadStream(fpath).pipe(res)
}
} else {
logger.LogReq(req)
const fname = fpath.split('/').pop()
res.render(pageToRender, {
path: fp,
fname,
url,
contentType,
albumArt: GetAlbumArt(path),
})
}
})
}
const fileTypes = [
['/*.mp4', 'video/mp4', 'video'],
['/*.mkv', 'audio/x-matroska', 'video'],
['/*.mp3', 'audio/mpeg', 'audio'],
['/*.pdf', 'application/pdf'],
['/*.zip', 'application/zip'],
]
function GetAlbumArt(path) {
let tmp = path.split('.')
tmp.pop()
tmp = tmp.join('.').split('/')
const last = tmp.pop()
return tmp.join('/') + '/.' + last + '.png'
}
fileTypes.forEach((t) => {
appGetFileType(app, t[0], t[1], t[2])
})
app.get('/*', function(req: Request, res) {
const parsedUrl = decodeURIComponent(req.url)
let curr =
listedFiles +
'/' +
parsedUrl.substring('/'.length, parsedUrl.length).split('?')[0]
let relPath = curr.substring(listedFiles.length, curr.length)
if (relPath[relPath.length - 1] !== '/') {
relPath += '/'
}
const temp = relPath.split('/')
let prevDir = ''
for (let i = 0; i < temp.length - 2; i++) {
prevDir += temp[i] + '/'
}
// curr = curr.replace(/\//g, "/");
// relPath = relPath.replace(/\//g, "/");
logger.LogReq(req)
try {
const stat = fs.lstatSync(curr)
if (stat.isDirectory() || stat.isSymbolicLink()) {
if (curr[curr.length - 1] !== '/') {
curr += '/'
}
const folders = []
const files = fs.readdirSync(curr)
files.sort(function(a, b) {
return (
fs.statSync(curr + b).mtime.getTime() -
fs.statSync(curr + a).mtime.getTime()
)
})
files.forEach((item) => {
if (item[0] !== '.') {
const res: any = { name: item }
const stat = fs.statSync(curr + '/' + item)
const fileSizeInBytes = stat['size']
res.size = Math.round(fileSizeInBytes / 1000000)
res.path = relPath
if (res.path[res.path.length - 1] !== '/') {
res.path += '/'
}
res.path += item
res.mtime = stat['mtime'].toLocaleString()
res.isDir = stat.isDirectory()
folders.push(res)
}
})
res.render('folders', {
folders: folders,
dirname: relPath,
prevDir,
url,
})
} else {
const fileStream = fs.createReadStream(curr)
fileStream.pipe(res)
}
} catch (err) {
res.render('nofile', {
missingFile: relPath,
url,
})
}
})
// -----------------------------------------------------------------------------------------------
app.get('*', function(req: Request, res) {
res.status(404).render('404')
})
app.post('*', function(req: Request, res) {
res.status(404).render('404')
})
return {
app: app,
}
}
export default {
name: 'Stuff',
getApp: GetApp,
setup: (data: SetupData): void => {
url = data.url
publicdirs = data.publicdirs
},
}

View file

@ -1,39 +0,0 @@
<html>
<body bgcolor="#212127">
<head>
<title><%= fname %></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=0.6" />
<style>
body {
font: normal 14px Verdana;
color: #999999;
}
video {
width: 100%
}
</style>
</head>
<center>
<h2>
<%= fname %>
</h2>
<img
id="coverArt"
style="width:auto; max-height: 100%;"
src="<%= url + albumArt %>"
alt=' '
/>
<audio id="audioPlayer" controls style="width:100%">
<source src="<%= url %><%= path %>?stream=true" type=<%= contentType %>>
</audio>
</center>
</body>
<script>
console.log('a')
document.getElementById('coverArt').style.height = window.innerHeight - 140
</script>
</html>

View file

@ -1,130 +0,0 @@
<html>
<body bgcolor="#212127">
<head>
<title><%=dirname%></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=0.6" />
<style>
body {
font: normal 14px Verdana;
color: #999999;
}
td {
vertical-align: top;
word-wrap: break-word;
word-break: break-all;
table-layout: fixed;
padding: 0 12;
vertical-align: middle;
}
textarea {
font: normal 14px Verdana;
color: #999999;
background-color: #212127;
width: 100%;
height: 700;
}
a {
color: #9999ff;
}
.subtable {
border-collapse: collapse;
table-layout:fixed;
width:100%
}
.maintable {
border-collapse: collapse;
table-layout:fixed;
width:100%
padding:0; margin:0;
border: none !important;
}
tr {
width:32%;
}
.butt {
background-color: #212127;
color: #999999;
cursor: pointer;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 13px;
}
</style>
</head>
<center>
<h1>
<%=dirname%>
</h1>
</center>
<h2>
<a href="<%= url + prevDir%>" > Up one level </a>
</h2>
</p>
<table class="maintable">
<% for (var i = 0; i < folders.length; i++) { %>
<tr>
<td>
<a
href="<%= url + folders[i].path %>"
style="font-size: 0px"
>
<button
class="butt"
style='<%= i % 2 === 0 ? "background-color: #2f2f37" : "" %>'
onmouseenter='mouseEnter(this, <%= i %>)'
onmouseleave='mouseLeave(this, <%= i %>)'
>
<table class="subtable">
<tr height="62">
<td style='width:30%;'>
<%=folders[i].name %>
</td>
<td style='width:20%;'>
<%=folders[i].mtime %>
</td>
<td style='width:10%;'>
<%
if (folders[i].isDir) {
%> <%= "DIR" %> <%
} else {
if (folders[i].size === 0) {
%> <%= "~0 MB" %> <%
} else {
%> <%= folders[i].size + ' MB' %> <%
}
}
%>
</td>
</tr>
</table>
</button>
</a>
</td>
</tr>
<% } %>
</table>
</body>
<script>
console.log('hi')
function mouseEnter (e, i) {
e.style.backgroundColor = "#555"
}
function mouseLeave (e, i) {
if (i % 2 == 0) {
e.style.backgroundColor = "#2f2f37"
} else {
e.style.backgroundColor = "#212127"
}
}
</script>
</html>

View file

@ -1,94 +0,0 @@
<html>
<body bgcolor="#212127">
<head>
<title>No such file/folder</title>
<meta charset="UTF-8">
<style>
body {
font: normal 14px Verdana;
color: #999999;
}
td {
vertical-align: top
}
textarea {
font: normal 14px Verdana;
color: #999999;
background-color: #212127;
width: 100%;
height: 700;
}
a {
color: #9999ff;
}
.subtable {
border-collapse: collapse;
table-layout:fixed;
width:100%
}
.maintable {
border-collapse: collapse;
table-layout:fixed;
width:100%
padding:0; margin:0;
border: none !important;
}
tr {
line-height: 29px;
width:32%;
}
.butt {
background-color: #212127;
color: #999999;
cursor: pointer;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 13px;
}
.active,
.butt:hover {
background-color: #555;
}
</style>
</head>
<center>
<h1>
No such file / folder:
</br>
<%= missingFile %>
</br>
<a href="<%= url %>" > Back to root </a>
</br>
<a
onclick='goBack("<%=missingFile%>")'
href="#"
>
Go back
</a>
</h1>
</center>
</body>
<script>
function goBack(path) {
path = path.replace('./public/files', '')
if (path[path.length - 1] == '/') {
path = path.substring(0, path.length -2)
}
let p = path.split('/')
p.pop()
if (p.length > 1) {
location.href = p.join('/')
} else {
location.href = '/'
}
}
</script>
</html>

View file

@ -1,34 +0,0 @@
<html>
<body bgcolor="#212127">
<head>
<title><%= fname %></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=0.6" />
<style>
body {
font: normal 14px Verdana;
color: #999999;
}
video {
width: 100%
}
</style>
</head>
<center>
<h2>
<%= fname %>
</h2>
</center>
<video id="videoPlayer" controls>
<source src="<%= url %><%= path %>?stream=true" type="video/mp4">
</video>
</body>
<script>
var v = document.getElementsByTagName('video')[0]
v.style.maxHeight = window.innerHeight - 100
v.maxWidth = window.innerWidth
console.log('a')
</script>
</html>

View file

@ -38,6 +38,7 @@ import https from 'https'
import cors from 'cors' import cors from 'cors'
import cookieParser from 'cookie-parser' import cookieParser from 'cookie-parser'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import type { Database } from 'better-sqlite3'
import logger from './utils/logger' import logger from './utils/logger'
import utils from './utils/utils' import utils from './utils/utils'
@ -51,7 +52,7 @@ const usersDBPath = './data/dbs/users.db'
const logFile = logger.logDir + logger.logFileName const logFile = logger.logDir + logger.logFileName
const vlogFile = logger.vlogDir + logger.logFileName const vlogFile = logger.vlogDir + logger.logFileName
function moveLogIfNotFromToday(path, to) { function moveLogIfNotFromToday(path: string, to: string) {
if (utils.FileExists(path)) { if (utils.FileExists(path)) {
const today = new Date() const today = new Date()
const stat = utils.statFile(path) const stat = utils.statFile(path)
@ -89,10 +90,10 @@ interface Module {
export interface SetupData { export interface SetupData {
url: string url: string
publicdirs: Array<string> publicdirs: Array<string>
userDB?: any userDB?: Database
nextdir?: string nextdir?: string
httpServer: any httpServer: http.Server
httpsServer: any httpsServer: https.Server
} }
if (!utils.FileExists(usersDBPath)) { if (!utils.FileExists(usersDBPath)) {
@ -122,7 +123,7 @@ try {
process.on('SIGINT', () => exit('SIGINT')) process.on('SIGINT', () => exit('SIGINT'))
process.on('SIGTERM', () => exit('SIGTERM')) process.on('SIGTERM', () => exit('SIGTERM'))
function exit(reason) { function exit(reason: string) {
console.log() console.log()
logger.Log(`Exiting, reason: ${reason}`) logger.Log(`Exiting, reason: ${reason}`)
Object.keys(modules).forEach((key) => { Object.keys(modules).forEach((key) => {
@ -152,7 +153,8 @@ const fullchainFile = '/etc/letsencrypt/live/frylabs.net/fullchain.pem'
const chainFile = '/etc/letsencrypt/live/frylabs.net/chain.pem' const chainFile = '/etc/letsencrypt/live/frylabs.net/chain.pem'
let certsLoaded = false let certsLoaded = false
let certs let certs: { key: string; cert: string; ca: string }
if ( if (
startHTTPS && startHTTPS &&
utils.FileExists(privkeyFile) && utils.FileExists(privkeyFile) &&
@ -177,7 +179,7 @@ if (
const app = express() const app = express()
const httpServer = http.createServer(app) const httpServer = http.createServer(app)
let httpsServer let httpsServer: https.Server
if (certsLoaded) { if (certsLoaded) {
httpsServer = https.createServer(certs, app) httpsServer = https.createServer(certs, app)
logger.Log('Listening on port: ' + httpsport + ' (https)') logger.Log('Listening on port: ' + httpsport + ' (https)')

View file

@ -1,12 +1,16 @@
import express from 'express' import express from 'express'
import { SearchResultQuestion } from '../utils/classes'
import type { Database } from 'better-sqlite3'
import type { Socket as SocketIoSocket } from 'socket.io'
import http from 'http'
import https from 'https'
export interface QuestionData { export interface QuestionData {
type: string type: string
date?: Date | number
images?: Array<string> images?: Array<string>
hashedImages?: Array<string> hashedImages?: Array<string>
possibleAnswers?: possibleAnswers?: Array<{
| Array<string>
| Array<{
text: string text: string
selectedByUser: boolean selectedByUser: boolean
}> }>
@ -60,11 +64,6 @@ export interface User {
created: Date created: Date
} }
export interface ModuleType {
app: express.Application
dailyAction?: Function
}
export interface User { export interface User {
id: number id: number
pw: string pw: string
@ -74,7 +73,8 @@ export interface User {
createdBy: number createdBy: number
} }
export interface Request extends express.Request { export interface Request<T = any> extends express.Request {
body: T
cookies: any cookies: any
session: any session: any
busboy: any busboy: any
@ -85,9 +85,47 @@ export interface SubmoduleData {
app: express.Application app: express.Application
url: string url: string
publicdirs: Array<string> publicdirs: Array<string>
userDB?: any userDB?: Database
nextdir?: string nextdir?: string
httpServer: any
moduleSpecificData?: any moduleSpecificData?: any
httpsServer?: any httpServer: http.Server
httpsServer: https.Server
}
export interface QuestionFromScript {
questions: Array<Question>
testUrl: string
subj: string
}
export interface DbSearchResult {
message?: string
recievedData?: string
question: Question
result: SearchResultQuestion[]
success: boolean
}
export interface RegisteredUserEntry {
cid: string
version: string
installSource: string
date: string
userAgent: string
uid: number
loginDate: string
}
export interface Submodule {
dailyAction?: () => void
load?: () => void
}
export interface ModuleType {
app: express.Application
dailyAction?: Function
}
export interface Socket extends SocketIoSocket {
user: User
} }

View file

@ -21,11 +21,15 @@ const recDataFile = './stats/recdata'
const dataLockFile = './data/lockData' const dataLockFile = './data/lockData'
import logger from '../utils/logger' import logger from '../utils/logger'
import { createQuestion } from '../utils/classes' import {
createQuestion,
WorkerResult,
SearchResultQuestion,
} from '../utils/classes'
import { doALongTask } from './workerPool' import { doALongTask } from './workerPool'
import idStats from '../utils/ids' import idStats from '../utils/ids'
import utils from '../utils/utils' import utils from '../utils/utils'
import { SearchResult, addQuestion, getSubjNameWithoutYear } from './classes' import { addQuestion, getSubjNameWithoutYear } from './classes'
// types // types
import { import {
@ -152,12 +156,12 @@ function processIncomingRequestUsingDb(
): Promise<Result> { ): Promise<Result> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
const recievedQuestions = [] const recievedQuestions: Question[] = []
logger.DebugLog('recievedData JSON parsed', 'isadding', 1) logger.DebugLog('recievedData JSON parsed', 'isadding', 1)
logger.DebugLog(recievedData, 'isadding', 3) logger.DebugLog(recievedData, 'isadding', 3)
const allQLength = recievedData.quiz.length const allQLength = recievedData.quiz.length
const questionSearchPromises = [] const questionSearchPromises: Promise<WorkerResult>[] = []
recievedData.quiz.forEach((question) => { recievedData.quiz.forEach((question) => {
logger.DebugLog('Question:', 'isadding', 2) logger.DebugLog('Question:', 'isadding', 2)
logger.DebugLog(question, 'isadding', 2) logger.DebugLog(question, 'isadding', 2)
@ -193,10 +197,10 @@ function processIncomingRequestUsingDb(
}) })
Promise.all(questionSearchPromises) Promise.all(questionSearchPromises)
.then((results: Array<SearchResult>) => { .then((results: Array<WorkerResult>) => {
const allQuestions = [] // all new questions here that do not have result const allQuestions: Question[] = [] // all new questions here that do not have result
results.forEach((result, i) => { results.forEach((result: WorkerResult, i) => {
const add = result.result.every((res) => { const add = result.result.every((res: SearchResultQuestion) => {
return res.match < minMatchAmmountToAdd return res.match < minMatchAmmountToAdd
}) })
if (add) { if (add) {
@ -216,7 +220,10 @@ function processIncomingRequestUsingDb(
logger.DebugLog(currentQuestion, 'isadding', 3) logger.DebugLog(currentQuestion, 'isadding', 3)
addQuestion(qdb.data, sName, { addQuestion(qdb.data, sName, {
...currentQuestion, ...currentQuestion,
date: new Date(), data: {
...currentQuestion.data,
date: new Date().getTime(),
},
}) })
}) })
@ -279,7 +286,7 @@ function processIncomingRequestUsingDb(
}) })
} }
export function isQuestionValid(question: Question): Boolean { export function isQuestionValid(question: Question): boolean {
if (!question.Q) { if (!question.Q) {
return false return false
} }
@ -292,8 +299,8 @@ export function isQuestionValid(question: Question): Boolean {
export function shouldSearchDataFile( export function shouldSearchDataFile(
df: DataFile, df: DataFile,
testUrl: string, testUrl: string,
trueIfAlways?: Boolean trueIfAlways?: boolean
): Boolean { ): boolean {
if ( if (
typeof df.shouldSearch === 'string' && typeof df.shouldSearch === 'string' &&
df.shouldSearch === 'always' && df.shouldSearch === 'always' &&
@ -316,7 +323,7 @@ export function shouldSearchDataFile(
export function shouldSaveDataFile( export function shouldSaveDataFile(
df: DataFile, df: DataFile,
recievedData: RecievedData recievedData: RecievedData
): Boolean { ): boolean {
if (df.locked) { if (df.locked) {
return false return false
} }
@ -348,17 +355,14 @@ export function shouldSaveDataFile(
} }
export function loadData(path: string): Array<Subject> { export function loadData(path: string): Array<Subject> {
return JSON.parse(utils.ReadFile(path)).reduce((acc, subj) => { return JSON.parse(utils.ReadFile(path)).map((subj: Subject) => {
return [ return {
...acc,
{
Name: subj.Name, Name: subj.Name,
Questions: subj.Questions.map((question) => { Questions: subj.Questions.map((question: Question) => {
return createQuestion(question) return createQuestion(question)
}), }),
}, }
] })
}, [])
} }
export function loadJSON( export function loadJSON(
@ -455,7 +459,7 @@ function deleteFromDb(
selectedDb: { path: string; name: string } selectedDb: { path: string; name: string }
} }
): { ): {
success: Boolean success: boolean
msg: string msg: string
deletedQuestion?: Question deletedQuestion?: Question
resultDb?: QuestionDb resultDb?: QuestionDb
@ -470,7 +474,7 @@ function deleteFromDb(
// } // }
// } // }
const { index, subjName } = edits const { index, subjName } = edits
let deletedQuestion let deletedQuestion: Question
if (isNaN(index) || !subjName) { if (isNaN(index) || !subjName) {
return { return {
success: false, success: false,
@ -511,10 +515,10 @@ function editQuestionInDb(
subjName: string subjName: string
type: string type: string
selectedDb: { path: string; name: string } selectedDb: { path: string; name: string }
newVal?: Question newVal: Question
} }
): { ): {
success: Boolean success: boolean
msg: string msg: string
newVal?: Question newVal?: Question
oldVal?: Question oldVal?: Question
@ -543,7 +547,8 @@ function editQuestionInDb(
// } // }
// } // }
const { index, subjName, newVal } = edits const { index, subjName, newVal } = edits
let oldVal
let oldVal: Question
if (isNaN(index) || !subjName) { if (isNaN(index) || !subjName) {
return { return {
success: false, success: false,
@ -598,10 +603,10 @@ function editSubjInDb(
}> }>
} }
): { ): {
success: Boolean success: boolean
msg: string msg: string
deletedQuestions?: Array<Question> deletedQuestions?: Array<Question>
changedQuestions?: Array<Question> changedQuestions?: { oldVal: Question; newVal: Question }[]
resultDb?: QuestionDb resultDb?: QuestionDb
} { } {
// { // {
@ -632,8 +637,8 @@ function editSubjInDb(
// } // }
// } // }
const { subjName, changedQuestions, deletedQuestions } = edits const { subjName, changedQuestions, deletedQuestions } = edits
const deletedQuestionsToWrite = [] const deletedQuestionsToWrite: Question[] = []
const changedQuestionsToWrite = [] const changedQuestionsToWrite: { oldVal: Question; newVal: Question }[] = []
if (!Array.isArray(changedQuestions) || !Array.isArray(deletedQuestions)) { if (!Array.isArray(changedQuestions) || !Array.isArray(deletedQuestions)) {
return { return {
success: false, success: false,
@ -695,29 +700,32 @@ function editSubjInDb(
} }
} }
export function editDb( // FIXME: newVal is optional in some places but not in others
questionDb: QuestionDb, export interface Edits {
edits: {
index: number index: number
subjName: string subjName: string
selectedDb: { path: string; name: string } selectedDb: { path: string; name: string }
type: string type: string
newVal?: Question newVal: Question
deletedQuestion?: Array<number> deletedQuestion?: Array<number>
changedQuestions?: Array<{ changedQuestions?: Array<{
index: number index: number
value: Question value: Question
}> }>
} }
export function editDb(
questionDb: QuestionDb,
edits: Edits
): { ): {
success: Boolean success: boolean
msg: string msg: string
resultDb?: QuestionDb resultDb?: QuestionDb
deletedQuestion?: Question deletedQuestion?: Question
newVal?: Question newVal?: Question
oldVal?: Question oldVal?: Question
deletedQuestions?: Array<Question> deletedQuestions?: Array<Question>
changedQuestions?: Array<Question> changedQuestions?: { oldVal: Question; newVal: Question }[]
} { } {
if (edits.type === 'delete') { if (edits.type === 'delete') {
return deleteFromDb(questionDb, edits) return deleteFromDb(questionDb, edits)
@ -729,4 +737,9 @@ export function editDb(
if (edits.type === 'subjEdit') { if (edits.type === 'subjEdit') {
return editSubjInDb(questionDb, edits) return editSubjInDb(questionDb, edits)
} }
return {
success: false,
msg: 'DB edit error, no matched type',
}
} }

View file

@ -8,21 +8,26 @@ import {
QuestionDb, QuestionDb,
Subject, Subject,
} from '../types/basicTypes' } from '../types/basicTypes'
import { editDb } from './actions' import { editDb, Edits } from './actions'
interface SearchResultQuestion extends Question { export interface WorkerResult {
msg: string
workerIndex: number
result: SearchResultQuestion[]
}
interface DetailedMatch {
qMatch: number
aMatch: number
dMatch: number
matchedSubjName: string
avg: number
}
export interface SearchResultQuestion {
q: Question
match: number match: number
} detailedMatch: DetailedMatch
export interface SearchResult {
result: Array<SearchResultQuestion>
dbName: string
}
const assert = (val) => {
if (!val) {
throw new Error('Assertion failed')
}
} }
const commonUselessAnswerParts = [ const commonUselessAnswerParts = [
@ -65,7 +70,7 @@ function getSubjNameWithoutYear(subjName: string): string {
// Not exported // Not exported
// --------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------
function simplifyString(toremove) { function simplifyString(toremove: string): string {
return toremove.replace(/\s/g, ' ').replace(/\s+/g, ' ').toLowerCase() return toremove.replace(/\s/g, ' ').replace(/\s+/g, ' ').toLowerCase()
} }
@ -73,7 +78,7 @@ function removeStuff(
value: string, value: string,
removableStrings: Array<string>, removableStrings: Array<string>,
toReplace?: string toReplace?: string
) { ): string {
removableStrings.forEach((removableString) => { removableStrings.forEach((removableString) => {
const regex = new RegExp(removableString, 'g') const regex = new RegExp(removableString, 'g')
value = value.replace(regex, toReplace || '') value = value.replace(regex, toReplace || '')
@ -82,11 +87,11 @@ function removeStuff(
} }
// damn nonbreaking space // damn nonbreaking space
function normalizeSpaces(input) { function normalizeSpaces(input: string): string {
return input.replace(/\s/g, ' ') return input.replace(/\s/g, ' ')
} }
function removeUnnecesarySpaces(toremove: string) { function removeUnnecesarySpaces(toremove: string): string {
return normalizeSpaces(toremove).replace(/\s+/g, ' ') return normalizeSpaces(toremove).replace(/\s+/g, ' ')
} }
@ -141,7 +146,7 @@ export function compareString(
return percent return percent
} }
function answerPreProcessor(value: string) { function answerPreProcessor(value: string): string {
if (!value) { if (!value) {
return value return value
} }
@ -150,9 +155,9 @@ function answerPreProcessor(value: string) {
} }
// 'a. pécsi sör' -> 'pécsi sör' // 'a. pécsi sör' -> 'pécsi sör'
function removeAnswerLetters(value: string) { function removeAnswerLetters(value: string): string {
if (!value) { if (!value) {
return return value
} }
const val = value.split('. ') const val = value.split('. ')
@ -164,9 +169,9 @@ function removeAnswerLetters(value: string) {
} }
} }
function simplifyQA(value: string, mods: Array<Function>) { function simplifyQA(value: string, mods: Array<Function>): string {
if (!value) { if (!value) {
return return value
} }
return mods.reduce((res, fn) => { return mods.reduce((res, fn) => {
@ -174,7 +179,7 @@ function simplifyQA(value: string, mods: Array<Function>) {
}, value) }, value)
} }
function simplifyAnswer(value: string) { function simplifyAnswer(value: string): string {
if (!value) { if (!value) {
return value return value
} }
@ -185,13 +190,17 @@ function simplifyAnswer(value: string) {
]) ])
} }
function simplifyQuestion(question: Question | string) { function simplifyQuestion(question: string): string {
if (!question) { if (!question) {
return return question
} }
if (typeof question === 'string') {
return simplifyQA(question, [removeUnnecesarySpaces, removeAnswerLetters]) return simplifyQA(question, [removeUnnecesarySpaces, removeAnswerLetters])
} else { }
function simplifyQuestionObj(question: Question): Question {
if (!question) {
return question
}
if (question.Q) { if (question.Q) {
question.Q = simplifyQA(question.Q, [ question.Q = simplifyQA(question.Q, [
removeUnnecesarySpaces, removeUnnecesarySpaces,
@ -205,7 +214,6 @@ function simplifyQuestion(question: Question | string) {
]) ])
} }
return question return question
}
} }
// --------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------
@ -241,10 +249,11 @@ function createQuestion(
logger.Log('Error creating question', logger.GetColor('redbg')) logger.Log('Error creating question', logger.GetColor('redbg'))
console.error(question, answer, data) console.error(question, answer, data)
console.error(err) console.error(err)
return null
} }
} }
function compareImage(data: QuestionData, data2: QuestionData) { function compareImage(data: QuestionData, data2: QuestionData): number {
if (data.hashedImages && data2.hashedImages) { if (data.hashedImages && data2.hashedImages) {
return compareString( return compareString(
data.hashedImages.join(' '), data.hashedImages.join(' '),
@ -266,7 +275,7 @@ function compareImage(data: QuestionData, data2: QuestionData) {
} }
} }
function compareData(q1: Question, q2: Question) { function compareData(q1: Question, q2: Question): number {
try { try {
if (q1.data.type === q2.data.type) { if (q1.data.type === q2.data.type) {
const dataType = q1.data.type const dataType = q1.data.type
@ -294,7 +303,7 @@ function compareData(q1: Question, q2: Question) {
return 0 return 0
} }
function compareQuestion(q1: Question, q2: Question) { function compareQuestion(q1: Question, q2: Question): number {
return compareString(q1.Q, q1.cache.Q, q2.Q, q2.cache.Q) return compareString(q1.Q, q1.cache.Q, q2.Q, q2.cache.Q)
// return compareString( // return compareString(
// q1.Q, // q1.Q,
@ -304,7 +313,7 @@ function compareQuestion(q1: Question, q2: Question) {
// ) // )
} }
function compareAnswer(q1: Question, q2: Question) { function compareAnswer(q1: Question, q2: Question): number {
return compareString(q1.A, q1.cache.A, q2.A, q2.cache.A) return compareString(q1.A, q1.cache.A, q2.A, q2.cache.A)
// return compareString( // return compareString(
// q1.A, // q1.A,
@ -316,15 +325,10 @@ function compareAnswer(q1: Question, q2: Question) {
function compareQuestionObj( function compareQuestionObj(
q1: Question, q1: Question,
q1subjName: string, _q1subjName: string,
q2: Question, q2: Question,
q2subjName: string q2subjName: string
): any { ): DetailedMatch {
assert(q1)
assert(typeof q1 === 'object')
assert(q2)
assert(typeof q2 === 'object')
const qMatch = compareQuestion(q1, q2) const qMatch = compareQuestion(q1, q2)
const aMatch = q2.A ? compareAnswer(q1, q2) : 0 const aMatch = q2.A ? compareAnswer(q1, q2) : 0
// -1 if botth questions are simple // -1 if botth questions are simple
@ -354,7 +358,7 @@ function compareQuestionObj(
} }
} }
function questionToString(question: Question) { function questionToString(question: Question): string {
const { Q, A, data } = question const { Q, A, data } = question
if (data.type !== 'simple') { if (data.type !== 'simple') {
@ -372,10 +376,8 @@ function searchSubject(
question: Question, question: Question,
subjName: string, subjName: string,
searchTillMatchPercent?: number searchTillMatchPercent?: number
) { ): SearchResultQuestion[] {
assert(question) let result: SearchResultQuestion[] = []
let result = []
let stopSearch = false let stopSearch = false
let i = subj.Questions.length - 1 let i = subj.Questions.length - 1
@ -416,10 +418,10 @@ function searchSubject(
return result return result
} }
function subjectToString(subj: Subject) { function subjectToString(subj: Subject): string {
const { Questions, Name } = subj const { Questions, Name } = subj
const result = [] const result: string[] = []
Questions.forEach((question) => { Questions.forEach((question) => {
result.push(questionToString(question)) result.push(questionToString(question))
}) })
@ -437,23 +439,14 @@ function addQuestion(
): void { ): void {
logger.DebugLog('Adding new question with subjName: ' + subj, 'qdb add', 1) logger.DebugLog('Adding new question with subjName: ' + subj, 'qdb add', 1)
logger.DebugLog(question, 'qdb add', 3) logger.DebugLog(question, 'qdb add', 3)
assert(data)
assert(subj)
assert(question)
assert(typeof question === 'object')
let i = 0 const i = data.findIndex((x) => {
// FIXME: this only adds to the first matched subject name. Check if this doesnt cause any bugs return !subj
while (
i < data.length &&
!subj
.toLowerCase() .toLowerCase()
.includes(getSubjNameWithoutYear(data[i].Name).toLowerCase()) .includes(getSubjNameWithoutYear(x.Name).toLowerCase())
) { })
i++
}
if (i < data.length) { if (i !== -1) {
logger.DebugLog('Adding new question to existing subject', 'qdb add', 1) logger.DebugLog('Adding new question to existing subject', 'qdb add', 1)
data[i].Questions.push(question) data[i].Questions.push(question)
} else { } else {
@ -465,34 +458,12 @@ function addQuestion(
} }
} }
function prepareQuestion( function prepareQuestion(question: Question): Question {
question: string | Question, return simplifyQuestionObj(createQuestion(question))
data: string | QuestionData
): any {
let preparedQuestion: Question
if (typeof question === 'object') {
preparedQuestion = createQuestion(question)
} else {
let parsedData: any
if (typeof data === 'string') {
try {
parsedData = JSON.parse(data)
} catch (err) {
// asd
}
}
if (typeof data === 'object') {
parsedData = data
}
preparedQuestion = createQuestion(question, null, parsedData)
}
return simplifyQuestion(preparedQuestion)
} }
function dataToString(data: Array<Subject>): string { function dataToString(data: Array<Subject>): string {
const result = [] const result: string[] = []
data.forEach((subj) => { data.forEach((subj) => {
result.push(subjectToString(subj)) result.push(subjectToString(subj))
}) })
@ -502,16 +473,13 @@ function dataToString(data: Array<Subject>): string {
function doSearch( function doSearch(
data: Array<Subject>, data: Array<Subject>,
subjName: string, subjName: string,
question: Question | string, question: Question,
questionData?: QuestionData,
searchTillMatchPercent?: number, searchTillMatchPercent?: number,
searchInAllIfNoResult?: Boolean searchInAllIfNoResult?: Boolean
): any { ): SearchResultQuestion[] {
let result = [] let result: SearchResultQuestion[] = []
const questionToSearch = prepareQuestion(question, questionData) const questionToSearch = prepareQuestion(question)
assert(questionToSearch.data)
data.every((subj) => { data.every((subj) => {
if ( if (
@ -569,7 +537,7 @@ function doSearch(
} }
result = setNoPossibleAnswersPenalties( result = setNoPossibleAnswersPenalties(
questionToSearch.possibleAnswers, questionToSearch.data.possibleAnswers,
result result
) )
@ -587,8 +555,8 @@ function doSearch(
} }
function setNoPossibleAnswersPenalties( function setNoPossibleAnswersPenalties(
possibleAnswers: any, possibleAnswers: QuestionData['possibleAnswers'],
result: any[] result: SearchResultQuestion[]
): any { ): any {
if (!Array.isArray(possibleAnswers)) { if (!Array.isArray(possibleAnswers)) {
return result return result
@ -602,7 +570,8 @@ function setNoPossibleAnswersPenalties(
const updated = result.map((result) => { const updated = result.map((result) => {
const hasMatch = result.q.data.possibleAnswers.some((possibleAnswer) => { const hasMatch = result.q.data.possibleAnswers.some((possibleAnswer) => {
return possibleAnswers.some((questionPossibleAnswer) => { return possibleAnswers.some((questionPossibleAnswer) => {
return questionPossibleAnswer.includes(questionPossibleAnswer) // FIXME: this could be object: questionPossibleAnswer
return questionPossibleAnswer.text.includes(possibleAnswer.text)
}) })
}) })
if (hasMatch) { if (hasMatch) {
@ -626,12 +595,24 @@ function setNoPossibleAnswersPenalties(
// Multi threaded stuff // Multi threaded stuff
// --------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------
interface WorkData {
subjName: string
question: Question
searchTillMatchPercent: number
searchInAllIfNoResult: boolean
searchIn: number[]
index: number
}
if (!isMainThread) { if (!isMainThread) {
// os.setPriority(10) // os.setPriority(10)
// logger.Log(`Worker thread priority set to ${os.getPriority()}`) // logger.Log(`Worker thread priority set to ${os.getPriority()}`)
const { workerIndex } = workerData const {
let qdbs: Array<QuestionDb> = workerData.initData workerIndex,
initData,
}: { workerIndex: number; initData: Array<QuestionDb> } = workerData
let qdbs: Array<QuestionDb> = initData
logger.Log( logger.Log(
`[THREAD #${workerIndex}]: Worker ${workerIndex} reporting for duty` `[THREAD #${workerIndex}]: Worker ${workerIndex} reporting for duty`
@ -642,30 +623,21 @@ if (!isMainThread) {
const { const {
subjName, subjName,
question, question,
questionData,
searchTillMatchPercent, searchTillMatchPercent,
searchInAllIfNoResult, searchInAllIfNoResult,
searchIn, searchIn,
index, index,
} = msg.data }: WorkData = msg.data
// console.log( let searchResult: SearchResultQuestion[] = []
// `[THREAD #${workerIndex}]: staring work${
// !isNaN(index) ? ` on job index #${index}` : ''
// }`
// )
let searchResult = []
try { try {
qdbs.forEach((qdb) => { qdbs.forEach((qdb) => {
// FIXME: === 0? if (searchIn.includes(qdb.index)) {
if (searchIn.length === 0 || searchIn.includes(qdb.index)) {
const res = doSearch( const res = doSearch(
qdb.data, qdb.data,
subjName, subjName,
question, question,
questionData,
searchTillMatchPercent, searchTillMatchPercent,
searchInAllIfNoResult searchInAllIfNoResult
) )
@ -686,17 +658,19 @@ if (!isMainThread) {
} catch (err) { } catch (err) {
logger.Log('Error in worker thread!', logger.GetColor('redbg')) logger.Log('Error in worker thread!', logger.GetColor('redbg'))
console.error(err) console.error(err)
console.error('subjName', subjName) console.error({
console.error('question', question) subjName: subjName,
console.error('questionData', questionData) question: question,
console.error('searchTillMatchPercent', searchTillMatchPercent) searchTillMatchPercent: searchTillMatchPercent,
console.error('searchInAllIfNoResult', searchInAllIfNoResult) searchInAllIfNoResult: searchInAllIfNoResult,
console.error('searchIn', searchIn) searchIn: searchIn,
console.error('index', index) index: index,
})
} }
// sorting // sorting
const sortedResult = searchResult.sort((q1, q2) => { const sortedResult: SearchResultQuestion[] = searchResult.sort(
(q1, q2) => {
if (q1.match < q2.match) { if (q1.match < q2.match) {
return 1 return 1
} else if (q1.match > q2.match) { } else if (q1.match > q2.match) {
@ -704,16 +678,19 @@ if (!isMainThread) {
} else { } else {
return 0 return 0
} }
}) }
)
// ONDONE: const workerResult: WorkerResult = {
parentPort.postMessage({
msg: `From thread #${workerIndex}: job ${ msg: `From thread #${workerIndex}: job ${
!isNaN(index) ? `#${index}` : '' !isNaN(index) ? `#${index}` : ''
}done`, }done`,
workerIndex: workerIndex, workerIndex: workerIndex,
result: sortedResult, result: sortedResult,
}) }
// ONDONE:
parentPort.postMessage(workerResult)
// console.log( // console.log(
// `[THREAD #${workerIndex}]: Work ${ // `[THREAD #${workerIndex}]: Work ${
@ -721,7 +698,7 @@ if (!isMainThread) {
// }done!` // }done!`
// ) // )
} else if (msg.type === 'dbEdit') { } else if (msg.type === 'dbEdit') {
const { dbIndex, edits } = msg.data const { dbIndex, edits }: { dbIndex: number; edits: Edits } = msg.data
const { resultDb } = editDb(qdbs[dbIndex], edits) const { resultDb } = editDb(qdbs[dbIndex], edits)
qdbs[dbIndex] = resultDb qdbs[dbIndex] = resultDb
logger.DebugLog(`Worker db edit ${workerIndex}`, 'worker update', 1) logger.DebugLog(`Worker db edit ${workerIndex}`, 'worker update', 1)
@ -731,7 +708,16 @@ if (!isMainThread) {
workerIndex: workerIndex, workerIndex: workerIndex,
}) })
} else if (msg.type === 'newQuestions') { } else if (msg.type === 'newQuestions') {
const { subjName, qdbIndex, newQuestions } = msg.data const {
subjName,
qdbIndex,
newQuestions,
}: {
subjName: string
qdbIndex: number
newQuestions: Question[]
} = msg.data
let added = false let added = false
qdbs = qdbs.map((qdb) => { qdbs = qdbs.map((qdb) => {
if (qdb.index === qdbIndex) { if (qdb.index === qdbIndex) {
@ -781,7 +767,8 @@ if (!isMainThread) {
// console.log(`[THREAD #${workerIndex}]: update`) // console.log(`[THREAD #${workerIndex}]: update`)
} else if (msg.type === 'newdb') { } else if (msg.type === 'newdb') {
qdbs.push(msg.newdb) const { data }: { data: QuestionDb } = msg
qdbs.push(data)
parentPort.postMessage({ parentPort.postMessage({
msg: `From thread #${workerIndex}: new db add done`, msg: `From thread #${workerIndex}: new db add done`,

View file

@ -15,7 +15,7 @@ export default {
runStatement: runStatement, runStatement: runStatement,
} }
import Sqlite from 'better-sqlite3' import Sqlite, { Database } from 'better-sqlite3'
import logger from '../utils/logger' import logger from '../utils/logger'
import utils from '../utils/utils' import utils from '../utils/utils'
@ -52,7 +52,7 @@ function GetDB(path: string): any {
return res return res
} }
function DebugLog(msg) { function DebugLog(msg: string) {
if (debugLog) { if (debugLog) {
logger.DebugLog(msg, 'sql', 0) logger.DebugLog(msg, 'sql', 0)
} }
@ -131,7 +131,7 @@ function CreateTable(db: any, name: any, columns: any, foreignKeys: any): any {
const cols = Object.keys(columns) const cols = Object.keys(columns)
.reduce((acc, key) => { .reduce((acc, key) => {
const item = columns[key] const item = columns[key]
const flags = [] const flags: string[] = []
const toCheck = { const toCheck = {
primary: 'PRIMARY KEY', primary: 'PRIMARY KEY',
notNull: 'NOT NULL', notNull: 'NOT NULL',
@ -150,16 +150,22 @@ function CreateTable(db: any, name: any, columns: any, foreignKeys: any): any {
}, []) }, [])
.join(', ') .join(', ')
const fKeys = [] const fKeys: string[] = []
if (foreignKeys) { if (foreignKeys) {
foreignKeys.forEach((foreignKey) => { foreignKeys.forEach(
(foreignKey: {
keysFrom: string[]
table: string
keysTo: string[]
}) => {
const { keysFrom, table, keysTo } = foreignKey const { keysFrom, table, keysTo } = foreignKey
fKeys.push( fKeys.push(
`, FOREIGN KEY(${keysFrom.join( `, FOREIGN KEY(${keysFrom.join(
', ' ', '
)}) REFERENCES ${table}(${keysTo.join(', ')})` )}) REFERENCES ${table}(${keysTo.join(', ')})`
) )
}) }
)
} }
// IF NOT EXISTS // IF NOT EXISTS
@ -244,7 +250,7 @@ function runStatement(db: any, command: string, runType?: string): any {
} }
function CloseDB(db: any): void { function CloseDB(db: any): void {
db.close((err) => { db.close((err: Error) => {
if (err) { if (err) {
return console.error(err.message) return console.error(err.message)
} }
@ -254,7 +260,7 @@ function CloseDB(db: any): void {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
function PrepareStatement(db, command) { function PrepareStatement(db: Database, command: string) {
if (!db) { if (!db) {
throw new Error( throw new Error(
'DB is undefined in prepare statement! DB action called with undefined db' 'DB is undefined in prepare statement! DB action called with undefined db'

View file

@ -68,7 +68,7 @@ function LogId(
Save() Save()
} }
function AddSubjToList(list: Array<string>, subj: string) { function AddSubjToList(list: { [key: string]: any }, subj: string) {
if (!list[subj]) { if (!list[subj]) {
list[subj] = 0 list[subj] = 0
} }

View file

@ -52,7 +52,7 @@ let uvData = {} // visit data, but per user
let udvData = {} // visit data, but per user and daily let udvData = {} // visit data, but per user and daily
let writes = 0 let writes = 0
let noLogips = [] let noLogips: string[] = []
function getColoredDateString(): string { function getColoredDateString(): string {
const date = new Date() const date = new Date()
@ -93,14 +93,18 @@ function Log(msg: string | object, color?: string): void {
) )
} }
function expandWithSpaces(text, count) { function expandWithSpaces(text: string, count: number) {
while (text.length < count) { while (text.length < count) {
text += ' ' text += ' '
} }
return text return text
} }
function LogReq(req: Request, toFile?: boolean, statusCode?: string): void { function LogReq(
req: Request,
toFile?: boolean,
statusCode?: string | number
): void {
try { try {
const ip: any = const ip: any =
req.headers['cf-connecting-ip'] || req.connection.remoteAddress req.headers['cf-connecting-ip'] || req.connection.remoteAddress
@ -173,7 +177,7 @@ function LogReq(req: Request, toFile?: boolean, statusCode?: string): void {
} }
} }
function parseNoLogFile(newData) { function parseNoLogFile(newData: string) {
noLogips = newData.split('\n') noLogips = newData.split('\n')
if (noLogips[noLogips.length - 1] === '') { if (noLogips[noLogips.length - 1] === '') {
noLogips.pop() noLogips.pop()
@ -185,7 +189,7 @@ function parseNoLogFile(newData) {
} }
function setNoLogReadInterval() { function setNoLogReadInterval() {
utils.WatchFile(nologFile, (newData) => { utils.WatchFile(nologFile, (newData: string) => {
parseNoLogFile(newData) parseNoLogFile(newData)
}) })
@ -227,19 +231,7 @@ function Load(): void {
setNoLogReadInterval() setNoLogReadInterval()
} }
function LogStat( function LogStat(url: string, hostname: string, userId: number): void {
url: string,
ip: string,
hostname: string,
userId: number
): void {
const nolog = noLogips.some((noLogips) => {
return ip.includes(noLogips)
})
if (nolog) {
return
}
url = hostname + url.split('?')[0] url = hostname + url.split('?')[0]
Inc(url) Inc(url)
AddUserIdStat(userId) AddUserIdStat(userId)
@ -248,7 +240,7 @@ function LogStat(
Save() Save()
} }
function IncUserStat(userId) { function IncUserStat(userId: number) {
try { try {
if (uvData[userId] === undefined) { if (uvData[userId] === undefined) {
uvData[userId] = 0 uvData[userId] = 0
@ -260,7 +252,7 @@ function IncUserStat(userId) {
} }
} }
function AddUserIdStat(userId) { function AddUserIdStat(userId: number) {
try { try {
const date = new Date() const date = new Date()
const now = const now =
@ -282,7 +274,7 @@ function AddUserIdStat(userId) {
} }
} }
function Inc(value) { function Inc(value: string) {
if (value.startsWith('/?')) { if (value.startsWith('/?')) {
value = '/' value = '/'
} }
@ -292,7 +284,7 @@ function Inc(value) {
vData[value]++ vData[value]++
} }
function AddVisitStat(name) { function AddVisitStat(name: string) {
const date = new Date() const date = new Date()
const now = const now =
date.getFullYear() + date.getFullYear() +
@ -346,7 +338,7 @@ function logHashed(msg: string): string {
return GetRandomColor(msg.toString()) + msg + C() return GetRandomColor(msg.toString()) + msg + C()
} }
function GetRandomColor(msg): string { function GetRandomColor(msg: string): string {
if (!msg) { if (!msg) {
return 'red' return 'red'
} }

View file

@ -211,7 +211,7 @@ function uploadFile(req: Request, path: string): Promise<any> {
fileDestination = path + '/' + fileName fileDestination = path + '/' + fileName
} }
file.mv(fileDestination, (err) => { file.mv(fileDestination, (err: Error) => {
if (err) { if (err) {
logger.Log(`Unable to upload file!`, logger.GetColor('redbg')) logger.Log(`Unable to upload file!`, logger.GetColor('redbg'))
console.error(err) console.error(err)

View file

@ -4,19 +4,52 @@ import { EventEmitter } from 'events'
import os from 'os' import os from 'os'
import logger from './logger' import logger from './logger'
import { Result } from './actions'
import type { Question, QuestionDb, QuestionData } from '../types/basicTypes'
import type { WorkerResult } from './classes'
interface WorkerObj { interface WorkerObj {
worker: any worker: Worker
index: number index: number
free: Boolean free: Boolean
} }
interface TaskObject {
type: 'work' | 'dbEdit' | 'newQuestions' | 'newdb'
data:
| {
searchIn: number[]
question: Question
subjName: string
testUrl?: string
questionData?: QuestionData
searchInAllIfNoResult?: boolean
searchTillMatchPercent?: number
[key: string]: any
}
| { dbIndex: number; edits: any }
| QuestionDb
| Result
}
interface PendingJob { interface PendingJob {
workData: any workData: TaskObject
doneEvent: any doneEvent: DoneEvent
targetWorkerIndex?: number targetWorkerIndex?: number
} }
interface JobEvent extends EventEmitter {
on(event: 'jobDone', listener: () => void): this
on(event: 'newJob', listener: () => void): this
emit(event: 'newJob'): boolean
emit(event: 'jobDone'): boolean
}
interface DoneEvent extends EventEmitter {
once(event: 'done', listener: (result: WorkerResult) => void): this
emit(event: 'done', res: WorkerResult): boolean
}
const alertOnPendingCount = 50 const alertOnPendingCount = 50
const workerFile = './src/utils/classes.ts' const workerFile = './src/utils/classes.ts'
let workers: Array<WorkerObj> let workers: Array<WorkerObj>
@ -24,7 +57,7 @@ const pendingJobs: {
[id: string]: PendingJob [id: string]: PendingJob
} = {} } = {}
const jobEvents = new EventEmitter() const jobEvents: JobEvent = new EventEmitter()
jobEvents.on('jobDone', () => { jobEvents.on('jobDone', () => {
processJob() processJob()
@ -36,17 +69,17 @@ jobEvents.on('newJob', () => {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function handleWorkerError(worker: WorkerObj, err) { function handleWorkerError(worker: WorkerObj, err: Error) {
// TODO: restart worker if exited or things like that // TODO: restart worker if exited or things like that
logger.Log('resourcePromise error', logger.GetColor('redbg')) logger.Log('resourcePromise error', logger.GetColor('redbg'))
console.error(err) console.error(err, worker)
} }
// TODO: accuire all workers here, and handle errors so they can be removed if threads exit // TODO: accuire all workers here, and handle errors so they can be removed if threads exit
export function msgAllWorker(data: any): Promise<any> { export function msgAllWorker(data: TaskObject): Promise<any> {
logger.DebugLog('MSGING ALL WORKER', 'job', 1) logger.DebugLog('MSGING ALL WORKER', 'job', 1)
return new Promise((resolve) => { return new Promise((resolve) => {
const promises = [] const promises: Promise<WorkerResult>[] = []
workers.forEach((worker) => { workers.forEach((worker) => {
promises.push(doALongTask(data, worker.index)) promises.push(doALongTask(data, worker.index))
}) })
@ -58,7 +91,7 @@ export function msgAllWorker(data: any): Promise<any> {
} }
export function doALongTask( export function doALongTask(
obj: any, obj: TaskObject,
targetWorkerIndex?: number targetWorkerIndex?: number
): Promise<any> { ): Promise<any> {
if (Object.keys(pendingJobs).length > alertOnPendingCount) { if (Object.keys(pendingJobs).length > alertOnPendingCount) {
@ -72,7 +105,7 @@ export function doALongTask(
const jobId = uuidv4() const jobId = uuidv4()
// FIXME: delete doneEvent? // FIXME: delete doneEvent?
const doneEvent = new EventEmitter() const doneEvent: DoneEvent = new EventEmitter()
pendingJobs[jobId] = { pendingJobs[jobId] = {
workData: obj, workData: obj,
targetWorkerIndex: targetWorkerIndex, targetWorkerIndex: targetWorkerIndex,
@ -80,7 +113,7 @@ export function doALongTask(
} }
jobEvents.emit('newJob') jobEvents.emit('newJob')
return new Promise((resolve) => { return new Promise((resolve) => {
doneEvent.once('done', (result) => { doneEvent.once('done', (result: WorkerResult) => {
jobEvents.emit('jobDone') jobEvents.emit('jobDone')
resolve(result) resolve(result)
}) })
@ -90,7 +123,7 @@ export function doALongTask(
export function initWorkerPool(initData: any): Array<WorkerObj> { export function initWorkerPool(initData: any): Array<WorkerObj> {
if (workers) { if (workers) {
logger.Log('WORKERS ALREADY EXISTS', logger.GetColor('redbg')) logger.Log('WORKERS ALREADY EXISTS', logger.GetColor('redbg'))
return return null
} }
workers = [] workers = []
@ -119,7 +152,7 @@ function processJob() {
if (Object.keys(pendingJobs).length > 0) { if (Object.keys(pendingJobs).length > 0) {
// FIXME: FIFO OR ANYTHING ELSE (JOB PROCESSING ORDER) // FIXME: FIFO OR ANYTHING ELSE (JOB PROCESSING ORDER)
const keys = Object.keys(pendingJobs) const keys = Object.keys(pendingJobs)
let jobKey, freeWorker let jobKey: string, freeWorker: WorkerObj
let i = 0 let i = 0
while (!freeWorker && i < keys.length) { while (!freeWorker && i < keys.length) {
jobKey = keys[i] jobKey = keys[i]
@ -159,7 +192,7 @@ function processJob() {
delete pendingJobs[jobKey] delete pendingJobs[jobKey]
doSomething(freeWorker, job.workData) doSomething(freeWorker, job.workData)
.then((res) => { .then((res: WorkerResult) => {
freeWorker.free = true freeWorker.free = true
job.doneEvent.emit('done', res) job.doneEvent.emit('done', res)
}) })
@ -169,7 +202,7 @@ function processJob() {
} }
} }
function getAWorker(i, initData) { function getAWorker(i: number, initData: Array<QuestionDb>) {
const worker = workerTs(workerFile, { const worker = workerTs(workerFile, {
workerData: { workerData: {
workerIndex: i, workerIndex: i,
@ -196,11 +229,11 @@ function getAWorker(i, initData) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function doSomething(currWorker, obj) { function doSomething(currWorker: WorkerObj, obj: TaskObject) {
const { /* index, */ worker } = currWorker const { /* index, */ worker } = currWorker
return new Promise((resolve) => { return new Promise((resolve) => {
worker.postMessage(obj) worker.postMessage(obj)
worker.once('message', (msg) => { worker.once('message', (msg: WorkerResult) => {
resolve(msg) resolve(msg)
}) })
}) })

@ -1 +1 @@
Subproject commit 57a7ab9fb7e6148e488cc492a8d8fb55de211bf6 Subproject commit 8e4e24bed641960f75156301f29ad6fc29d9c754

View file

@ -1,5 +1,10 @@
{ {
"compilerOptions": { "compilerOptions": {
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns":true,
"noFallthroughCasesInSwitch": true,
"suppressImplicitAnyIndexErrors": true,
"moduleResolution": "node", "moduleResolution": "node",
"esModuleInterop": true, "esModuleInterop": true,
"target": "es6", "target": "es6",
@ -9,7 +14,7 @@
"preserveConstEnums": true, "preserveConstEnums": true,
"sourceMap": true, "sourceMap": true,
"outDir": "dist", "outDir": "dist",
"noImplicitAny": false, "noImplicitAny": true,
"lib": [ "lib": [
"ES2020" "ES2020"
] ]