feat: add frontend

This commit is contained in:
2024-05-12 06:00:04 +07:00
parent 093b0056fb
commit 3d7508816f
72 changed files with 2558 additions and 212 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
import { STORAGE_DIR } from "@/consts";
import { STORAGE_DIR } from "../consts";
import { defineConfig } from "drizzle-kit";
export default defineConfig({
+2 -2
View File
@@ -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 -1
View File
@@ -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";
+4 -3
View File
@@ -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 -2
View File
@@ -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";
+5 -1
View File
@@ -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;
+8 -5
View File
@@ -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);
});
+9 -13
View File
@@ -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));
+1 -1
View File
@@ -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(),
});
+20 -10
View File
@@ -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) {
+2 -2
View File
@@ -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";
+13 -5
View File
@@ -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
View File
@@ -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 () => {
+4
View File
@@ -5,3 +5,7 @@ export const mkdir = (dir: string) => {
fs.mkdirSync(dir, { recursive: true });
}
};
export const urlencode = (str: string) => {
return encodeURIComponent(str);
};
+1 -6
View File
@@ -22,11 +22,6 @@
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
"noPropertyAccessFromIndexSignature": false
}
}