diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee333a8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +data/ +stats/ +node_modules/ +dist/ +nextStatic/ +publicDirs/ +extraModules/ +nolog + diff --git a/devel/tests/testData/withLocation.json b/devel/tests/testData/withLocation.json new file mode 100644 index 0000000..6c8ae4c --- /dev/null +++ b/devel/tests/testData/withLocation.json @@ -0,0 +1,78 @@ +{ + "subj": "Válgazd II. 2020S BSc Nap EA", + "version": "2.1.0.0", + "id": "1582138883656", + "location": "https://bacon.com", + "quiz": [ + { + "Q": "Ha a forgóeszközök értéke 42000, a készletek 10000 és a rövid lejáratú kötelezettségek 33600, mekkora a likviditási ráta (L2) (két tizedesig)?", + "A": "A helyes válasz: 1,25.", + "data": { + "type": "simple" + } + }, + { + "Q": "Folyó források lehetnek", + "A": "c. Rövid lejáratú hitel, szállítói kötelezettségek, ÁFA tartozás", + "data": { + "type": "simple" + } + }, + { + "Q": "A pénzügyi tervezés és gazdálkodás módszerei a következők:", + "A": "b. mobilitás vizsgálat,likviditási terv, diszponibilitás", + "data": { + "type": "simple" + } + }, + { + "Q": "A mérlegben az eszközök pénzzétételi lehetősége és a források esedékessége között egyensúly kell hogy fenn álljon.", + "A": "Igaz", + "data": { + "type": "simple" + } + }, + { + "Q": "Ha a nettó forgótőke pozitív, akkor a vállalkozás rövid lejáratú forrásokból fedezi befektetett eszközei egy részét.", + "A": "Hamis", + "data": { + "type": "simple" + } + }, + { + "Q": "Az adósságszolgálati mutató a vállalati tiszta jövedelem és az azt terhelő kötelezettségek hányadosa.", + "A": "Igaz", + "data": { + "type": "simple" + } + }, + { + "Q": "Az éves beszámoló részei:", + "A": "b. Mérleg, eredménykimutatás, kiegészítő melléklet", + "data": { + "type": "simple" + } + }, + { + "Q": "Ha a vállalati saját tőkéje 25M, és az összes kötelezettsége 100M, mekkora a vállalat tőkeellátottsági mutatója (%)?", + "A": "A helyes válasz: 20.", + "data": { + "type": "simple" + } + }, + { + "Q": "Egy vállalat tavalyi 2019-es eredményét a mérlegen a saját tőke részeként a mérleg szerinti eredmény soron láthatjuk.\n", + "A": "Hamis", + "data": { + "type": "simple" + } + }, + { + "Q": "A készletezési periódus a szállítói számla kiegyenlítésétől a vevővel szembeni követelés beérkezéséig tart.", + "A": "Hamis", + "data": { + "type": "simple" + } + } + ] +} diff --git a/scripts/serverStats.js b/scripts/serverStats.js new file mode 100644 index 0000000..afeeed9 --- /dev/null +++ b/scripts/serverStats.js @@ -0,0 +1,397 @@ +// ----------------------------------------------------------------- +// CONFIG +// ----------------------------------------------------------------- + +const cols = process.stdout.columns +// const rows = process.stdout.rows +const maxStatLength = Math.floor(cols / 4) +const statNameSpacing = 5 +const beforeRowSpace = 13 +const coloredWords = { + red: ['lred', 'thanks'], + cyan: [ + 'getveteranpw', + 'pwrequest', + 'getpw', + 'availablepws', + 'login', + 'logout', + ], + green: [ + 'manual', + 'todos', + 'allquestions', + 'subjectbrowser', + 'contribute', + 'feedback', + 'ranklist', + 'allqr', + 'possibleAnswers', + ], + blue: ['isadding', 'ask'], + magenta: [ + 'donate', + 'tiszai', + 'install', + 'irc', + 'discord', + 'postfeedback', + 'votetodo', + 'registerscript', + 'quickvote', + ], +} +const filterFromDailyStats = [ + 'savedQuestions', + 'sio/f', + 'sound/', + '/img/', + '.php', + 'favicon', + 'robots.txt', + 'ads.txt', + '/f/', + '.git', +] + +// ----------------------------------------------------------------- + +const fs = require('fs') // eslint-disable-line + +const dir = process.argv[2] +if (!dir) { + console.log('No params') + process.exit() +} + +function getDayIndex(offset) { + if (!offset) { + offset = 0 + } + + const date = new Date() + if (offset) { + date.setDate(date.getDate() + offset) + } + return ( + date.getFullYear() + + '-' + + ('0' + (date.getMonth() + 1)).slice(-2) + + '-' + + ('0' + date.getDate()).slice(-2) + ) +} + +function hr(char) { + console.log(C('blue') + getLetterNTimes(char || '-', cols) + C()) +} + +function printHeader(text) { + hr() + console.log(C('green') + text + C()) + hr() +} + +function C(color) { + if (color !== undefined) { + color = color.toLowerCase() + } + + if (color === 'redbg') { + return '\x1b[41m' + } + if (color === 'bluebg') { + return '\x1b[44m' + } + if (color === 'green') { + return '\x1b[32m' + } + if (color === 'red') { + return '\x1b[31m' + } + if (color === 'yellow') { + return '\x1b[33m' + } + if (color === 'blue') { + return '\x1b[34m' + } + if (color === 'magenta') { + return '\x1b[35m' + } + if (color === 'cyan') { + return '\x1b[36m' + } + return '\x1b[0m' +} + +function readJSON(name) { + return JSON.parse(fs.readFileSync(name, 'utf8')) +} + +function tail(text, number) { + const splitedText = text.split('\n') + return splitedText.slice(Math.max(splitedText.length - number, 1)).join('\n') +} + +function head(text, number) { + return text + .split('\n') + .slice(0, number) + .join('\n') +} + +function countLinesMatching(text, toMatch) { + let count = 0 + text.split('\n').forEach((line) => { + if (line.includes(toMatch.toLowerCase())) { + count++ + } + }) + return count +} + +function readFile(name) { + return fs.readFileSync(name, 'utf8') +} + +function getLetterNTimes(letter, number) { + let res = '' + while (res.length < number) { + res += letter + } + return res +} + +function pCols(cols, rowTitles, colorNames, firstRowColor, showDiff) { + // console.log(cols) + let maxLength = 0 + Object.keys(cols).forEach((key) => { + if (cols[key].length > maxLength) { + maxLength = cols[key].length + } + }) + for (let i = 0; i < maxLength; i++) { + const row = [] + const lastItems = [] + Object.keys(cols) + .reverse() + .forEach((key) => { + const val = cols[key] + if (!val[i]) { + return + } + + lastItems.push(val[i]) + const keyName = val[i].name || val[i] + + let slicedName = keyName.slice(0, maxStatLength) + const toColor = colorNames + ? Object.keys(coloredWords).reduce((acc, key) => { + const colorArray = coloredWords[key] + + const includes = colorArray.some((colorableIdName) => { + return keyName + .toLowerCase() + .includes(colorableIdName.toLowerCase()) + }) + + if (includes) { + return key + } + + return acc + }, '') + : false + + const sep = (i + 1) % 5 === 0 ? '.' : ' ' + + while (slicedName.length < maxStatLength) { + slicedName = slicedName + sep + } + + let ammount = val[i].val ? val[i].val.toString() : '' + while (ammount.length < 4) { + ammount = ammount + ' ' + } + + if (toColor) { + row.push(C(toColor) + slicedName + ' ' + ammount + C()) + } else { + row.push(slicedName + ' ' + ammount) + } + }) + + // ROW TITLE --------------------------------------------------- + let currRowTitle = + rowTitles && rowTitles[i] + ? rowTitles[i] + : getLetterNTimes(' ', beforeRowSpace) + + while (currRowTitle.length < beforeRowSpace) { + currRowTitle = currRowTitle + ' ' + } + currRowTitle = C('blue') + currRowTitle + C() + // COLORING ---------------------------------------------------- + let res = '' + if (firstRowColor && i === 0) { + res = + currRowTitle + + C('green') + + row.join(getLetterNTimes(' ', statNameSpacing)) + + C() + } else { + res = currRowTitle + row.join(getLetterNTimes(' ', statNameSpacing)) + } + // SHOW DIFF --------------------------------------------------- + if (showDiff && i !== 0) { + let diff = + lastItems[lastItems.length - 1] - lastItems[lastItems.length - 2] + if (diff > 0) { + diff = '+' + diff.toString() + } + res += C('blue') + diff + C() + } + + console.log(res) + } +} + +// ------------------------------------------------------------------------------ +printHeader('Daily stats') + +const dailyStats = readJSON(`${dir}stats/vstats`) +function preProcessDailyStats(obj) { + const formatted = Object.keys(obj).reduce((acc, key) => { + const includes = filterFromDailyStats.some((keyword) => { + return key.toLowerCase().includes(keyword.toLowerCase()) + }) + if (!includes) { + acc.push({ name: key, val: obj[key] }) + } + return acc + }, []) + return formatted.sort((a, b) => { + if (a.name > b.name) { + return 1 + } else if (a.name < b.name) { + return -1 + } else { + return 0 + } + }) +} + +pCols( + [ + preProcessDailyStats(dailyStats[getDayIndex()]), + preProcessDailyStats(dailyStats[getDayIndex(-1)]), + preProcessDailyStats(dailyStats[getDayIndex(-2)]), + ], + null, + true +) + +// ------------------------------------------------------------------------------ +printHeader('User id test solving') +const userIdTestSolving = readJSON(`${dir}stats/idvstats`) +function preProcessUIdTestSolving(obj, minLength) { + if (!obj) { + return '0' + } + if (minLength) { + return Object.keys(obj) + .filter((key) => { + return obj[key] > minLength + }) + .length.toString() + } else { + return Object.keys(obj).length.toString() + } +} + +pCols( + [ + ['Today', preProcessUIdTestSolving(userIdTestSolving[getDayIndex()])], + ['Yesterday', preProcessUIdTestSolving(userIdTestSolving[getDayIndex(-1)])], + [ + 'Before yesterday', + preProcessUIdTestSolving(userIdTestSolving[getDayIndex(-2)]), + ], + ], + null, + false, + 'green' +) + +// ------------------------------------------------------------------------------ +printHeader('User id requests') +const clientIdTestSolving = readJSON(`${dir}stats/uvstats`) + +pCols( + [ + [ + 'Today', + preProcessUIdTestSolving(clientIdTestSolving[getDayIndex()]), + preProcessUIdTestSolving(clientIdTestSolving[getDayIndex()], 2), + ], + [ + 'Yesterday', + preProcessUIdTestSolving(clientIdTestSolving[getDayIndex(-1)]), + preProcessUIdTestSolving(clientIdTestSolving[getDayIndex(-1)], 2), + ], + [ + 'Before Yesterday', + preProcessUIdTestSolving(clientIdTestSolving[getDayIndex(-2)]), + preProcessUIdTestSolving(clientIdTestSolving[getDayIndex(-2)], 2), + ], + ], + ['', 'All', 'More than 2'], + false, + 'green' +) + +// ------------------------------------------------------------------------------ +printHeader('Daily data count') +const dailyDataCount = readFile(`${dir}stats/dailyDataCount`) + +printLastDataCount([ + JSON.parse(head(tail(dailyDataCount, 1), 1)), + JSON.parse(head(tail(dailyDataCount, 2), 1)), + JSON.parse(head(tail(dailyDataCount, 3), 1)), +]) + +function printLastDataCount(data) { + const res = [['Today'], ['Yesterday'], ['Before yesterday']] + data.forEach((dataCount, i) => { + res[i].push(dataCount.userCount.toString()) + res[i].push(dataCount.subjectCount.toString()) + res[i].push(dataCount.questionCount.toString()) + }) + + pCols(res, ['', 'Users', 'Subjects', 'Questions'], false, 'green', true) +} +// ------------------------------------------------------------------------------ +printHeader('Daily script install / update check count') +const todaysLogs = readFile(`${dir}stats/vlogs/${getDayIndex()}`) +const yesterdaysLogs = readFile(`${dir}stats/vlogs/${getDayIndex(-1)}`) +const beforeYesterdaysLogs = readFile(`${dir}stats/vlogs/${getDayIndex(-2)}`) + +const installs = [ + [ + 'Today', + countLinesMatching(todaysLogs, '?install').toString(), + countLinesMatching(todaysLogs, '?up').toString(), + ], + [ + 'Yesterday', + countLinesMatching(yesterdaysLogs, '?install').toString(), + countLinesMatching(yesterdaysLogs, '?up').toString(), + ], + [ + 'Before yesterday', + countLinesMatching(beforeYesterdaysLogs, '?install').toString(), + countLinesMatching(beforeYesterdaysLogs, '?up').toString(), + ], +] + +pCols(installs, ['', 'Installs', 'Updates'], false, 'green') diff --git a/src/modules/qmining/qmining.ts b/src/modules/qmining/qmining.ts index 6919e8b..7f86234 100644 --- a/src/modules/qmining/qmining.ts +++ b/src/modules/qmining/qmining.ts @@ -98,10 +98,14 @@ function GetApp(): ModuleType { loadDonateURL() - utils.WatchFile(linksFile, (newData) => { - logger.Log(`Donate URL changed: ${newData.replace(/\/n/g, '')}`) - loadDonateURL() - }) + if (utils.FileExists(linksFile)) { + utils.WatchFile(linksFile, (newData) => { + logger.Log(`Donate URL changed: ${newData.replace(/\/n/g, '')}`) + loadDonateURL() + }) + } else { + logger.Log('Couldnt read donate URL file!', logger.GetColor('red')) + } // -------------------------------------------------------------- // REDIRECTS @@ -187,8 +191,6 @@ function GetApp(): ModuleType { }, ] - console.log(links, simpleRedirects) - simpleRedirects.forEach((redirect) => { app.get(redirect.from, function(req: Request, res) { if (!redirect.nolog) { diff --git a/src/server.ts b/src/server.ts index 6663bf3..2e6d758 100755 --- a/src/server.ts +++ b/src/server.ts @@ -40,7 +40,7 @@ import utils from './utils/utils' import dbtools from './utils/dbtools' import reqlogger from './middlewares/reqlogger.middleware' import idStats from './utils/ids' -const extraModulesFile = './data/extraModules.json' +const extraModulesFile = './extraModules/extraModules.json' const statExcludeFile = './data/statExclude.json' const modulesFile = './src/modules.json' const usersDBPath = './data/dbs/users.db'