|
|
@@ -0,0 +1,131 @@
|
|
|
+import fs from 'fs';
|
|
|
+import fsp from 'fs/promises';
|
|
|
+import path from 'path';
|
|
|
+import { Database as SQLiteDB, OPEN_CREATE, OPEN_READWRITE, RunResult } from 'sqlite3';
|
|
|
+
|
|
|
+import { Logger } from '../../common/util/logger.class';
|
|
|
+
|
|
|
+export class Database {
|
|
|
+ private db: SQLiteDB;
|
|
|
+
|
|
|
+ public async open() {
|
|
|
+ try {
|
|
|
+ const DATA_DIR = process.env.DATA_DIR || 'data';
|
|
|
+
|
|
|
+ if (!fs.existsSync(DATA_DIR)) await fsp.mkdir(DATA_DIR);
|
|
|
+
|
|
|
+ const DATA_FILE = path.resolve(DATA_DIR, 'data.db');
|
|
|
+ const exists = fs.existsSync(DATA_FILE);
|
|
|
+
|
|
|
+ await new Promise<void>((res, rej) => {
|
|
|
+ this.db = new SQLiteDB(DATA_FILE, OPEN_READWRITE | OPEN_CREATE, err => (err ? rej(err) : res()));
|
|
|
+ });
|
|
|
+ Logger.info('[INFO]', exists ? 'Opened' : 'Created', 'SQLite3 Database file', DATA_FILE);
|
|
|
+
|
|
|
+ if (!exists) {
|
|
|
+ // INITIAL TABLE SETUP
|
|
|
+ await this.run(
|
|
|
+ `CREATE TABLE Server (
|
|
|
+ ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
+ Title TEXT NOT NULL UNIQUE,
|
|
|
+ FQDN TEXT NOT NULL UNIQUE
|
|
|
+ );`,
|
|
|
+ []
|
|
|
+ );
|
|
|
+
|
|
|
+ await this.run(
|
|
|
+ `CREATE TABLE ServerConfig (
|
|
|
+ ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
+ ServerID INTEGER NOT NULL,
|
|
|
+ Key TEXT NOT NULL,
|
|
|
+ Value TEXT NOT NULL,
|
|
|
+ FOREIGN KEY(ServerID) REFERENCES Server(ID),
|
|
|
+ UNIQUE(ServerID, Key)
|
|
|
+ )`,
|
|
|
+ []
|
|
|
+ );
|
|
|
+
|
|
|
+ await this.run(
|
|
|
+ `CREATE TABLE ServerDataEntry (
|
|
|
+ ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
+ ServerID INTEGER NOT NULL,
|
|
|
+ Timestamp INTEGER NOT NULL,
|
|
|
+ FOREIGN KEY(ServerID) REFERENCES Server(ID),
|
|
|
+ UNIQUE(ServerID, Timestamp)
|
|
|
+ );`,
|
|
|
+ []
|
|
|
+ );
|
|
|
+
|
|
|
+ await this.run(
|
|
|
+ `CREATE TABLE ServerDataValue (
|
|
|
+ ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
+ EntryID INTEGER NOT NULL,
|
|
|
+ Key TEXT NOT NULL,
|
|
|
+ Value REAL NOT NULL,
|
|
|
+ FOREIGN KEY(EntryID) REFERENCES ServerDataEntry(ID),
|
|
|
+ UNIQUE(EntryID, Key)
|
|
|
+ );`,
|
|
|
+ []
|
|
|
+ );
|
|
|
+
|
|
|
+ let result = await this.run(`INSERT INTO Server(Title, FQDN) VALUES(?, ?);`, ['Raspi4', '10.8.0.10']);
|
|
|
+ const serverID = result.lastID;
|
|
|
+ Logger.debug(`[DEBUG] Created Server #${serverID}`);
|
|
|
+ result = await this.run(`INSERT INTO ServerConfig(ServerID, Key, Value) VALUES(?, ?, ?);`, [serverID, 'syncInterval', 300]);
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ Logger.error('[FATAL] Initializing Database failed:', err);
|
|
|
+ Logger.error('[EXITING]');
|
|
|
+ process.exit(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public async getAllServerConfigs(): Promise<Server[]> {
|
|
|
+ const res = await this.stmt(
|
|
|
+ `SELECT
|
|
|
+ Server.*,
|
|
|
+ ServerConfig.Key,
|
|
|
+ ServerConfig.Value
|
|
|
+ FROM Server
|
|
|
+ LEFT OUTER JOIN ServerConfig ON Server.ID = ServerConfig.ServerID
|
|
|
+ ORDER BY Server.Title, ServerConfig.Key`,
|
|
|
+ []
|
|
|
+ );
|
|
|
+
|
|
|
+ return res.rows.reduce((res: Server[], line, i) => {
|
|
|
+ const serverID = line['ID'];
|
|
|
+ let server: Server;
|
|
|
+ if (i === 0 || res[res.length - 1].id !== serverID) {
|
|
|
+ server = { id: serverID, title: line['Title'], fqdn: line['FQDN'], config: {} };
|
|
|
+ res.push(server);
|
|
|
+ } else {
|
|
|
+ server = res[res.length - 1];
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!!line['Key']) {
|
|
|
+ server.config[line['Key']] = line['Value'];
|
|
|
+ }
|
|
|
+
|
|
|
+ return res;
|
|
|
+ }, [] as Server[]);
|
|
|
+ }
|
|
|
+
|
|
|
+ private async run(sql: string, params: any): Promise<RunResult> {
|
|
|
+ return new Promise<RunResult>((res, rej) => {
|
|
|
+ this.db.run(sql, params, function (err) {
|
|
|
+ if (err) return rej(err);
|
|
|
+ res(this);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private async stmt(sql: string, params: any[]) {
|
|
|
+ return new Promise<{ result: RunResult; rows: any[] }>((res, rej) => {
|
|
|
+ const stmt = this.db.prepare(sql);
|
|
|
+ stmt.all(params, function (err, rows) {
|
|
|
+ if (err) return rej(err);
|
|
|
+ res({ result: this, rows });
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|