// https://www.sqlitetutorial.net/sqlite-nodejs/ // https://github.com/JoshuaWise/better-sqlite3/blob/HEAD/docs/api.md export default { GetDB: GetDB, AddColumn: AddColumn, TableInfo: TableInfo, Update: Update, Delete: Delete, CreateTable: CreateTable, SelectAll: SelectAll, Select: Select, Insert: Insert, CloseDB: CloseDB, runStatement: runStatement, sanitizeQuery: sanitizeQuery, } import Sqlite from 'better-sqlite3' import logger from '../utils/logger' import utils from '../utils/utils' const debugLog = process.env.NS_SQL_DEBUG_LOG function sanitizeQuery(val) { if (typeof val === 'string') { return val.replace(/'/g, '').replace(/;/g, '') } return val } // { asd: 'asd', basd: 4 } => asd = 'asd', basd = 4 function GetSqlQuerry(conditions: any, type: string, joiner?: string) { const res = Object.keys(conditions).reduce((acc, key) => { const item = conditions[key] const conditionKey = sanitizeQuery(key) const condition = sanitizeQuery(conditions[key]) if (typeof item === 'string') { acc.push(`${conditionKey} = '${condition}'`) } else { acc.push(`${conditionKey} = ${condition}`) } return acc }, []) if (type === 'where') { if (joiner) { return res.join(` ${joiner} `) } else { return res.join(' AND ') } } else { return res.join(', ') } } // ------------------------------------------------------------------------- function GetDB(path: string): any { utils.CreatePath(path) const res = new Sqlite(path) res.pragma('synchronous = OFF') return res } function DebugLog(msg) { if (debugLog) { logger.DebugLog(msg, 'sql', 0) } } function AddColumn(db: any, table: any, col: any): any { try { const colName = Object.keys(col)[0] const colType = col.type const command = `ALTER TABLE ${table} ADD COLUMN ${colName} ${colType}` const stmt = PrepareStatement(db, command) return stmt.run() } catch (err) { console.error(err) } } function TableInfo(db: any, table: any): any { try { const command = `PRAGMA table_info(${table})` const stmt = PrepareStatement(db, command) const infoRes = stmt.all() const s2 = `SELECT COUNT(*) FROM ${table}` const stmt2 = PrepareStatement(db, s2) const countRes = stmt2.get() return { columns: infoRes, dataCount: countRes[Object.keys(countRes)[0]], } } catch (err) { console.error(err) } } function Update(db: any, table: any, newData: any, conditions: any): any { try { const command = `UPDATE ${table} SET ${GetSqlQuerry( newData, 'set' )} WHERE ${GetSqlQuerry(conditions, 'where')}` const stmt = PrepareStatement(db, command) return stmt.run() } catch (err) { console.error(err) } } function Delete(db: any, table: any, conditions: any): any { try { const command = `DELETE FROM ${table} WHERE ${GetSqlQuerry( conditions, 'where' )}` const stmt = PrepareStatement(db, command) return stmt.run() } catch (err) { console.error(err) } } function CreateTable(db: any, name: any, columns: any, foreignKeys: any): any { // CREATE TABLE users(pw text PRIMARY KEY NOT NULL, id number, lastIP text, notes text, loginCount // number, lastLogin text, lastAccess text // // FOREIGN KEY(songartist, songalbum) REFERENCES album(albumartist, albumname) ) try { const cols = Object.keys(columns) .reduce((acc, key) => { const item = columns[key] const flags = [] const toCheck = { primary: 'PRIMARY KEY', notNull: 'NOT NULL', unique: 'UNIQUE', autoIncrement: 'AUTOINCREMENT', defaultZero: 'DEFAULT 0', } Object.keys(toCheck).forEach((key) => { if (item[key]) { flags.push(toCheck[key]) } }) acc.push(`${key} ${item.type} ${flags.join(' ')}`) return acc }, []) .join(', ') const fKeys = [] if (foreignKeys) { foreignKeys.forEach((foreignKey) => { const { keysFrom, table, keysTo } = foreignKey fKeys.push( `, FOREIGN KEY(${keysFrom.join( ', ' )}) REFERENCES ${table}(${keysTo.join(', ')})` ) }) } // IF NOT EXISTS const command = `CREATE TABLE ${name}(${cols}${fKeys.join(' ')})` const stmt = PrepareStatement(db, command) return stmt.run() } catch (err) { console.error(err) } } function SelectAll(db: any, from: any): any { try { const command = `SELECT * from ${from}` const stmt = PrepareStatement(db, command) return stmt.all() } catch (err) { console.error(err) } } // SELECT * FROM MyTable WHERE SomeColumn > LastValue ORDER BY SomeColumn LIMIT 100; function Select(db: any, from: any, conditions: any, options: any = {}): any { const { joiner, limit } = options try { let command = `SELECT * from ${from} WHERE ${GetSqlQuerry( conditions, 'where', joiner )}` if (!isNaN(limit)) { command += ` LIMIT ${limit}` } const stmt = PrepareStatement(db, command) return stmt.all() } catch (err) { console.error(err) } } function Insert(db: any, table: any, data: any): any { 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 command = `INSERT INTO ${table} (${cols}) VALUES (${values})` const stmt = PrepareStatement(db, command) return stmt.run() } catch (err) { console.error(err) } } function runStatement(db: any, command: string, runType?: string): any { const stmt = PrepareStatement(db, command) if (!runType) { return stmt.all() } else if (runType === 'run') { return stmt.run() } } function CloseDB(db: any): void { db.close((err) => { if (err) { return console.error(err.message) } DebugLog('Close the database connection.') }) } // ------------------------------------------------------------------------- function PrepareStatement(db, command) { if (!db) { throw new Error( 'DB is undefined in prepare statement! DB action called with undefined db' ) } DebugLog(command) return db.prepare(command) }