diff --git a/devel/tests/testData/ElektroNewSinceFirstResJSON.json b/devel/tests/testData/ElektroNewSinceFirstResJSON.json new file mode 100644 index 0000000..6a227c4 --- /dev/null +++ b/devel/tests/testData/ElektroNewSinceFirstResJSON.json @@ -0,0 +1,69 @@ +{ + "subj": "2019/20/2 - Elektronika - AMEEL0IBNE/L1", + "version": "2.0.0.4", + "id": "1585155641104", + "quiz": [ + { + "Q": "A multiméterek váltakozó jellemzők mérése esetén mindig csúcsértéket jeleznek ki.\n", + "A": "Hamis", + "data": { + "type": "simple" + } + }, + { + "Q": "A multiméterek váltakozó jellemzők mérése esetén mindig effektív értéket jeleznek ki.\n", + "A": "Igaz", + "data": { + "type": "simple" + } + }, + { + "Q": "Az analóg multiméter kijelzője lágyvasas műszer.\n", + "A": "Hamis", + "data": { + "type": "simple" + } + }, + { + "Q": "Az analóg multiméter kijelzője Deprez műszer.\n", + "A": "Igaz", + "data": { + "type": "simple" + } + }, + { + "Q": "Az állandó mágnesű Deprez műszer egyenirányítóval kiegészítve (egyenirányítós állandó mágnesű műszer) kizárólag egyenáram (illetve egyenfeszültség) mérésére alkalmas.", + "A": "Hamis", + "data": { + "type": "image", + "images": [ + "6emZyTu7t7Y2PjPyatVf0h9RWybD15" + ] + } + }, + { + "Q": "Az állandó mágnesű Deprez alapműszer alkalmas mind egyen, mind váltakozó áram (illetve feszültség) mérésére is.", + "A": "Hamis", + "data": { + "type": "image", + "images": [ + "jLtbYDdNNoQAAAAASUVORK5CYIIA" + ] + } + }, + { + "Q": "A korszerűbb digitális multiméterek általában Tru RMS konvertereket (egyenirányítókat) tartalmaznak, így egyszerű középértéket mérnek és effektív értéket is jeleznek ki.\n", + "A": "Hamis", + "data": { + "type": "simple" + } + }, + { + "Q": "A korszerűbb digitális multiméterek általában Tru RMS konvertereket (egyenirányítókat) tartalmaznak, így abszolút középértéket mérnek és effektív értéket is jeleznek ki.\n", + "A": "Hamis", + "data": { + "type": "simple" + } + } + ] +} diff --git a/devel/tests/testData/NewSinceFirstResJSON2.json b/devel/tests/testData/NewSinceFirstResJSON2.json new file mode 100644 index 0000000..1b3da23 --- /dev/null +++ b/devel/tests/testData/NewSinceFirstResJSON2.json @@ -0,0 +1,49 @@ +{ + "subj": "2019/20/2 - Elektronika - NIEEL0HBNE/EL0_LA_07-H:13:30-15:10", + "version": "2.0.0.4", + "id": "1585554409104", + "quiz": [ + { + "Q": "Az egyenirányító kapcsolásokban a kimeneti hullámosság hogyan csökkenthető, az alábbi lehetőségek közül?", + "A": "a. A pufferkondenzátor értékének növelésével.", + "data": { + "type": "simple" + } + }, + { + "Q": "ELAB paneles mérés során hogyan lehet a dióda nyitó irányú előfeszítését záró irányú előfeszítésre változtatni?", + "A": "a. A tápegység csatlakozóit meg kell cserélni, így negatív feszültség kerül a kapcsolásra.", + "data": { + "type": "simple" + } + }, + { + "Q": "Feszültségmérő műszert hogyan kapcsolunk egy mérőkörre?", + "A": "c. Csak párhuzamosan kapcsolhatjuk egy vagy több elemmel.", + "data": { + "type": "simple" + } + }, + { + "Q": "Milyen szerepet tölt be az egyenirányító kapcsolásokban a kapacitás?", + "A": "A helyes válasz: Amikor a bemeneti feszültség ÉRTÉKE KISEBB mint a kimeneti feszültség, a dióda lezár az egyenirányító kapcsolásban. Ezalatt az idő alatt az egyenirányító kimenetén folyó áramot a kondenzátorban tárolt energia biztosítja. .", + "data": { + "type": "simple" + } + }, + { + "Q": "Mit jelent a dióda záróirányú feléledési ideje?", + "A": "a. Egy dióda nyitóirányú előfeszítésből, ha hirtelen záróirányú előfeszítésbe kerül, a záróirányú előfeszítésbe kapcsoláskor egy rövid ideig (nanosec. nagyságrend) folyik rajta egy záróirányú áram, majd ez az áram 0 értékűre csökken.", + "data": { + "type": "simple" + } + }, + { + "Q": "Egy TTL inverter esetén a logikai igaz értékhez 2V-5V, illetve 2,7V-5V feszültségtartomány van megadva. Mit jelent ez? ", + "A": "c. Logikai igaz értéknek 2V-5V között a bemeneti, 2,7V-5V között a kimeneti feszültségtartományt értjük.", + "data": { + "type": "simple" + } + } + ] +} diff --git a/devel/tests/testData/NewSinceFirstResJSON3.json b/devel/tests/testData/NewSinceFirstResJSON3.json new file mode 100644 index 0000000..0361557 --- /dev/null +++ b/devel/tests/testData/NewSinceFirstResJSON3.json @@ -0,0 +1,35 @@ +{ + "subj": "2019/20/2 - Újrakonfigurálható digitális áramkörök", + "version": "2.0.0.4", + "id": "1575851111714", + "quiz": [ + { + "Q": "A tömbök optimalizálására alkalmazható kényszerfeltételek:\n\n\n", + "A": "A helyes válaszok: ARRAY_MAP, ARRAY_PARTITION, ARRAY_RESHAPE", + "data": { + "type": "simple" + } + }, + { + "Q": "Az alábbi felsorolásból válassza ki a modul szintű protokollokat:\n\n\n", + "A": "A helyes válaszok: al_ctrl_hs, ap_ctrl_none, ap_ctrl_chain", + "data": { + "type": "simple" + } + }, + { + "Q": "Ciklusok optimalizálásaira alkalmazható kényszerfeltételek:\n", + "A": "A helyes válaszok: UNROLL, LOOP_FLATTEN, LOOP_MERGE,\nPIPELINE", + "data": { + "type": "simple" + } + }, + { + "Q": "Egy tömb típusú függvényargumentumhoz automatikusan a következő típusú protokoll rendelődik:", + "A": "c. ap_bram", + "data": { + "type": "simple" + } + } + ] +} diff --git a/devel/tests/testData/digit.json b/devel/tests/testData/digit.json new file mode 100644 index 0000000..43df651 --- /dev/null +++ b/devel/tests/testData/digit.json @@ -0,0 +1,50 @@ +{ + "subj": "2019/20/2 - Digitális technika II.", + "version": "2.0.0.4", + "id": "1585143211102", + "quiz": [ + { + "Q": "Melyik tároló működési tábláját látja? Válassza ki a helyes megoldást!", + "A": "A helyes válasz: RS.", + "data": { + "type": "image", + "images": [ + "NeQ5NhVhEjEEovqtIc xqQiTiCEQ1W" + ] + } + }, + { + "Q": "Melyik flip-flop állapotgráfja az alábbi?\n\n", + "A": "A helyes válasz: JKnegált.", + "data": { + "type": "image", + "images": [ + "0C4Xu0TgekRTx2ECAiXL0RgesTzAyE" + ] + } + }, + { + "Q": "Készítsen T flip-flopból JK flip-flopot! Válassza ki a kapcsolási rajzot!\n\n\n\n", + "A": "", + "data": { + "type": "image", + "images": [ + "ZYJDqZQKATWDvRgGAuIBRACAmYBQAi", + "e1a9eWlpZigW9b8K1I1dik QiC1BI0", + "EAO6UFkQPWwAAAABJRU5ErkJggg==", + "wdy3q M gpKygAAAABJRU5ErkJggg=", + "t5yLDOdM4AAAAASUVORK5CYII=", + "1 0IdIQbygbYUDbAhrIBNpQNsKFsgA", + "8FEOmCBoLJ8AAAAASUVORK5CYII=" + ] + } + }, + { + "Q": "A flip-flopok aszinkron vezérlő bemenetei erősebbek a szinkron vezérlő bemeneteknél.\n", + "A": "Igaz", + "data": { + "type": "simple" + } + } + ] +} diff --git a/devel/tests/testData/newDataSinceFirstResJSON.json b/devel/tests/testData/newDataSinceFirstResJSON.json new file mode 100644 index 0000000..8384e2e --- /dev/null +++ b/devel/tests/testData/newDataSinceFirstResJSON.json @@ -0,0 +1,147 @@ +{ + "subj": "2019/20/2 - IT Networks - BMXIHE6BNE/NMEI_IHE6BNE_01GY", + "version": "2.0.0.4", + "id": "1572281342041", + "quiz": [ + { + "Q": "How many layers in OSI model?\n\n\nThere are...\n", + "A": "A helyes válasz: 7 layers.", + "data": { + "type": "simple" + } + }, + { + "Q": "What does FTP stand for?\n", + "A": "A helyes válasz: File Transfer Protocol.", + "data": { + "type": "simple" + } + }, + { + "Q": "What is an Ip address?\n\n\nIt is ...\n", + "A": "A helyes válasz: All of above.", + "data": { + "type": "simple" + } + }, + { + "Q": "Which are types of a network by Geography distance?\n", + "A": "A helyes válasz: LAN, MAN, WAN, Internet.", + "data": { + "type": "simple" + } + }, + { + "Q": "What is a repeater function?\n", + "A": "A helyes válasz: Amplifier, Expand the network, and operating in Physical layer.", + "data": { + "type": "simple" + } + }, + { + "Q": "What is the unit of Network layer?\n", + "A": "A helyes válasz: Packets.", + "data": { + "type": "simple" + } + }, + { + "Q": "What is the right order of a network size by geography distance?\n", + "A": "A helyes válasz: LAN → 1, Internet → 4, WAN → 3, MAN → 2.", + "data": { + "type": "simple" + } + }, + { + "Q": "What is the right order of OSI model?\n", + "A": "A helyes válasz: Data link → Layer 2, Presentation → Layer 6, Transportation → Layer 4, Network → Layer 3, Physical → Layer 1, Session → Layer 5, Application → Layer 7.", + "data": { + "type": "simple" + } + }, + { + "Q": "What does the Ipv4 address include?\n\n\nIpv4 address includes...\n", + "A": "A helyes válasz: Network ID and Host ID.", + "data": { + "type": "simple" + } + }, + { + "Q": "What does DHCP stand for?\n", + "A": "A helyes válasz: Dynamic Host Configuration Protocol.", + "data": { + "type": "simple" + } + }, + { + "Q": "What is the order of Standard A?\n", + "A": "A helyes válasz: White Orange → 1, Orange → 2, White green → 3, White blue → 5, Green → 6, White Brown → 7, Brown → 8, Blue → 4.", + "data": { + "type": "simple" + } + }, + { + "Q": "What is the maximum speed of STP cable?\n", + "A": "A helyes válasz: 155 Mbps.", + "data": { + "type": "simple" + } + }, + { + "Q": "How many kinds of media transmission?\n", + "A": "A helyes válasz: 2.", + "data": { + "type": "simple" + } + }, + { + "Q": "What is the maximum length of thin coaxial cable?\n", + "A": "A helyes válasz: 185 m.", + "data": { + "type": "simple" + } + }, + { + "Q": "What is the maximum length of thick coaxial cable?\n", + "A": "A helyes válasz: 500 m.", + "data": { + "type": "simple" + } + }, + { + "Q": "What is the maximum length of UTP cable?\n", + "A": "A helyes válasz: 100 m.", + "data": { + "type": "simple" + } + }, + { + "Q": "Which is the right answer to these sentences about Switch?\n", + "A": "A helyes válasz: Combination between Bridge and Hub.", + "data": { + "type": "simple" + } + }, + { + "Q": "What is modem function?\n", + "A": "A helyes válasz: Convert digital signal to analog signal and vice versa.", + "data": { + "type": "simple" + } + }, + { + "Q": "List the order of designing a network?\n", + "A": "A helyes válasz: Network testing → Step 5, Requirement analysis → Step 2, Gathering requirements from clients → Step 1, Design solutions → Step 3, System maintenance → Step 6, Network settings → Step 4.", + "data": { + "type": "simple" + } + }, + { + "Q": "When will we use straight-through cable?\n", + "A": "A helyes válasz: Hub - Switch.", + "data": { + "type": "simple" + } + } + ] +} diff --git a/modules/api/api.js b/modules/api/api.js index 4e4abb6..943600b 100644 --- a/modules/api/api.js +++ b/modules/api/api.js @@ -29,6 +29,8 @@ const app = express() const logger = require('../../utils/logger.js') const utils = require('../../utils/utils.js') const actions = require('../../utils/actions.js') +const dbtools = require('../../utils/dbtools.js') +const auth = require('../../modules/api/auth.middleware.js') const recivedFiles = 'public/recivedfiles' const uloadFiles = 'public/f' @@ -39,12 +41,31 @@ const versionFile = 'public/version' const passwordFile = 'data/dataEditorPasswords.json' const dataEditsLog = 'stats/dataEdits' const dailyDataCountFile = 'stats/dailyDataCount' +const usersDBPath = 'data/dbs/users.db' +const dbStructPath = './modules/api/apiDBStruct.json' + +let authDB +function CreateDB () { + const dbStruct = utils.ReadJSON(dbStructPath) + // TODO: check if path exists, create it if not + authDB = dbtools.GetDB(usersDBPath) + + Object.keys(dbStruct).forEach((tableName) => { + const tableData = dbStruct[tableName] + dbtools.CreateTable(authDB, tableName, tableData.tableStruct) + }) +} +CreateDB() app.set('view engine', 'ejs') app.set('views', [ './modules/api/views', './sharedViews' ]) +app.use(auth({ + debugLog: true, + authDB: authDB +})) app.use(express.static('public')) app.use(busboy({ limits: { diff --git a/modules/api/apiDBStruct.json b/modules/api/apiDBStruct.json new file mode 100644 index 0000000..85b646d --- /dev/null +++ b/modules/api/apiDBStruct.json @@ -0,0 +1,35 @@ +{ + "users": { + "tableStruct": { + "userID": { + "type": "number", + "primary": true, + "notNull": true + }, + "pw": { + "type": "text" + }, + "lastIP": { + "type": "text" + }, + "notes": { + "type": "text" + }, + "loginCount": { + "type": "number" + } + } + }, + "acesses": { + "tableStruct": { + "userID": { + "type": "number", + "primary": true, + "notNull": true + }, + "ip": { + "type": "text" + } + } + } +} diff --git a/modules/api/auth.middleware.js b/modules/api/auth.middleware.js new file mode 100644 index 0000000..1f5a681 --- /dev/null +++ b/modules/api/auth.middleware.js @@ -0,0 +1,16 @@ +const logger = require('../../utils/logger.js') +const dbtools = require('../../utils/dbtools.js') + +module.exports = function (options) { + const { debugLog, authDB } = options + + return function (req, res, next) { + if (debugLog) { + logger.Log('AUTH: ' + req.url) + } + + res.end('NO ACCESS') + + // next() + } +} diff --git a/modules/qmining/qmining-page b/modules/qmining/qmining-page index 35263fc..86b01f4 160000 --- a/modules/qmining/qmining-page +++ b/modules/qmining/qmining-page @@ -1 +1 @@ -Subproject commit 35263fccad68f2b8a564532658a5489c92674675 +Subproject commit 86b01f443a306695a9a17f29785ba20b7f08f810 diff --git a/package.json b/package.json index 389f1c4..cc93c82 100755 --- a/package.json +++ b/package.json @@ -2,15 +2,18 @@ "name": "node-ejs", "main": "server.js", "dependencies": { + "better-sqlite3": "^6.0.1", "connect-busboy": "0.0.2", "cookie-parser": "^1.4.5", "cors": "^2.8.5", "ejs": "^1.0.0", "express": "^4.6.1", "express-ejs-layouts": "^1.1.0", + "sqlite3": "^4.1.1", "vhost": "^3.0.2" }, "scripts": { - "start": "node ./server.js" + "start": "node ./server.js", + "dev": "node ./server.js" } } diff --git a/server.js b/server.js index 2589204..aaa30c1 100755 --- a/server.js +++ b/server.js @@ -38,16 +38,7 @@ const loggableKeywords = [ ] let modules = JSON.parse(utils.ReadFile(modulesFile)) -let logLevel = parseInt(GetParams()[0]) -if (isNaN(logLevel)) { - logLevel = 0 -} -logger.Log('Loglevel is: ' + logLevel) -logger.Load(logLevel) // TODO: debug level in enviromet variable / param - -function GetParams () { - return process.argv.splice(2) -} +logger.Load() try { if (utils.FileExists(extraModulesFile)) { @@ -169,6 +160,7 @@ function setLogTimer () { setLogTimer() logger.Log('Node version: ' + process.version) +logger.Log('Current working directory: ' + process.cwd()) logger.Log('Listening on port: ' + port) const httpServer = http.createServer(app) diff --git a/utils/dbtools.js b/utils/dbtools.js new file mode 100644 index 0000000..f1eed3d --- /dev/null +++ b/utils/dbtools.js @@ -0,0 +1,189 @@ +// https://www.sqlitetutorial.net/sqlite-nodejs/ +// https://github.com/JoshuaWise/better-sqlite3/blob/HEAD/docs/api.md + +module.exports = { + GetDB, + AddColumn, + TableInfo, + UpdateRecord, + Delete, + CreateTable, + SelectAll, + Select, + Insert, + CloseDB +} + +const Sqlite = require('better-sqlite3') +const logger = require('../utils/logger.js') + +const debugLog = process.env.NS_SQL_DEBUG_LOG + +// { asd: 'asd', basd: 4 } => asd = 'asd', basd = 4 +function GetSqlQuerry (conditions) { + return Object.keys(conditions).reduce((acc, key) => { + const item = conditions[key] + if (typeof item === 'string') { + acc.push(`${key} = '${conditions[key]}'`) + } else { + acc.push(`${key} = ${conditions[key]}`) + } + return acc + }, []).join(', ') +} + +// ------------------------------------------------------------------------- + +function GetDB (path) { + return new Sqlite(path) +} + +function DebugLog (msg) { + if (debugLog) { + logger.DebugLog(msg, 'sql', 0) + } +} + +function AddColumn (db, table, col) { + try { + const colName = Object.keys(col)[0] + const colType = col.type + + const s = `ALTER TABLE ${table} ADD COLUMN ${colName} ${colType}` + DebugLog(s) + const stmt = db.prepare(s) + + return stmt.run() + } catch (e) { + console.error(e) + } +} + +function TableInfo (db, table) { + try { + + } catch (e) { + console.error(e) + } + const s = `PRAGMA table_info(${table})` + DebugLog(s) + const stmt = db.prepare(s) + + const infoRes = stmt.all() + + const s2 = `SELECT COUNT(*) FROM ${table}` + DebugLog(s2) + const stmt2 = db.prepare(s2) + + const countRes = stmt2.get() + + return { + columns: infoRes, + dataCount: countRes[Object.keys(countRes)[0]] + } +} + +function UpdateRecord (db, table, newData, conditions) { + try { + const s = `UPDATE ${table} SET ${GetSqlQuerry(newData)} WHERE ${GetSqlQuerry(conditions)}` + DebugLog(s) + const stmt = db.prepare(s) + + return stmt.run() + } catch (e) { + console.error(e) + } +} + +function Delete (db, table, conditions) { + try { + const s = `DELETE FROM ${table} WHERE ${GetSqlQuerry(conditions)}` + DebugLog(s) + const stmt = db.prepare(s) + + return stmt.run() + } catch (e) { + console.error(e) + } +} + +function CreateTable (db, name, columns) { + try { + const cols = Object.keys(columns).reduce((acc, key) => { + const item = columns[key] + const isPrimary = item.primary ? ' PRIMARY KEY' : '' + const isNotNull = item.notNull ? ' NOT NULL' : '' + + acc.push(`${key} ${item.type}${isPrimary}${isNotNull}`) + return acc + }, []).join(', ') + + const s = `CREATE TABLE IF NOT EXISTS ${name}(${cols})` + DebugLog(s) + + const stmt = db.prepare(s) + return stmt.run() + } catch (e) { + console.error(e) + } +} + +function SelectAll (db, from) { + try { + const s = `SELECT * from ${from}` + DebugLog(s) + + const stmt = db.prepare(s) + return stmt.all() + } catch (e) { + console.error(e) + } +} + +function Select (db, from, conditions) { + try { + const s = `SELECT * from ${from} WHERE ${GetSqlQuerry(conditions)}` + DebugLog(s) + + const stmt = db.prepare(s) + return stmt.all() + } catch (e) { + console.error(e) + } +} + +function Insert (db, table, data) { + try { + const cols = Object.keys(data).reduce((acc, key) => { + acc.push(`${key}`) + return acc + }, []).join(', ') + + const values = Object.keys(data).reduce((acc, key) => { + const item = data[key] + if (typeof item === 'string') { + acc.push(`'${item}'`) + } else { + acc.push(`${item}`) + } + return acc + }, []).join(', ') + + const s = `INSERT INTO ${table} (${cols}) VALUES (${values})` + DebugLog(s) + const stmt = db.prepare(s) + + return stmt.run() + } catch (e) { + console.error(e) + } +} + +function CloseDB (db) { + db.close((err) => { + if (err) { + return console.error(err.message) + } + DebugLog('Close the database connection.') + }) +} diff --git a/utils/logger.js b/utils/logger.js index 0299a6c..58f8913 100755 --- a/utils/logger.js +++ b/utils/logger.js @@ -43,13 +43,6 @@ const statFile = 'stats/stats' const vStatFile = 'stats/vstats' const nologFile = './nolog' -const writeInterval = 10 - -let data = {} -let vData = {} -let writes = 0 -let debugLevel = 0 - const colors = [ 'green', 'red', @@ -59,6 +52,14 @@ const colors = [ 'cyan' ] +const writeInterval = 10 +const debugLevel = parseInt(process.env.NS_LOGLEVEL) || 0 +Log('Loglevel is: ' + debugLevel) + +let data = {} +let vData = {} +let writes = 0 + let noLogips = [] function GetDateString () { @@ -158,10 +159,7 @@ function setNoLogReadInterval () { parseNoLogFile(utils.ReadFile(nologFile)) } -function Load (debugLvl) { - if (debugLvl) { - debugLevel = debugLvl - } +function Load () { Log('Loading logger...') try { var prevData = utils.ReadFile(statFile) diff --git a/utils/utils.js b/utils/utils.js index 31cf9c7..d86c7ff 100755 --- a/utils/utils.js +++ b/utils/utils.js @@ -1,5 +1,6 @@ module.exports = { ReadFile: ReadFile, + ReadJSON: ReadJSON, WriteFile: WriteFile, writeFileAsync: WriteFileAsync, AppendToFile: AppendToFile, @@ -27,6 +28,15 @@ function ReadDir (path) { return fs.readdirSync(path) } +function ReadJSON (name) { + try { + return JSON.parse(ReadFile(name)) + } catch (e) { + console.log(e) + throw new Error('Coulndt parse JSON in "ReadJSON", file: ' + name) + } +} + function ReadFile (name) { if (!FileExists(name)) { throw new Error('No such file: ' + name) } return fs.readFileSync(name, 'utf8')