migrate to typescript

This commit is contained in:
Dainius Daukševičius
2020-10-17 21:56:54 +03:00
committed by Dainius Dauksevicius
parent c462323dd5
commit 08d27265fc
120 changed files with 5002 additions and 4711 deletions

View File

@@ -0,0 +1,13 @@
export interface IDatabase {
init(): void;
prepare(type: QueryType, query: string, params: any[]): any;
get<TModel>(query: string, params: any[]): TModel;
getAll<TModel>(query: string, params: any[]): TModel[];
run(query: string, params: any[]): void;
exec(query: string): any;
}
export type QueryType = 'get' | 'all' | 'run';

View File

@@ -1,28 +0,0 @@
var MysqlInterface = require('sync-mysql');
const logger = require('../utils/logger.js');
class Mysql {
constructor(msConfig) {
this.connection = new MysqlInterface(msConfig);
}
exec(query) {
this.prepare('run', query, []);
}
prepare (type, query, params) {
logger.debug("prepare (mysql): type: " + type + ", query: " + query + ", params: " + params);
if (type === 'get') {
return this.connection.query(query, params)[0];
} else if (type === 'run') {
this.connection.query(query, params);
} else if (type === 'all') {
return this.connection.query(query, params);
} else {
logger.warn('returning undefined...');
return undefined;
}
}
}
module.exports = Mysql;

49
src/databases/Mysql.ts Normal file
View File

@@ -0,0 +1,49 @@
import {Logger} from '../utils/logger';
import {IDatabase, QueryType} from './IDatabase';
// @ts-ignore
import MysqlInterface from 'sync-mysql';
export class Mysql implements IDatabase {
private connection: any;
constructor(private config: any) {
}
init() {
this.connection = new MysqlInterface(this.config);
}
exec(query: string) {
this.prepare('run', query, []);
}
prepare(type: QueryType, query: string, params: any[]) {
Logger.debug(`prepare (mysql): type: ${type}, query: ${query}, params: ${params}`);
const queryResult = this.connection.query(query, params);
switch (type) {
case 'get': {
return queryResult[0];
}
case 'all': {
return queryResult;
}
case 'run': {
break;
}
}
}
public get<TModel>(query: string, params: any[]): TModel {
return this.prepare('get', query, params);
}
public getAll<TModel>(query: string, params: any[]): TModel[] {
return this.prepare('all', query, params);
}
public run(query: string, params: any[]): void {
this.prepare('run', query, params);
}
}

View File

@@ -1,33 +0,0 @@
const { db } = require("./databases");
var config = require('../config.js');
const logger = require('../utils/logger.js');
class Sqlite {
constructor(connection) {
this.connection = connection;
}
getConnection() {
return this.connection;
}
prepare(type, query, params) {
if (type === 'get') {
return this.connection.prepare(query).get(...params);
} else if (type === 'run') {
this.connection.prepare(query).run(...params);
} else if (type === 'all') {
return this.connection.prepare(query).all(...params);
} else {
logger.debug('sqlite query: returning undefined')
logger.debug("prepare: type: " + type + ", query: " + query + ", params: " + params);
return undefined;
}
}
exec(query) {
return this.connection.exec(query);
}
}
module.exports = Sqlite;

107
src/databases/Sqlite.ts Normal file
View File

@@ -0,0 +1,107 @@
import {IDatabase, QueryType} from './IDatabase';
import Sqlite3, {Database, Database as SQLiteDatabase} from 'better-sqlite3';
import fs from "fs";
import path from "path";
import {getHash} from '../utils/getHash';
import {Logger} from '../utils/logger';
export class Sqlite implements IDatabase {
private db: SQLiteDatabase;
constructor(private config: SqliteConfig)
{
}
prepare(type: QueryType, query: string, params: any[]) {
const preparedQuery = this.db.prepare(query);
switch (type) {
case 'get': {
return preparedQuery.get(...params);
}
case 'all': {
return preparedQuery.all(...params);
}
case 'run': {
preparedQuery.run(...params);
break;
}
}
}
get<TModel>(query: string, params: any[]): TModel {
return this.prepare('get', query, params);
}
getAll<TModel>(query: string, params: any[]): TModel[] {
return this.prepare('all', query, params);
}
run(query: string, params: any[]): void {
this.prepare('run', query, params);
}
exec(query: string) {
return this.db.exec(query);
}
init() {
// Make dirs if required
if (!fs.existsSync(path.join(this.config.dbPath, "../"))) {
fs.mkdirSync(path.join(this.config.dbPath, "../"));
}
this.db = new Sqlite3(this.config.dbPath, {readonly: this.config.readOnly, fileMustExist: !this.config.createDbIfNotExists});
if (this.config.createDbIfNotExists && !this.config.readOnly && fs.existsSync(this.config.dbSchemaFileName)) {
this.db.exec(fs.readFileSync(this.config.dbSchemaFileName).toString());
}
if (!this.config.readOnly) {
this.db.function("sha256", (str: string) => {
return getHash(str, 1);
});
// Upgrade database if required
Sqlite.upgradeDB(this.db, this.config.fileNamePrefix, this.config.dbSchemaFolder);
}
// Enable WAL mode checkpoint number
if (this.config.enableWalCheckpointNumber) {
this.db.exec("PRAGMA journal_mode=WAL;");
this.db.exec("PRAGMA wal_autocheckpoint=1;");
}
// Enable Memory-Mapped IO
this.db.exec("pragma mmap_size= 500000000;");
}
attachDatabase(database: string, attachAs: string) {
this.db.prepare(`ATTACH ? as ${attachAs}`).run(database);
}
private static upgradeDB(db: Database, fileNamePrefix: string, schemaFolder: string) {
const versionCodeInfo = db.prepare("SELECT value FROM config WHERE key = ?").get("version");
let versionCode = versionCodeInfo ? versionCodeInfo.value : 0;
let path = schemaFolder + "/_upgrade_" + fileNamePrefix + "_" + (parseInt(versionCode) + 1) + ".sql";
Logger.debug('db update: trying ' + path);
while (fs.existsSync(path)) {
Logger.debug('db update: updating ' + path);
db.exec(fs.readFileSync(path).toString());
versionCode = db.prepare("SELECT value FROM config WHERE key = ?").get("version").value;
path = schemaFolder + "/_upgrade_" + fileNamePrefix + "_" + (parseInt(versionCode) + 1) + ".sql";
Logger.debug('db update: trying ' + path);
}
Logger.debug('db update: no file ' + path);
}
}
export interface SqliteConfig {
dbPath: string;
dbSchemaFileName: string;
dbSchemaFolder: string;
fileNamePrefix: string;
readOnly: boolean;
createDbIfNotExists: boolean;
enableWalCheckpointNumber: boolean
}

