Add Backup and Restore

This commit is contained in:
2026-03-02 14:51:19 +01:00
parent 634be9b01d
commit ec27ff9c04
37 changed files with 505 additions and 8 deletions
+2
View File
@@ -0,0 +1,2 @@
import { Command } from "commander";
export default function (): Command;
+22
View File
@@ -0,0 +1,22 @@
import { Command } from "commander";
import init from "../functions/init";
import path from "path";
import grabDBDir from "../utils/grab-db-dir";
import fs from "fs";
import grabDBBackupFileName from "../utils/grab-db-backup-file-name";
import chalk from "chalk";
import trimBackups from "../utils/trim-backups";
export default function () {
return new Command("backup")
.description("Backup Database")
.action(async (opts) => {
console.log(`Backing up database ...`);
const { config } = await init();
const { backup_dir, db_file_path } = grabDBDir({ config });
const new_db_file_name = grabDBBackupFileName({ config });
fs.cpSync(db_file_path, path.join(backup_dir, new_db_file_name));
trimBackups({ config });
console.log(`${chalk.bold(chalk.green(`DB Backup Success!`))}`);
process.exit();
});
}
+4
View File
@@ -2,6 +2,8 @@
import { program } from "commander";
import schema from "./schema";
import typedef from "./typedef";
import backup from "./backup";
import restore from "./restore";
/**
* # Describe Program
*/
@@ -14,6 +16,8 @@ program
*/
program.addCommand(schema());
program.addCommand(typedef());
program.addCommand(backup());
program.addCommand(restore());
/**
* # Handle Unavailable Commands
*/
+2
View File
@@ -0,0 +1,2 @@
import { Command } from "commander";
export default function (): Command;
+44
View File
@@ -0,0 +1,44 @@
import { Command } from "commander";
import init from "../functions/init";
import grabDBDir from "../utils/grab-db-dir";
import fs from "fs";
import chalk from "chalk";
import grabSortedBackups from "../utils/grab-sorted-backups";
import { select } from "@inquirer/prompts";
import grabBackupData from "../utils/grab-backup-data";
import path from "path";
export default function () {
return new Command("restore")
.description("Restore Database")
.action(async (opts) => {
console.log(`Restoring up database ...`);
const { config } = await init();
const { backup_dir, db_file_path } = grabDBDir({ config });
const backups = grabSortedBackups({ config });
if (!backups?.[0]) {
console.error(`No Backups to restore. Use the \`backup\` command to create a backup`);
process.exit(1);
}
try {
const selected_backup = await select({
message: "Select a backup:",
choices: backups.map((b, i) => {
const { backup_date } = grabBackupData({
backup_name: b,
});
return {
name: `Backup #${i + 1}: ${backup_date.toDateString()} ${backup_date.getHours()}:${backup_date.getMinutes()}:${backup_date.getSeconds().toString().padStart(2, "0")}`,
value: b,
};
}),
});
fs.cpSync(path.join(backup_dir, selected_backup), db_file_path);
console.log(`${chalk.bold(chalk.green(`DB Restore Success!`))}`);
process.exit();
}
catch (error) {
console.error(`Backup Restore ERROR => ${error.message}`);
process.exit();
}
});
}
+2
View File
@@ -7,6 +7,7 @@ import dbSchemaToTypeDef from "../lib/sqlite/schema-to-typedef";
import _ from "lodash";
import { DefaultFields } from "../types";
import appendDefaultFieldsToDbSchema from "../utils/append-default-fields-to-db-schema";
import chalk from "chalk";
export default function () {
return new Command("schema")
.description("Build DB From Schema")
@@ -32,6 +33,7 @@ export default function () {
dst_file: out_file,
});
}
console.log(`${chalk.bold(chalk.green(`DB Schema setup success!`))}`);
process.exit();
});
}
+2
View File
@@ -4,6 +4,7 @@ import dbSchemaToTypeDef from "../lib/sqlite/schema-to-typedef";
import path from "path";
import grabDirNames from "../data/grab-dir-names";
import appendDefaultFieldsToDbSchema from "../utils/append-default-fields-to-db-schema";
import chalk from "chalk";
export default function () {
return new Command("typedef")
.description("Build DB From Schema")
@@ -23,6 +24,7 @@ export default function () {
console.error(``);
process.exit(1);
}
console.log(`${chalk.bold(chalk.green(`Typedef gen success!`))}`);
process.exit();
});
}
+1
View File
@@ -1,3 +1,4 @@
export declare const AppData: {
readonly ConfigFileName: "bun-sqlite.config.ts";
readonly MaxBackups: 10;
};
+1
View File
@@ -1,3 +1,4 @@
export const AppData = {
ConfigFileName: "bun-sqlite.config.ts",
MaxBackups: 10,
};
+3 -3
View File
@@ -1,16 +1,16 @@
import { Database } from "bun:sqlite";
import path from "node:path";
import * as sqliteVec from "sqlite-vec";
import grabDirNames from "../../data/grab-dir-names";
import init from "../../functions/init";
import grabDBDir from "../../utils/grab-db-dir";
const { ROOT_DIR } = grabDirNames();
const { config } = await init();
let db_dir = ROOT_DIR;
if (config.db_dir) {
db_dir = config.db_dir;
}
const DBFilePath = path.join(db_dir, config.db_name);
const DbClient = new Database(DBFilePath, {
const { db_file_path } = grabDBDir({ config });
const DbClient = new Database(db_file_path, {
create: true,
});
sqliteVec.load(DbClient);
+1
View File
@@ -997,6 +997,7 @@ export type BunSQLiteConfig = {
* The Directory for backups
*/
db_backup_dir: string;
max_backups?: number;
/**
* The Root Directory for the DB file and schema
*/
+9
View File
@@ -0,0 +1,9 @@
type Params = {
backup_name: string;
};
export default function grabBackupData({ backup_name }: Params): {
backup_date: Date;
backup_date_timestamp: number;
origin_backup_name: string;
};
export {};
+7
View File
@@ -0,0 +1,7 @@
export default function grabBackupData({ backup_name }) {
const backup_parts = backup_name.split("-");
const backup_date_timestamp = Number(backup_parts.pop());
const origin_backup_name = backup_parts.join("-");
const backup_date = new Date(backup_date_timestamp);
return { backup_date, backup_date_timestamp, origin_backup_name };
}
+6
View File
@@ -0,0 +1,6 @@
import type { BunSQLiteConfig } from "../types";
type Params = {
config: BunSQLiteConfig;
};
export default function grabDBBackupFileName({ config }: Params): string;
export {};
+4
View File
@@ -0,0 +1,4 @@
export default function grabDBBackupFileName({ config }) {
const new_db_file_name = `${config.db_name}-${Date.now()}`;
return new_db_file_name;
}
+10
View File
@@ -0,0 +1,10 @@
import type { BunSQLiteConfig } from "../types";
type Params = {
config: BunSQLiteConfig;
};
export default function grabDBDir({ config }: Params): {
db_dir: string;
backup_dir: string;
db_file_path: string;
};
export {};
+12
View File
@@ -0,0 +1,12 @@
import path from "path";
import grabDirNames from "../data/grab-dir-names";
export default function grabDBDir({ config }) {
const { ROOT_DIR } = grabDirNames();
let db_dir = ROOT_DIR;
if (config.db_dir) {
db_dir = config.db_dir;
}
const backup_dir = path.resolve(db_dir, config.db_backup_dir);
const db_file_path = path.resolve(db_dir, config.db_name);
return { db_dir, backup_dir, db_file_path };
}
+6
View File
@@ -0,0 +1,6 @@
import type { BunSQLiteConfig } from "../types";
type Params = {
config: BunSQLiteConfig;
};
export default function grabSortedBackups({ config }: Params): string[];
export {};
+18
View File
@@ -0,0 +1,18 @@
import grabDBDir from "../utils/grab-db-dir";
import fs from "fs";
export default function grabSortedBackups({ config }) {
const { backup_dir } = grabDBDir({ config });
const backups = fs.readdirSync(backup_dir);
/**
* Order Backups. Most recent first.
*/
const ordered_backups = backups.sort((a, b) => {
const a_date = Number(a.split("-").pop());
const b_date = Number(b.split("-").pop());
if (a_date > b_date) {
return -1;
}
return 1;
});
return ordered_backups;
}
+6
View File
@@ -0,0 +1,6 @@
import type { BunSQLiteConfig } from "../types";
type Params = {
config: BunSQLiteConfig;
};
export default function trimBackups({ config }: Params): void;
export {};
+19
View File
@@ -0,0 +1,19 @@
import grabDBDir from "../utils/grab-db-dir";
import fs from "fs";
import grabSortedBackups from "./grab-sorted-backups";
import { AppData } from "../data/app-data";
import path from "path";
export default function trimBackups({ config }) {
const { backup_dir } = grabDBDir({ config });
const backups = grabSortedBackups({ config });
const max_backups = config.max_backups || AppData["MaxBackups"];
for (let i = 0; i < backups.length; i++) {
const backup_name = backups[i];
if (!backup_name)
continue;
if (i > max_backups - 1) {
const backup_file_to_unlink = path.join(backup_dir, backup_name);
fs.unlinkSync(backup_file_to_unlink);
}
}
}