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

@ -19,16 +19,18 @@
------------------------------------------------------------------------- */
// package requires
import express from 'express'
import bodyParser from 'body-parser'
import express, { RequestHandler } from 'express'
import fileUpload from 'express-fileupload'
import type { Database } from 'better-sqlite3'
import http from 'http'
import https from 'https'
// other requires
import logger from '../../utils/logger'
import utils from '../../utils/utils'
import auth from '../../middlewares/auth.middleware'
import { SetupData } from '../../server'
import { ModuleType, Request } from '../../types/basicTypes'
import { ModuleType, Request, Submodule } from '../../types/basicTypes'
// files
const rootRedirectToFile = 'data/apiRootRedirectTo'
@ -37,11 +39,11 @@ const rootRedirectToFile = 'data/apiRootRedirectTo'
const moduleName = 'API'
// stuff gotten from server.js
let userDB
let url
let publicdirs = []
let httpServer
let httpsServer
let userDB: Database
let url: string
let publicdirs: string[] = []
let httpServer: http.Server
let httpsServer: https.Server
function GetApp(): ModuleType {
const app = express()
@ -51,7 +53,7 @@ function GetApp(): ModuleType {
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 = domain.join('.') // "frylabs.net"
logger.DebugLog(`Cookie domain: ${domain}`, 'cookie', 1)
@ -59,15 +61,15 @@ function GetApp(): ModuleType {
// -------------------------------------------------------------------------------------------
app.use(
bodyParser.urlencoded({
express.urlencoded({
limit: '10mb',
extended: true,
})
}) as RequestHandler
)
app.use(
bodyParser.json({
express.json({
limit: '10mb',
})
}) as RequestHandler
)
app.set('view engine', 'ejs')
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')
})
app.post('*', function (req: Request, res: any) {
app.post('*', function (_req: Request, res: any) {
res.status(404).render('404')
})
@ -182,14 +184,14 @@ function GetApp(): ModuleType {
function setupSubModules(
parentApp: express.Application,
moduleSpecificData?: any
): any {
): Submodule[] {
const submoduleDir = './submodules/'
const absolutePath = __dirname + '/' + submoduleDir
if (!utils.FileExists(absolutePath)) {
return
return null
}
const files = utils.ReadDir(absolutePath)
const moduleDatas = []
const moduleDatas: Submodule[] = []
files.forEach((file) => {
if (!file.endsWith('.js')) {
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
}
interface Message {
id: number
sender: number
reciever: number
msg: string
type: string
date: number
unread: number
}
function setup(data: SubmoduleData): void {
const { app, httpServer, httpsServer, userDB, publicdirs } = data
const msgDB = dbtools.GetDB(msgDbPath)
@ -28,8 +38,13 @@ function setup(data: SubmoduleData): void {
},
})
function chatMessageRead(data) {
const { sender, reciever } = data
function chatMessageRead({
sender,
reciever,
}: {
sender: number
reciever: number
}) {
dbtools.runStatement(
msgDB,
`update msgs
@ -50,7 +65,7 @@ function setup(data: SubmoduleData): void {
socket.on('join', function (/*data*/) {
socket.join(userid.toString())
const groups = dbtools
const groups: number[] = dbtools
.runStatement(
msgDB,
`select * from
@ -65,14 +80,14 @@ function setup(data: SubmoduleData): void {
)t
order by t.a asc`
)
.reduce((acc, x) => {
.reduce((acc: number[], x: { a: number }) => {
if (x.a !== userid) acc.push(x.a)
return acc
}, [])
socket.emit('prev messages', {
prevMsgs: groups.map((to) => {
const first = dbtools.runStatement(
const first: Message = dbtools.runStatement(
msgDB,
`select * from msgs
where sender = ${userid} and reciever = ${to} or
@ -147,7 +162,7 @@ function setup(data: SubmoduleData): void {
// socket.on('close', () => {})
})
app.post('/postchatfile', function (req: Request, res: any) {
app.post('/postchatfile', function (req: Request, res) {
logger.LogReq(req)
utils
.uploadFile(req, uloadFiles)
@ -189,12 +204,12 @@ function setup(data: SubmoduleData): void {
)t
order by t.a asc`
)
.reduce((acc, x) => {
.reduce((acc: number[], x: { a: number }) => {
if (x.a !== userid) acc.push(x.a)
return acc
}, [])
const prevMsgs = groups.map((to) => {
const prevMsgs = groups.map((to: number) => {
const first = dbtools.runStatement(
msgDB,
`select * from msgs
@ -207,7 +222,7 @@ function setup(data: SubmoduleData): void {
})
res.json({
unreads: prevMsgs.reduce((acc, msg) => {
unreads: prevMsgs.reduce((acc: number[], msg: Message) => {
if (msg && msg.unread === 1 && msg.sender !== userid) {
acc.push(msg.sender)
}

View file

@ -1,3 +1,5 @@
import { Response } from 'express'
import logger from '../../../utils/logger'
import utils from '../../../utils/utils'
import { Request, SubmoduleData, User } from '../../../types/basicTypes'
@ -8,7 +10,7 @@ const uloadFiles = 'data/f'
function setup(data: SubmoduleData): void {
const { app /* userDB, url, publicdirs, moduleSpecificData */ } = data
app.post('/postfeedbackfile', function (req: Request, res: any) {
app.post('/postfeedbackfile', function (req: Request, res: Response) {
utils
.uploadFile(req, uloadFiles)
.then(() => {
@ -23,7 +25,7 @@ function setup(data: SubmoduleData): void {
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)
if (req.body.fromLogin) {
logger.Log(
@ -55,7 +57,7 @@ function setup(data: SubmoduleData): void {
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 }) => {
res.redirect('/f/' + fileName)
})

View file

@ -4,10 +4,40 @@ import logger from '../../../utils/logger'
import utils from '../../../utils/utils'
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 forumContentsFileName = '.contents.json'
function countComments(comments) {
function countComments(comments: Comment[]) {
return comments.reduce((acc, comment) => {
if (comment.subComments) {
acc += countComments(comment.subComments) + 1
@ -18,7 +48,7 @@ function countComments(comments) {
}, 0)
}
function addComment(obj, path, comment) {
function addComment(obj: Comment[], path: number[], comment: Comment) {
if (path.length === 0) {
obj.push(comment)
} 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 (obj[path[0]].user === userid) {
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) {
const index = path[0]
if (!obj[index].reacts) {
@ -54,8 +96,8 @@ function addReaction(obj, path, { reaction, isDelete, uid }) {
if (isDelete) {
if (obj[index].reacts[reaction]) {
obj[index].reacts[reaction] = obj[index].reacts[reaction].filter(
(uid) => {
return uid !== uid
(currUid: number) => {
return uid !== currUid
}
)
if (obj[index].reacts[reaction].length === 0) {
@ -84,7 +126,7 @@ function addReaction(obj, path, { reaction, isDelete, uid }) {
function getForumData(
forumName: string,
forumDir: string
): { forumPath: string; contentFilePath: string; contents: any } {
): { forumPath: string; contentFilePath: string; contents: ForumContents[] } {
const safeForumName = forumName.replace(/\./g, '').replace(/\/+/g, '')
const forumPath = forumDir + '/' + safeForumName
const contentFilePath = forumPath + '/' + forumContentsFileName
@ -96,7 +138,7 @@ function getForumData(
if (!utils.FileExists(contentFilePath)) {
utils.WriteFile('{}', contentFilePath)
}
const contents = utils.ReadJSON(contentFilePath)
const contents: ForumContents[] = utils.ReadJSON(contentFilePath)
return {
forumPath: forumPath,
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 postPath = forumPath + '/' + safePostKey
if (!contents[postKey] || !utils.FileExists(postPath)) {
return
return null
}
return { postData: utils.ReadJSON(postPath), postPath: postPath }
}
@ -167,6 +216,7 @@ function setup(data: SubmoduleData): void {
if (passed) {
i++
}
return false
})
res.json({
@ -175,218 +225,263 @@ function setup(data: SubmoduleData): void {
})
})
app.post('/addPost', (req: Request, res) => {
logger.LogReq(req)
app.post(
'/addPost',
(
req: Request<{
forumName: string
content: string
title: string
}>,
res
) => {
logger.LogReq(req)
const { title, content } = req.body
const forumName: any = req.body.forumName
if (!forumName) {
res.json({
success: false,
msg: `Forum name is not specified!`,
})
return
}
const { forumPath, contents, contentFilePath } = getForumData(
forumName,
forumDir
)
const user: User = req.session.user
const admins: any = utils.FileExists(adminUsersFile)
? utils.ReadJSON(adminUsersFile)
: []
const { title, content } = req.body
const forumName = req.body.forumName
if (!forumName) {
res.json({
success: false,
msg: `Forum name is not specified!`,
})
return
}
const { forumPath, contents, contentFilePath } = getForumData(
forumName,
forumDir
)
const user: User = req.session.user
const admins: any = utils.FileExists(adminUsersFile)
? utils.ReadJSON(adminUsersFile)
: []
const newPostKey = uuidv4()
const postData = {
date: utils.GetDateString(),
user: user.id,
title: title,
admin: admins.includes(user.id),
commentCount: 0,
}
contents[newPostKey] = postData
utils.WriteFile(JSON.stringify(contents, null, 2), contentFilePath)
utils.WriteFile(
JSON.stringify({ ...postData, content: content }),
forumPath + '/' + newPostKey
)
res.json({
success: true,
newPostKey: newPostKey,
newEntry: { ...postData, content: content },
})
})
app.post('/rmPost', (req: Request, res) => {
logger.LogReq(req)
const { postKey } = req.body
const forumName: any = req.body.forumName
if (!forumName) {
res.json({
success: false,
msg: `Forum name is not specified!`,
})
return
}
const { forumPath, contentFilePath, contents } = getForumData(
forumName,
forumDir
)
const user: User = req.session.user
if (contents[postKey] && contents[postKey].user === user.id) {
utils.deleteFile(forumPath + '/' + postKey)
delete contents[postKey]
utils.WriteFile(JSON.stringify(contents), contentFilePath)
res.json({ success: true, deletedId: postKey })
} else {
res.json({
success: false,
msg:
'Entry does not exists, or you are not authorized to delete this post',
})
return
}
})
app.post('/comment', (req: Request, res) => {
logger.LogReq(req)
const forumName: any = req.body.forumName
if (!forumName) {
res.json({
success: false,
msg: `Forum name is not specified!`,
})
return
}
const { forumPath, contentFilePath, contents } = getForumData(
forumName,
forumDir
)
const user: User = req.session.user
const admins: any = utils.FileExists(adminUsersFile)
? utils.ReadJSON(adminUsersFile)
: []
const { type, path, postKey } = req.body
if (!postKey || !type || !path) {
res.json({ success: false, msg: 'type or path or postKey is undefined' })
return
}
const { postPath, postData } = getPostData(postKey, contents, forumPath)
if (!postData) {
res.json({
success: false,
msg: `Post entry '${postKey}' from forum '${forumName}' does not exits!`,
})
}
if (type === 'add') {
const { content } = req.body
const comment = {
const newPostKey = uuidv4()
const postData = {
date: utils.GetDateString(),
user: user.id,
content: content,
title: title,
admin: admins.includes(user.id),
commentCount: 0,
}
if (!postData.comments) {
postData.comments = []
contents[newPostKey] = postData
utils.WriteFile(JSON.stringify(contents, null, 2), contentFilePath)
utils.WriteFile(
JSON.stringify({ ...postData, content: content }),
forumPath + '/' + newPostKey
)
res.json({
success: true,
newPostKey: newPostKey,
newEntry: { ...postData, content: content },
})
}
)
app.post(
'/rmPost',
(
req: Request<{
postKey: string
forumName: string
}>,
res
) => {
logger.LogReq(req)
const { postKey } = req.body
const forumName: any = req.body.forumName
if (!forumName) {
res.json({
success: false,
msg: `Forum name is not specified!`,
})
return
}
addComment(postData.comments, path, comment)
} else if (type === 'delete') {
if (postData.comments) {
const success = deleteComment(postData.comments, path, user.id)
if (!success) {
res.json({
success: false,
msg: 'you cant delete other users comments',
postData: postData,
})
return
const { forumPath, contentFilePath, contents } = getForumData(
forumName,
forumDir
)
const user: User = req.session.user
if (contents[postKey] && contents[postKey].user === user.id) {
utils.deleteFile(forumPath + '/' + postKey)
delete contents[postKey]
utils.WriteFile(JSON.stringify(contents), contentFilePath)
res.json({ success: true, deletedId: postKey })
} else {
res.json({
success: false,
msg: 'Entry does not exists, or you are not authorized to delete this post',
})
return
}
}
)
app.post(
'/comment',
(
req: Request<{
type: string
path: number[]
postKey: string
forumName: string
content: string
}>,
res
) => {
logger.LogReq(req)
const forumName: any = req.body.forumName
if (!forumName) {
res.json({
success: false,
msg: `Forum name is not specified!`,
})
return
}
const { forumPath, contentFilePath, contents } = getForumData(
forumName,
forumDir
)
const user: User = req.session.user
const admins: any = utils.FileExists(adminUsersFile)
? utils.ReadJSON(adminUsersFile)
: []
const { type, path, postKey } = req.body
if (!postKey || !type || !path) {
res.json({
success: false,
msg: 'type or path or postKey is undefined',
})
return
}
const { postPath, postData } = getPostData(postKey, contents, forumPath)
if (!postData) {
res.json({
success: false,
msg: `Post entry '${postKey}' from forum '${forumName}' does not exits!`,
})
}
if (type === 'add') {
const { content } = req.body
const comment = {
date: utils.GetDateString(),
user: user.id,
content: content,
admin: admins.includes(user.id),
}
}
} else {
res.json({ success: false, msg: 'no such type' })
return
}
contents[postKey].commentCount = countComments(postData.comments)
utils.WriteFile(JSON.stringify(postData, null, 2), postPath)
utils.WriteFile(JSON.stringify(contents, null, 2), contentFilePath)
res.json({ success: true, postKey: postKey, postData: postData })
})
app.post('/react', (req: Request, res) => {
logger.LogReq(req)
const forumName: any = req.body.forumName
if (!forumName) {
res.json({
success: false,
msg: `Forum name is not specified!`,
})
return
}
const { forumPath, contents } = getForumData(forumName, forumDir)
const user: User = req.session.user
const { postKey, path, reaction, isDelete } = req.body
if (!postKey || !reaction) {
res.json({ success: false, msg: 'no postkey or reaction' })
return
}
const { postPath, postData } = getPostData(postKey, contents, forumPath)
if (!postData) {
res.json({
success: false,
msg: `Post entry '${postKey}' from forum '${forumName}' does not exits!`,
})
}
if (!path || path.length === 0) {
if (postData) {
if (isDelete) {
if (postData.reacts) {
postData.reacts[reaction] = postData.reacts[reaction].filter(
(uid) => {
return uid !== user.id
}
)
if (postData.reacts[reaction].length === 0) {
delete postData.reacts[reaction]
}
if (!postData.comments) {
postData.comments = []
}
addComment(postData.comments, path, comment)
} else if (type === 'delete') {
if (postData.comments) {
const success = deleteComment(postData.comments, path, user.id)
if (!success) {
res.json({
success: false,
msg: 'you cant delete other users comments',
postData: postData,
})
return
}
} else {
if (!postData.reacts) {
postData.reacts = { [reaction]: [user.id] }
}
} else {
res.json({ success: false, msg: 'no such type' })
return
}
contents[postKey].commentCount = countComments(postData.comments)
utils.WriteFile(JSON.stringify(postData, null, 2), postPath)
utils.WriteFile(JSON.stringify(contents, null, 2), contentFilePath)
res.json({ success: true, postKey: postKey, postData: postData })
}
)
app.post(
'/react',
(
req: Request<{
forumName: string
postKey: string
path: number[]
reaction: string
isDelete: boolean
}>,
res
) => {
logger.LogReq(req)
const forumName: any = req.body.forumName
if (!forumName) {
res.json({
success: false,
msg: `Forum name is not specified!`,
})
return
}
const { forumPath, contents } = getForumData(forumName, forumDir)
const user: User = req.session.user
const { postKey, path, reaction, isDelete } = req.body
if (!postKey || !reaction) {
res.json({ success: false, msg: 'no postkey or reaction' })
return
}
const { postPath, postData } = getPostData(postKey, contents, forumPath)
if (!postData) {
res.json({
success: false,
msg: `Post entry '${postKey}' from forum '${forumName}' does not exits!`,
})
}
if (!path || path.length === 0) {
if (postData) {
if (isDelete) {
if (postData.reacts) {
postData.reacts[reaction] = postData.reacts[reaction].filter(
(uid) => {
return uid !== user.id
}
)
if (postData.reacts[reaction].length === 0) {
delete postData.reacts[reaction]
}
}
} else {
if (Array.isArray(postData.reacts[reaction])) {
if (!postData.reacts[reaction].includes(user.id)) {
postData.reacts[reaction].push(user.id)
}
if (!postData.reacts) {
postData.reacts = { [reaction]: [user.id] }
} else {
postData.reacts[reaction] = [user.id]
if (Array.isArray(postData.reacts[reaction])) {
if (!postData.reacts[reaction].includes(user.id)) {
postData.reacts[reaction].push(user.id)
}
} else {
postData.reacts[reaction] = [user.id]
}
}
}
}
} else {
addReaction(postData.comments, path, {
reaction: reaction,
isDelete: isDelete,
uid: user.id,
})
}
} else {
addReaction(postData.comments, path, {
reaction: reaction,
isDelete: isDelete,
uid: user.id,
})
}
utils.WriteFile(JSON.stringify(postData, null, 2), postPath)
res.json({ success: true, postData: postData })
})
utils.WriteFile(JSON.stringify(postData, null, 2), postPath)
res.json({ success: true, postData: postData })
}
)
}
export default {

View file

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

View file

@ -4,12 +4,24 @@ import { Request, SubmoduleData, User } from '../../../types/basicTypes'
const quickVoteResultsDir = 'stats/qvote'
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 {
const { app /* userDB, url, publicdirs, moduleSpecificData */ } = data
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 user: User = req.session.user
@ -22,7 +34,7 @@ function setup(data: SubmoduleData): void {
}
// FIXME: check vote type in file
let votes: any = {}
let votes: QuickVotes = {}
if (utils.FileExists(quickVotes)) {
votes = utils.ReadJSON(quickVotes)
} else {
@ -49,7 +61,7 @@ function setup(data: SubmoduleData): void {
const voteFile = quickVoteResultsDir + '/' + key + '.json'
let voteData = {
let voteData: QuickVote = {
votes: {},
sum: {},
}

View file

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

View file

@ -1,17 +1,66 @@
import { Response } from 'express'
import logger from '../../../utils/logger'
import utils from '../../../utils/utils'
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'
function setup(data: SubmoduleData): void {
const { app /* userDB, url, publicdirs, moduleSpecificData */ } = data
app.get('/voteTodo', (req: Request, res: any) => {
app.get('/voteTodo', (req: Request, res: Response) => {
logger.LogReq(req)
const userId = req.session.user.id
const id: any = req.query.id
const todos = utils.ReadJSON(todosFile)
const todos: Todos = utils.ReadJSON(todosFile)
if (!id) {
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)
const userId = req.session.user.id
const todos = utils.ReadJSON(todosFile)

View file

@ -6,7 +6,7 @@ import { Request, SubmoduleData, User } from '../../../types/basicTypes'
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 dir = userFilesDir + '/' + safeSubdir
const usersFile = dir + '/' + dataFileName
@ -16,7 +16,6 @@ function listDir(publicDir, subdir, userFilesDir) {
success: false,
msg: `Directory ${subdir} does not exists`,
}
return
}
if (!utils.FileExists(usersFile)) {
utils.WriteFile('{}', usersFile)
@ -28,7 +27,6 @@ function listDir(publicDir, subdir, userFilesDir) {
success: false,
msg: `Path '${safeSubdir}' does not exists`,
}
return
}
return {
@ -65,7 +63,7 @@ function listDir(publicDir, subdir, userFilesDir) {
function setup(data: SubmoduleData): void {
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
try {
if (req.url.includes('/userFiles/')) {
@ -264,7 +262,7 @@ function setup(data: SubmoduleData): void {
data[fname].downvotes = []
}
const removeVote = (from, uid) => {
const removeVote = (from: number[], uid: number) => {
if (!from.includes(uid)) {
return from
}

View file

@ -1,4 +1,5 @@
import { v4 as uuidv4 } from 'uuid'
import type { Database } from 'better-sqlite3'
import logger from '../../../utils/logger'
import utils from '../../../utils/utils'
@ -11,7 +12,15 @@ const usersDbBackupPath = 'data/dbs/backup'
const maxPWCount = 3
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 ...')
utils.CreatePath(usersDbBackupPath, true)
userDB
@ -23,7 +32,7 @@ function BackupDB(usersDbBackupPath, userDB) {
.then(() => {
logger.Log('Auth DB backup complete!')
})
.catch((err) => {
.catch((err: Error) => {
logger.Log('Auth DB backup failed!', logger.GetColor('redbg'))
console.error(err)
})
@ -62,8 +71,7 @@ function setup(data: SubmoduleData): any {
res.json({
result: 'error',
success: false,
msg:
'Too many passwords requested or cant request password yet, try later',
msg: 'Too many passwords requested or cant request password yet, try later',
})
logger.Log(
`User #${requestingUser.id} requested too much passwords`,
@ -128,8 +136,10 @@ function setup(data: SubmoduleData): any {
userID: user.id,
isScript: isScript ? 1 : 0,
})
.sort((a, b) => {
return new Date(a).getTime() - new Date(b).getTime()
.sort((a: Session, b: Session) => {
return (
new Date(a.lastAccess).getTime() - new Date(b.lastAccess).getTime()
)
})
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()
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