View File

@@ -1,81 +0,0 @@
var config = require('../config.js');
var Sqlite3 = require('better-sqlite3');
var fs = require('fs');
var path = require('path');
var Sqlite = require('./Sqlite.js')
var Mysql = require('./Mysql.js');
const logger = require('../utils/logger.js');
const getHash = require('../utils/getHash.js');
let options = {
readonly: config.readOnly,
fileMustExist: !config.createDatabaseIfNotExist
};
if (config.mysql) {
module.exports = {
db: new Mysql(config.mysql),
privateDB: new Mysql(config.privateMysql)
};
} else {
// Make dirs if required
if (!fs.existsSync(path.join(config.db, "../"))) {
fs.mkdirSync(path.join(config.db, "../"));
}
if (!fs.existsSync(path.join(config.privateDB, "../"))) {
fs.mkdirSync(path.join(config.privateDB, "../"));
}
var db = new Sqlite3(config.db, options);
var privateDB = new Sqlite3(config.privateDB, options);
if (config.createDatabaseIfNotExist && !config.readOnly) {
if (fs.existsSync(config.dbSchema)) db.exec(fs.readFileSync(config.dbSchema).toString());
if (fs.existsSync(config.privateDBSchema)) privateDB.exec(fs.readFileSync(config.privateDBSchema).toString());
}
if (!config.readOnly) {
db.function("sha256", (string) => {
return getHash(string, 1);
});
// Upgrade database if required
ugradeDB(db, "sponsorTimes");
ugradeDB(privateDB, "private")
}
// Attach private db to main db
db.prepare("ATTACH ? as privateDB").run(config.privateDB);
// Enable WAL mode checkpoint number
if (!config.readOnly && config.mode === "production") {
db.exec("PRAGMA journal_mode=WAL;");
db.exec("PRAGMA wal_autocheckpoint=1;");
}
// Enable Memory-Mapped IO
db.exec("pragma mmap_size= 500000000;");
privateDB.exec("pragma mmap_size= 500000000;");
module.exports = {
db: new Sqlite(db),
privateDB: new Sqlite(privateDB)
};
function ugradeDB(db, prefix) {
let versionCodeInfo = db.prepare("SELECT value FROM config WHERE key = ?").get("version");
let versionCode = versionCodeInfo ? versionCodeInfo.value : 0;
let path = config.schemaFolder + "/_upgrade_" + prefix + "_" + (parseInt(versionCode) + 1) + ".sql";
logger.debug('db update: trying ' + path);
while (fs.existsSync(path)) {
logger.debug('db update: updating ' + path);
db.exec(fs.readFileSync(path).toString());
versionCode = db.prepare("SELECT value FROM config WHERE key = ?").get("version").value;
path = config.schemaFolder + "/_upgrade_" + prefix + "_" + (parseInt(versionCode) + 1) + ".sql";
logger.debug('db update: trying ' + path);
}
logger.debug('db update: no file ' + path);
}
}

View File

@@ -0,0 +1,47 @@
import {config} from '../config';
import {Sqlite} from './Sqlite';
import {Mysql} from './Mysql';
import {IDatabase} from './IDatabase';
let db: IDatabase;
let privateDB: IDatabase;
if (config.mysql) {
db = new Mysql(config.mysql);
privateDB = new Mysql(config.privateMysql);
}
else {
db = new Sqlite({
dbPath: config.db,
dbSchemaFileName: config.dbSchema,
dbSchemaFolder: config.schemaFolder,
fileNamePrefix: 'sponsorTimes',
readOnly: config.readOnly,
createDbIfNotExists: config.createDatabaseIfNotExist,
enableWalCheckpointNumber: !config.readOnly && config.mode === "production"
});
privateDB = new Sqlite({
dbPath: config.privateDB,
dbSchemaFileName: config.privateDBSchema,
dbSchemaFolder: config.schemaFolder,
fileNamePrefix: 'private',
readOnly: config.readOnly,
createDbIfNotExists: config.createDatabaseIfNotExist,
enableWalCheckpointNumber: false
});
}
function initDb() {
db.init();
privateDB.init();
if (db instanceof Sqlite) {
// Attach private db to main db
(db as Sqlite).attachDatabase(config.privateDB, 'privateDB');
}
}
export {
db,
privateDB,
initDb,
}