mirror of
https://github.com/khairul169/db-backup-tool.git
synced 2026-09-16 17:33:26 +07:00
chore: init project
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
import path from "path";
|
||||
|
||||
export const DOCKER_HOST = "host.docker.internal";
|
||||
export const STORAGE_DIR = path.resolve(__dirname, "../../storage");
|
||||
@@ -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;
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import fs from "fs";
|
||||
|
||||
export const mkdir = (dir: string) => {
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user