mirror of
https://github.com/khairul169/db-backup-tool.git
synced 2026-09-16 17:33:26 +07:00
feat: add frontend
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { STORAGE_DIR } from "@/consts";
|
||||
import { STORAGE_DIR } from "../consts";
|
||||
import { defineConfig } from "drizzle-kit";
|
||||
|
||||
export default defineConfig({
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import path from "path";
|
||||
import { drizzle } from "drizzle-orm/bun-sqlite";
|
||||
import { Database } from "bun:sqlite";
|
||||
import { DATABASE_PATH } from "@/consts";
|
||||
import { mkdir } from "@/utility/utils";
|
||||
import { DATABASE_PATH } from "../consts";
|
||||
import { mkdir } from "../utility/utils";
|
||||
import schema from "./schema";
|
||||
|
||||
// Create database directory if not exists
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import fs from "fs";
|
||||
import { migrate } from "drizzle-orm/bun-sqlite/migrator";
|
||||
import { DATABASE_PATH } from "@/consts";
|
||||
import { DATABASE_PATH } from "../consts";
|
||||
import db, { sqlite } from ".";
|
||||
import { seed } from "./seed";
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
PostgresConfig,
|
||||
} from "../../types/database.types";
|
||||
import { exec } from "../../utility/process";
|
||||
import { urlencode } from "../../utility/utils";
|
||||
import BaseDbms from "./base";
|
||||
|
||||
class PostgresDbms extends BaseDbms {
|
||||
@@ -18,7 +19,7 @@ class PostgresDbms extends BaseDbms {
|
||||
}
|
||||
|
||||
async dump(dbName: string, path: string) {
|
||||
return exec(["pg_dump", this.dbUrl + `/${dbName}`, "-Ftar", "-f", path]);
|
||||
return exec(["pg_dump", this.dbUrl + `/${dbName}`, "-Z9", "-f", path]);
|
||||
}
|
||||
|
||||
async restore(path: string) {
|
||||
@@ -29,7 +30,7 @@ class PostgresDbms extends BaseDbms {
|
||||
"-cC",
|
||||
"--if-exists",
|
||||
"--exit-on-error",
|
||||
"-Ftar",
|
||||
// "-Ftar",
|
||||
path,
|
||||
]);
|
||||
}
|
||||
@@ -45,7 +46,7 @@ class PostgresDbms extends BaseDbms {
|
||||
private get dbUrl() {
|
||||
const { user, pass, host } = this.config;
|
||||
const port = this.config.port || 5432;
|
||||
return `postgresql://${user}:${pass}@${host}:${port}`;
|
||||
return `postgresql://${user}:${urlencode(pass)}@${host}:${port}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import {
|
||||
createBackupSchema,
|
||||
getAllBackupQuery,
|
||||
restoreBackupSchema,
|
||||
} from "@/schemas/backup.schema";
|
||||
import BackupService from "@/services/backup.service";
|
||||
} from "../schemas/backup.schema";
|
||||
import BackupService from "../services/backup.service";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import { Hono } from "hono";
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Hono } from "hono";
|
||||
import { handleError } from "@/middlewares/error-handler";
|
||||
import server from "./server.router";
|
||||
import backup from "./backup.router";
|
||||
import { cors } from "hono/cors";
|
||||
import { handleError } from "../middlewares/error-handler";
|
||||
|
||||
const routers = new Hono()
|
||||
// Middlewares
|
||||
.onError(handleError)
|
||||
.use(cors())
|
||||
|
||||
// App health check
|
||||
.get("/health-check", (c) => c.text("OK"))
|
||||
@@ -14,4 +16,6 @@ const routers = new Hono()
|
||||
.route("/servers", server)
|
||||
.route("/backups", backup);
|
||||
|
||||
export type AppRouter = typeof routers;
|
||||
|
||||
export default routers;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Hono } from "hono";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import { checkServerSchema, createServerSchema } from "@/schemas/server.schema";
|
||||
import { HTTPException } from "hono/http-exception";
|
||||
import DatabaseUtil from "@/lib/database-util";
|
||||
import ServerService from "@/services/server.service";
|
||||
import ServerService from "../services/server.service";
|
||||
import {
|
||||
checkServerSchema,
|
||||
createServerSchema,
|
||||
} from "../schemas/server.schema";
|
||||
import DatabaseUtil from "../lib/database-util";
|
||||
|
||||
const serverService = new ServerService();
|
||||
const router = new Hono()
|
||||
@@ -27,7 +30,7 @@ const router = new Hono()
|
||||
return c.json({ success: true, databases });
|
||||
} catch (err) {
|
||||
throw new HTTPException(400, {
|
||||
message: "Cannot connect to the database.",
|
||||
message: (err as any).message || "Cannot connect to the database.",
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -49,7 +52,7 @@ const router = new Hono()
|
||||
|
||||
.get("/:id", async (c) => {
|
||||
const { id } = c.req.param();
|
||||
const server = await serverService.getOrFail(id);
|
||||
const server = await serverService.getById(id);
|
||||
return c.json(server);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import db from "@/db";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { backupModel, databaseModel } from "@/db/models";
|
||||
import DatabaseUtil from "@/lib/database-util";
|
||||
import ServerService from "@/services/server.service";
|
||||
import { and, asc, eq, sql } from "drizzle-orm";
|
||||
import { BACKUP_DIR } from "@/consts";
|
||||
import { mkdir } from "@/utility/utils";
|
||||
import { hashFile } from "@/utility/hash";
|
||||
import ServerService from "../services/server.service";
|
||||
import db from "../db";
|
||||
import { backupModel, databaseModel } from "../db/models";
|
||||
import DatabaseUtil from "../lib/database-util";
|
||||
import { BACKUP_DIR } from "../consts";
|
||||
import { mkdir } from "../utility/utils";
|
||||
import { hashFile } from "../utility/hash";
|
||||
|
||||
let isRunning = false;
|
||||
const serverService = new ServerService();
|
||||
@@ -19,16 +19,12 @@ const runBackup = async (task: PendingTasks[number]) => {
|
||||
.set({ status: "running" })
|
||||
.where(eq(backupModel.id, task.id));
|
||||
|
||||
const server = serverService.parse(task.server as never);
|
||||
const server = serverService.parse(task.server);
|
||||
const dbName = task.database.name;
|
||||
const dbUtil = new DatabaseUtil(server.connection);
|
||||
|
||||
if (task.type === "backup") {
|
||||
const key = path.join(
|
||||
server.connection.host,
|
||||
dbName,
|
||||
`${Date.now()}.tar`
|
||||
);
|
||||
const key = path.join(server.connection.host, dbName, `${Date.now()}`);
|
||||
const outFile = path.join(BACKUP_DIR, key);
|
||||
mkdir(path.dirname(outFile));
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ const sshSchema = z
|
||||
const postgresSchema = z.object({
|
||||
type: z.literal("postgres"),
|
||||
host: z.string(),
|
||||
port: z.number().optional(),
|
||||
port: z.coerce.number().int().optional(),
|
||||
user: z.string(),
|
||||
pass: z.string(),
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import db from "@/db";
|
||||
import { backupModel, serverModel } from "@/db/models";
|
||||
import db from "../db";
|
||||
import { backupModel, serverModel } from "../db/models";
|
||||
import type {
|
||||
CreateBackupSchema,
|
||||
GetAllBackupQuery,
|
||||
RestoreBackupSchema,
|
||||
} from "@/schemas/backup.schema";
|
||||
import { and, desc, eq, inArray } from "drizzle-orm";
|
||||
} from "../schemas/backup.schema";
|
||||
import { and, count, desc, eq, inArray } from "drizzle-orm";
|
||||
import DatabaseService from "./database.service";
|
||||
import { HTTPException } from "hono/http-exception";
|
||||
|
||||
@@ -20,18 +20,28 @@ export default class BackupService {
|
||||
const page = query.page || 1;
|
||||
const limit = query.limit || 10;
|
||||
|
||||
const where = and(
|
||||
serverId ? eq(backupModel.serverId, serverId) : undefined,
|
||||
databaseId ? eq(backupModel.databaseId, databaseId) : undefined
|
||||
);
|
||||
|
||||
const [totalRows] = await db
|
||||
.select({ count: count() })
|
||||
.from(backupModel)
|
||||
.where(where)
|
||||
.limit(1);
|
||||
|
||||
const backups = await db.query.backup.findMany({
|
||||
where: (i) =>
|
||||
and(
|
||||
serverId ? eq(i.serverId, serverId) : undefined,
|
||||
databaseId ? eq(i.databaseId, databaseId) : undefined
|
||||
),
|
||||
where,
|
||||
with: {
|
||||
database: { columns: { name: true } },
|
||||
},
|
||||
orderBy: desc(serverModel.createdAt),
|
||||
limit,
|
||||
offset: (page - 1) * limit,
|
||||
});
|
||||
|
||||
return backups;
|
||||
return { count: totalRows.count, rows: backups };
|
||||
}
|
||||
|
||||
async getOrFail(id: string) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import db from "@/db";
|
||||
import { databaseModel } from "@/db/models";
|
||||
import db from "../db";
|
||||
import { databaseModel } from "../db/models";
|
||||
import { desc, eq } from "drizzle-orm";
|
||||
import { HTTPException } from "hono/http-exception";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import db from "@/db";
|
||||
import { databaseModel, serverModel, type ServerModel } from "@/db/models";
|
||||
import type { CreateServerSchema } from "@/schemas/server.schema";
|
||||
import db from "../db";
|
||||
import { databaseModel, serverModel, type ServerModel } from "../db/models";
|
||||
import type { CreateServerSchema } from "../schemas/server.schema";
|
||||
import { asc, desc, eq } from "drizzle-orm";
|
||||
import { HTTPException } from "hono/http-exception";
|
||||
|
||||
@@ -36,7 +36,15 @@ export default class ServerService {
|
||||
databases: true,
|
||||
},
|
||||
});
|
||||
return server;
|
||||
|
||||
if (!server) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const result = this.parse(server);
|
||||
delete result.connection.pass;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async create(data: CreateServerSchema) {
|
||||
@@ -73,7 +81,7 @@ export default class ServerService {
|
||||
});
|
||||
}
|
||||
|
||||
parse(data: ServerModel) {
|
||||
parse<T extends Pick<ServerModel, "connection" | "ssh">>(data: T) {
|
||||
const result = {
|
||||
...data,
|
||||
connection: data.connection ? JSON.parse(data.connection) : null,
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import DatabaseUtil from "@/lib/database-util";
|
||||
import { DOCKER_HOST, BACKUP_DIR } from "@/consts";
|
||||
import { mkdir } from "@/utility/utils";
|
||||
import DatabaseUtil from "../lib/database-util";
|
||||
import { DOCKER_HOST, BACKUP_DIR } from "../consts";
|
||||
import { mkdir } from "../utility/utils";
|
||||
import path from "path";
|
||||
|
||||
const main = async () => {
|
||||
|
||||
@@ -5,3 +5,7 @@ export const mkdir = (dir: string) => {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
};
|
||||
|
||||
export const urlencode = (str: string) => {
|
||||
return encodeURIComponent(str);
|
||||
};
|
||||
|
||||
@@ -22,11 +22,6 @@
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user