mrfrys-node-server/src/utils/dbtools.ts
2022-03-22 18:49:39 +01:00

276 lines
6.4 KiB
TypeScript

// 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)
}