First version without typescript errors

This commit is contained in:
2026-06-21 07:05:49 +01:00
parent 9c8f6e20aa
commit eb86e1803d
16 changed files with 362 additions and 223 deletions
+2 -15
View File
@@ -5,9 +5,6 @@ import grabDBConnection from "./grab-db-connection";
type Param = {
query: string;
values?: string[] | object;
noErrorLogs?: boolean;
database?: string;
tableSchema?: BUN_MARIADB_TableSchemaType;
config?: ConnectionConfig;
};
@@ -17,18 +14,12 @@ type Param = {
*/
export default async function dbHandler<
T extends { [k: string]: any } = { [k: string]: any },
>({
query,
values,
noErrorLogs,
database,
config,
}: Param): Promise<DBResponseObject> {
>({ query, values, config }: Param): Promise<DBResponseObject> {
let CONNECTION: Connection | undefined;
let results: T | null = null;
try {
CONNECTION = await grabDBConnection({ database, config });
CONNECTION = await grabDBConnection({ config });
if (query && values) {
const queryResults = await CONNECTION.query(query, values);
@@ -46,10 +37,6 @@ export default async function dbHandler<
throw new Error("Authentication Failed!");
}
if (!noErrorLogs) {
console.log(error);
}
results = null;
} finally {
await CONNECTION?.end();
+1 -1
View File
@@ -8,7 +8,7 @@ type Return = ConnectionConfig["ssl"] | undefined;
* # Grab SSL
*/
export default function grabDbSSL(): Return {
const caProivdedPath = process.env.DSQL_SSL_CA_CERT;
const caProivdedPath = process.env.BUN_MARIADB_SERVER_SSL_KEY_PATH;
if (!caProivdedPath?.match(/./)) {
return undefined;
+9 -8
View File
@@ -8,17 +8,18 @@ import grabDbSSL from "./grab-db-ssl";
export default function grabDSQLConnectionConfig(
param?: DsqlConnectionParam,
): ConnectionConfig {
const CONN_TIMEOUT = 10000;
const configData = global.CONFIG;
const CONN_TIMEOUT = configData?.connection_timeout || 10000;
const config: ConnectionConfig = {
host: process.env.DSQL_DB_HOST,
user: process.env.DSQL_DB_USERNAME,
password: process.env.DSQL_DB_PASSWORD,
// database: param?.dbFullName,
port: process.env.DSQL_DB_PORT
? Number(process.env.DSQL_DB_PORT)
host: process.env.BUN_MARIADB_SERVER_HOST,
user: process.env.BUN_MARIADB_SERVER_USERNAME,
password: process.env.BUN_MARIADB_SERVER_PASSWORD,
database: configData?.db_name,
port: process.env.BUN_MARIADB_SERVER_PORT
? Number(process.env.BUN_MARIADB_SERVER_PORT)
: undefined,
charset: "utf8mb4",
charset: configData?.charset || "utf8mb4",
ssl: grabDbSSL(),
bigIntAsNumber: true,
supportBigNumbers: true,
+35 -27
View File
@@ -4,37 +4,45 @@ type Params = {
data: any | any[];
};
export default async function ({ sql: passed_sql, table, data }: Params) {
let sql = passed_sql;
function quoteIdentifier(identifier: string): string {
return `\`${identifier.replace(/`/g, "``")}\``;
}
export default async function ({ sql: passedSql, table, data }: Params) {
const config = global.CONFIG;
const dbSchema = global.DB_SCHEMA;
const table_schema = dbSchema.tables.find((t) => t.tableName == table);
const now = Date.now();
const tableSchema = dbSchema?.tables.find(
(t: { tableName: string }) => t.tableName == table,
);
if (table_schema?.tableName) {
const set_sql_arr = Object.keys(
Array.isArray(data) ? data[0] : data,
).map((field) => `${field} = excluded.${field}`);
set_sql_arr.push(`updated_at = ${now}`);
const set_sql = set_sql_arr.join(", ");
const unique_fields = table_schema.fields.filter((f) => f.unique);
for (let i = 0; i < unique_fields.length; i++) {
const field = unique_fields[i];
sql += ` ON CONFLICT(${field?.fieldName}) DO UPDATE SET ${set_sql}`;
}
if (table_schema.uniqueConstraints?.[0]) {
for (let i = 0; i < table_schema.uniqueConstraints.length; i++) {
const constraint = table_schema.uniqueConstraints[i];
sql += ` ON CONFLICT(${constraint?.constraintTableFields?.map((c) => c.value)?.join(", ")}) DO UPDATE SET ${set_sql}`;
}
}
if (!tableSchema?.tableName) {
return passedSql;
}
return sql;
const dataObject = Array.isArray(data) ? data[0] : data;
const dataKeys = Object.keys(dataObject || {});
if (dataKeys.length === 0) {
return passedSql;
}
const hasUniqueField =
tableSchema.fields.some((field) => field.unique) ||
Boolean(tableSchema.uniqueConstraints?.length);
if (!hasUniqueField) {
return passedSql;
}
const setSql = dataKeys
.filter((field: string) => field !== "updated_at")
.map(
(field: string) =>
`${quoteIdentifier(field)} = VALUES(${quoteIdentifier(field)})`,
);
setSql.push(`updated_at = ${Date.now()}`);
return `${passedSql} ON DUPLICATE KEY UPDATE ${setSql.join(", ")}`;
}
+39 -20
View File
@@ -1,4 +1,4 @@
import DbClient from ".";
import dbHandler from "../db-handler";
import _ from "lodash";
import type { APIResponseObject, ServerQueryParam } from "../../types";
import sqlGenerator from "../../utils/sql-generator";
@@ -12,6 +12,10 @@ type Params<
targetId?: number | string;
};
function quoteIdentifier(identifier: string): string {
return `\`${identifier.replace(/`/g, "``")}\``;
}
export default async function DbDelete<
Schema extends { [k: string]: any } = { [k: string]: any },
Table extends string = string,
@@ -39,30 +43,13 @@ export default async function DbDelete<
}
sqlObj = sqlGenerator({
tableName: table,
tableName: quoteIdentifier(table),
genObject: finalQuery,
});
const whereClause = sqlObj.string.match(/WHERE .*/)?.[0];
if (whereClause) {
let sql = `DELETE FROM ${table} ${whereClause}`;
sqlObj.string = sql;
const res = DbClient.run(sql, sqlObj.values);
return {
success: Boolean(res.changes),
postInsertReturn: {
affectedRows: res.changes,
insertId: Number(res.lastInsertRowid),
},
debug: {
sqlObj,
},
};
} else {
if (!whereClause) {
return {
success: false,
msg: `No WHERE clause`,
@@ -71,6 +58,38 @@ export default async function DbDelete<
},
};
}
const sql = `DELETE FROM ${quoteIdentifier(table)} ${whereClause}`;
sqlObj.string = sql;
const res = await dbHandler({
query: sql,
values: sqlObj.values as any,
});
if (!res.success) {
return {
success: false,
msg: "Database delete failed",
debug: {
sqlObj,
},
};
}
const singleRes = res.single_res as any;
return {
success: Boolean(singleRes?.affectedRows),
postInsertReturn: {
affectedRows: Number(singleRes?.affectedRows),
insertId: Number(singleRes?.insertId),
},
debug: {
sqlObj,
},
};
} catch (error: any) {
return {
success: false,
+27 -7
View File
@@ -1,4 +1,4 @@
import DbClient from ".";
import dbHandler from "../db-handler";
import type { APIResponseObject, SQLInsertGenReturn } from "../../types";
import sqlInsertGenerator from "../../utils/sql-insert-generator";
import grabDuplicateSafeInsertSql from "../grab-duplicate-safe-insert-sql";
@@ -35,21 +35,41 @@ export default async function DbInsert<
data: finalData as any[],
}) || null;
let sql = sqlObj?.query || "";
if (!sqlObj) {
return {
success: false,
msg: "No insert SQL generated",
};
}
let sql = sqlObj.query;
if (update_on_duplicate && data[0]) {
sql = await grabDuplicateSafeInsertSql({ data, table, sql });
}
(sqlObj || ({} as any)).query = sql;
const res = await dbHandler({
query: sql,
values: sqlObj.values as any,
});
const res = DbClient.run(sql, sqlObj?.values || []);
if (!res.success) {
return {
success: false,
msg: "Database insert failed",
debug: {
sqlObj,
},
};
}
const singleRes = res.single_res as any;
return {
success: Boolean(Number(res.lastInsertRowid)),
success: Boolean(singleRes?.affectedRows || singleRes?.insertId),
postInsertReturn: {
affectedRows: res.changes,
insertId: Number(res.lastInsertRowid),
affectedRows: Number(singleRes?.affectedRows),
insertId: Number(singleRes?.insertId),
},
debug: {
sqlObj,
+26 -20
View File
@@ -59,10 +59,6 @@ class MariaDBSchemaManager {
console.log("Schema synchronization complete!");
}
private getDatabaseName(): string | undefined {
return this.db_schema.dbName || this.db_schema.dbSlug;
}
private quoteIdentifier(identifier: string): string {
return `\`${identifier.replace(/`/g, "``")}\``;
}
@@ -71,7 +67,8 @@ class MariaDBSchemaManager {
where: string;
values: QueryValues;
} {
const databaseName = this.getDatabaseName();
const config = global.CONFIG;
const databaseName = config?.db_name;
if (databaseName) {
return {
@@ -87,12 +84,11 @@ class MariaDBSchemaManager {
}
private async run(query: string, values?: QueryValues): Promise<void> {
const config = global.CONFIG;
const res = await dbHandler({
query,
values: values as any,
config: this.getDatabaseName()
? { database: this.getDatabaseName() }
: undefined,
config: config?.db_config,
});
if (!res.success) {
@@ -107,9 +103,7 @@ class MariaDBSchemaManager {
const res = await dbHandler<T>({
query,
values: values as any,
config: this.getDatabaseName()
? { database: this.getDatabaseName() }
: undefined,
config: global.CONFIG.db_config,
});
if (!res.success) {
@@ -195,7 +189,9 @@ class MariaDBSchemaManager {
for (const tableName of tablesToDrop) {
console.log(`Dropping table: ${tableName}`);
await this.run(`DROP TABLE IF EXISTS ${this.quoteIdentifier(tableName)}`);
await this.run(
`DROP TABLE IF EXISTS ${this.quoteIdentifier(tableName)}`,
);
await this.removeDbManagerTable(tableName);
}
}
@@ -308,7 +304,10 @@ class MariaDBSchemaManager {
const options = ["ENGINE=InnoDB"];
if (table.collation) {
options.push("DEFAULT CHARSET=utf8mb4", `COLLATE ${table.collation}`);
options.push(
"DEFAULT CHARSET=utf8mb4",
`COLLATE ${table.collation}`,
);
}
return ` ${options.join(" ")}`;
@@ -321,11 +320,12 @@ class MariaDBSchemaManager {
await this.recreateTable(table);
}
private async getTableColumns(
tableName: string,
): Promise<ColumnInfoRow[]> {
private async getTableColumns(tableName: string): Promise<ColumnInfoRow[]> {
const tableSchemaWhere = this.tableSchemaWhere(tableName);
const rows = await this.query<{ COLUMN_NAME: string; COLUMN_TYPE: string }>(
const rows = await this.query<{
COLUMN_NAME: string;
COLUMN_TYPE: string;
}>(
`SELECT COLUMN_NAME AS name, COLUMN_TYPE AS type FROM information_schema.COLUMNS WHERE ${tableSchemaWhere.where} AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION`,
tableSchemaWhere.values,
);
@@ -379,7 +379,9 @@ class MariaDBSchemaManager {
existingRows = await this.query<Record<string, any>>(
`SELECT * FROM ${this.quoteIdentifier(table.tableName)}`,
);
await this.run(`DROP TABLE ${this.quoteIdentifier(table.tableName)}`);
await this.run(
`DROP TABLE ${this.quoteIdentifier(table.tableName)}`,
);
}
await this.createTable(table);
@@ -395,7 +397,9 @@ class MariaDBSchemaManager {
const existingColumns = await this.getTableColumns(table.tableName);
const columnsToKeep = table.fields
.filter((field) =>
existingColumns.some((column) => column.name === field.fieldName),
existingColumns.some(
(column) => column.name === field.fieldName,
),
)
.map((field) => field.fieldName)
.filter((fieldName): fieldName is string => Boolean(fieldName));
@@ -624,7 +628,9 @@ class MariaDBSchemaManager {
if (!stillExists) {
console.log(`Dropping index: ${indexName}`);
await this.run(`DROP INDEX ${this.quoteIdentifier(indexName)} ON ${this.quoteIdentifier(table.tableName)}`);
await this.run(
`DROP INDEX ${this.quoteIdentifier(indexName)} ON ${this.quoteIdentifier(table.tableName)}`,
);
}
}
+53 -19
View File
@@ -1,5 +1,4 @@
import mysql from "mysql";
import DbClient from ".";
import dbHandler from "../db-handler";
import _ from "lodash";
import type { APIResponseObject, ServerQueryParam } from "../../types";
import sqlGenerator from "../../utils/sql-generator";
@@ -14,6 +13,10 @@ type Params<
targetId?: number | string;
};
function quoteIdentifier(identifier: string): string {
return `\`${identifier.replace(/`/g, "``")}\``;
}
export default async function DbSelect<
Schema extends { [k: string]: any } = { [k: string]: any },
Table extends string = string,
@@ -42,44 +45,75 @@ export default async function DbSelect<
}
sqlObj = sqlGenerator({
tableName: table,
tableName: quoteIdentifier(table),
genObject: finalQuery,
});
let sql = mysql.format(sqlObj.string, sqlObj.values);
const res = await dbHandler<Schema>({
query: sqlObj.string,
values: sqlObj.values as any,
});
const res = DbClient.query<Schema, Schema[]>(sql);
const batchRes = res.all();
if (!res.success) {
return {
success: false,
msg: "Database select failed",
debug: {
sqlObj,
sql: sqlObj.string,
},
};
}
const batchRes = (res.payload || []) as Schema[];
let resp: APIResponseObject<Schema> = {
success: Boolean(batchRes[0]),
success: true,
payload: batchRes,
singleRes: batchRes[0],
debug: {
sqlObj,
sql,
sql: sqlObj.string,
},
};
if (count) {
let count_sql_object = sqlGenerator({
tableName: table,
const countSqlObject = sqlGenerator({
tableName: quoteIdentifier(table),
genObject: finalQuery,
count,
});
let count_sql = mysql.format(
count_sql_object.string,
count_sql_object.values,
);
const countSql = `SELECT COUNT(*) AS count FROM (${countSqlObject.string}) AS c`;
count_sql = `SELECT COUNT(*) FROM (${count_sql}) as c`;
const countRes = await dbHandler<{ count: number }>({
query: countSql,
values: countSqlObject.values as any,
});
const count_res = DbClient.query<Schema, Schema[]>(count_sql).all();
if (!countRes.success) {
return {
...resp,
success: false,
msg: "Database count failed",
debug: {
...resp.debug,
count_sql: countSql,
},
};
}
const count_val = count_res[0]?.["COUNT(*)"];
resp["count"] = Number(count_val);
resp["debug"]["count_sql"] = count_sql;
const countRows = countRes.payload || [];
const countVal = countRows[0]?.count ?? countRows[0]?.["COUNT(*)"];
resp = {
...resp,
count: Number(countVal),
debug: {
...resp.debug,
count_sql: countSql,
},
};
}
return resp;
+31 -12
View File
@@ -1,5 +1,4 @@
import DbClient from ".";
import _ from "lodash";
import dbHandler from "../db-handler";
import type { APIResponseObject, SQLInsertGenValueType } from "../../types";
type Params = {
@@ -11,25 +10,45 @@ export default async function DbSQL<
T extends { [k: string]: any } = { [k: string]: any },
>({ sql, values }: Params): Promise<APIResponseObject<T>> {
try {
const trimmed_sql = sql.trim();
const trimmedSql = sql.trim();
const isSelect = trimmedSql.match(/^select/i);
const res = trimmed_sql.match(/^select/i)
? DbClient.query(trimmed_sql).all(...(values || []))
: DbClient.run(trimmed_sql, values || []);
const res = await dbHandler<T>({
query: trimmedSql,
values: values as any,
});
if (!res.success) {
return {
success: false,
msg: "Database query failed",
debug: {
sqlObj: {
sql: trimmedSql,
values,
},
sql,
},
};
}
const payload = isSelect ? ((res.payload || []) as T[]) : undefined;
const singleRes = isSelect ? payload?.[0] : (res.single_res as T);
const singleRaw = res.single_res as any;
return {
success: true,
payload: Array.isArray(res) ? (res as T[]) : undefined,
singleRes: Array.isArray(res) ? (res as T[])?.[0] : undefined,
postInsertReturn: Array.isArray(res)
payload,
singleRes,
postInsertReturn: isSelect
? undefined
: {
affectedRows: res.changes,
insertId: Number(res.lastInsertRowid),
affectedRows: Number(singleRaw?.affectedRows),
insertId: Number(singleRaw?.insertId),
},
debug: {
sqlObj: {
sql: trimmed_sql,
sql: trimmedSql,
values,
},
sql,
+65 -48
View File
@@ -1,4 +1,4 @@
import DbClient from ".";
import dbHandler from "../db-handler";
import _ from "lodash";
import type {
APIResponseObject,
@@ -17,6 +17,10 @@ type Params<
targetId?: number | string;
};
function quoteIdentifier(identifier: string): string {
return `\`${identifier.replace(/`/g, "``")}\``;
}
export default async function DbUpdate<
Schema extends { [k: string]: any } = { [k: string]: any },
Table extends string = string,
@@ -45,63 +49,76 @@ export default async function DbUpdate<
}
const sqlQueryObj = sqlGenerator({
tableName: table,
tableName: quoteIdentifier(table),
genObject: finalQuery,
});
let values: SQLInsertGenValueType[] = [];
const whereClause = sqlQueryObj.string.match(/WHERE .*/)?.[0];
if (whereClause) {
let sql = `UPDATE ${table} SET`;
const finalData: { [k: string]: SQLInsertGenValueType } = {
updated_at: Date.now(),
...data,
};
const keys = Object.keys(finalData);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (!key) continue;
const isLast = i == keys.length - 1;
sql += ` ${key}=?`;
const value = finalData[key];
values.push(value || null);
if (!isLast) {
sql += `,`;
}
}
sql += ` ${whereClause}`;
values = [...values, ...sqlQueryObj.values];
sqlObj.string = sql;
sqlObj.values = values as any[];
const res = DbClient.run(sql, values);
return {
success: Boolean(res.changes),
postInsertReturn: {
affectedRows: res.changes,
insertId: Number(res.lastInsertRowid),
},
debug: {
sqlObj,
},
};
} else {
if (!whereClause) {
return {
success: false,
msg: `No WHERE clause`,
};
}
let values: SQLInsertGenValueType[] = [];
let sql = `UPDATE ${quoteIdentifier(table)} SET`;
const finalData: { [k: string]: SQLInsertGenValueType } = {
updated_at: Date.now(),
...data,
};
const keys = Object.keys(finalData);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (!key) continue;
const isLast = i == keys.length - 1;
sql += ` ${quoteIdentifier(key)}=?`;
values.push(finalData[key] ?? null);
if (!isLast) {
sql += `,`;
}
}
sql += ` ${whereClause}`;
values = [...values, ...sqlQueryObj.values];
sqlObj.string = sql;
sqlObj.values = values as any[];
const res = await dbHandler({
query: sql,
values: values as any,
});
if (!res.success) {
return {
success: false,
msg: "Database update failed",
debug: {
sqlObj,
},
};
}
const singleRes = res.single_res as any;
return {
success: Boolean(singleRes?.affectedRows || singleRes?.changedRows),
postInsertReturn: {
affectedRows: Number(singleRes?.affectedRows),
insertId: Number(singleRes?.insertId),
},
debug: {
sqlObj,
},
};
} catch (error: any) {
return {
success: false,
-7
View File
@@ -1,7 +0,0 @@
import _ from "lodash";
import type { BUN_MARIADB_DatabaseSchemaType } from "../../types";
export const DbSchema: BUN_MARIADB_DatabaseSchemaType = {
dbName: "travis-ai",
tables: [],
};