chore: init project

This commit is contained in:
2024-05-10 09:43:18 +07:00
commit 387fa38e65
34 changed files with 650 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
import type { DatabaseListItem } from "../types/database.types";
class BaseDbms {
async getDatabases(): Promise<DatabaseListItem[]> {
return [];
}
async dump(_dbName: string, _path: string): Promise<string> {
return "";
}
async restore(_path: string): Promise<string> {
return "";
}
}
export default BaseDbms;
+49
View File
@@ -0,0 +1,49 @@
import type { DatabaseListItem, PostgresConfig } from "../types/database.types";
import { exec } from "../utility/process";
import BaseDbms from "./base";
class PostgresDbms extends BaseDbms {
constructor(private config: PostgresConfig) {
super();
}
async getDatabases() {
return this.sql<DatabaseListItem>(
"SELECT datname AS name, pg_size_pretty(pg_database_size(datname)) AS size \
FROM pg_database WHERE datistemplate=false ORDER BY name ASC"
);
}
async dump(dbName: string, path: string) {
return exec(["pg_dump", this.dbUrl + `/${dbName}`, "-Ftar", "-f", path]);
}
async restore(path: string) {
return exec([
"pg_restore",
"-d",
this.dbUrl,
"-cC",
"--if-exists",
"--exit-on-error",
"-Ftar",
path,
]);
}
private async sql<T = any>(query: string) {
const sql = `SELECT row_to_json(row) FROM (${query}) row`;
return exec(["psql", this.dbUrl, "-t", "-c", sql])
.then((res) => res.split("\n").map((i) => i.trim()))
.then((res) => res.filter((i) => i.length > 0))
.then((i) => i.map((data) => JSON.parse(data) as T));
}
private get dbUrl() {
const { user, pass, host } = this.config;
const port = this.config.port || 5432;
return `postgresql://${user}:${pass}@${host}:${port}`;
}
}
export default PostgresDbms;
+31
View File
@@ -0,0 +1,31 @@
import BaseDbms from "../dbms/base";
import PostgresDbms from "../dbms/postgres";
import type { DatabaseConfig, DatabaseListItem } from "../types/database.types";
class DatabaseUtil {
private db = new BaseDbms();
constructor(public config: DatabaseConfig) {
switch (config.type) {
case "postgres":
this.db = new PostgresDbms(config);
break;
default:
throw Error("Database type not supported: " + config.type);
}
}
async getDatabases(): Promise<DatabaseListItem[]> {
return this.db.getDatabases();
}
async dump(dbName: string, path: string): Promise<string> {
return this.db.dump(dbName, path);
}
async restore(path: string): Promise<string> {
return this.db.restore(path);
}
}
export default DatabaseUtil;
+14
View File
@@ -0,0 +1,14 @@
export type DatabaseConfig = PostgresConfig;
export type PostgresConfig = {
type: "postgres";
host: string;
user: string;
pass: string;
port?: number;
};
export type DatabaseListItem = {
name: string;
size: string;
};
+4
View File
@@ -0,0 +1,4 @@
import path from "path";
export const DOCKER_HOST = "host.docker.internal";
export const STORAGE_DIR = path.resolve(__dirname, "../../storage");
+23
View File
@@ -0,0 +1,23 @@
type ExecOptions = {
env?: any;
// TODO: add ssh wrapper
ssh?: any;
};
export const exec = async (
cmds: string[],
options: Partial<ExecOptions> = {}
) => {
const proc = Bun.spawn(cmds, {
env: options.env,
stderr: "pipe",
});
const err = await new Response(proc.stderr).text();
const res = await new Response(proc.stdout).text();
if (err) {
throw new Error(err);
}
return res;
};
+7
View File
@@ -0,0 +1,7 @@
import fs from "fs";
export const mkdir = (dir: string) => {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
};