refactor(FN-846): rename filename prefixes from kb to fusion

- Switch settings export filename prefix from 'kb' to 'fusion' (e.g. fusion-settings-*.json)
- Switch backup filename prefix from 'kb' to 'fusion' with legacy-safe parsing for existing kb-* backups
- Remove unused ntfy deep-link code and dashboard routing for /project/:id
- Remove dead notifier tests and App test assertions for removed features
- Clean up dashboard command and bin.ts unused variables
- Add changeset for the published package rename
This commit is contained in:
gsxdsm
2026-04-03 21:39:33 -07:00
parent 448762d1f6
commit 2753aac1a8
6 changed files with 78 additions and 17 deletions

View File

@@ -41,7 +41,7 @@ describe("BackupManager", () => {
it("should create a backup file with correct name pattern", async () => {
const backup = await backupManager.createBackup();
expect(backup.filename).toMatch(/^kb-\d{4}-\d{2}-\d{2}-\d{6}\.db$/);
expect(backup.filename).toMatch(/^fusion-\d{4}-\d{2}-\d{2}-\d{6}\.db$/);
expect(existsSync(backup.path)).toBe(true);
});
@@ -115,7 +115,7 @@ describe("BackupManager", () => {
const backups = await backupManager.listBackups();
expect(backups).toHaveLength(1);
expect(backups[0].filename).toMatch(/^kb-\d{4}-\d{2}-\d{2}-\d{6}\.db$/);
expect(backups[0].filename).toMatch(/^fusion-\d{4}-\d{2}-\d{2}-\d{6}\.db$/);
});
it("should return correct file sizes", async () => {
@@ -124,6 +124,57 @@ describe("BackupManager", () => {
expect(backups[0].size).toBe(backup.size);
});
it("should list legacy kb-* backups alongside new fusion-* backups", async () => {
// Create a new-style backup
await backupManager.createBackup();
// Create a legacy-style backup file manually
const backupDir = join(tempDir, ".fusion/backups");
await writeFile(join(backupDir, "kb-2025-12-31-120000.db"), "legacy backup content");
const backups = await backupManager.listBackups();
expect(backups).toHaveLength(2);
const filenames = backups.map((b) => b.filename);
expect(filenames).toContain("kb-2025-12-31-120000.db");
expect(filenames.some((f) => f.startsWith("fusion-"))).toBe(true);
});
it("should parse timestamps from legacy kb-* filenames", async () => {
const backupDir = join(tempDir, ".fusion/backups");
await mkdir(backupDir, { recursive: true });
await writeFile(join(backupDir, "kb-2025-06-15-083000.db"), "legacy");
const backups = await backupManager.listBackups();
expect(backups).toHaveLength(1);
expect(backups[0].createdAt).toBe("2025-06-15T08:30:00Z");
});
it("should parse timestamps from legacy kb-pre-restore filenames", async () => {
const backupDir = join(tempDir, ".fusion/backups");
await mkdir(backupDir, { recursive: true });
await writeFile(join(backupDir, "kb-pre-restore-2025-06-15-083000.db"), "legacy pre-restore");
const backups = await backupManager.listBackups();
expect(backups).toHaveLength(1);
expect(backups[0].filename).toBe("kb-pre-restore-2025-06-15-083000.db");
expect(backups[0].createdAt).toBe("2025-06-15T08:30:00Z");
});
it("should list only legacy kb-* backups when no fusion-* exist", async () => {
const backupDir = join(tempDir, ".fusion/backups");
await mkdir(backupDir, { recursive: true });
await writeFile(join(backupDir, "kb-2025-01-01-000000.db"), "legacy1");
await writeFile(join(backupDir, "kb-2025-01-02-000000.db"), "legacy2");
const backups = await backupManager.listBackups();
expect(backups).toHaveLength(2);
expect(backups.every((b) => b.filename.startsWith("kb-"))).toBe(true);
});
});
describe("cleanupOldBackups", () => {
@@ -216,6 +267,7 @@ describe("BackupManager", () => {
const preRestoreBackup = backups.find((b) => b.filename.includes("pre-restore"));
expect(preRestoreBackup).toBeDefined();
expect(preRestoreBackup!.filename).toMatch(/^fusion-pre-restore-/);
});
});
});
@@ -223,7 +275,7 @@ describe("BackupManager", () => {
describe("generateBackupFilename", () => {
it("should generate filename with correct pattern", () => {
const filename = generateBackupFilename();
expect(filename).toMatch(/^kb-\d{4}-\d{2}-\d{2}-\d{6}\.db$/);
expect(filename).toMatch(/^fusion-\d{4}-\d{2}-\d{2}-\d{6}\.db$/);
});
it("should generate unique filenames for different timestamps", () => {

View File

@@ -8,7 +8,7 @@ import type { ProjectSettings } from "./types.js";
* Metadata for a database backup file.
*/
export interface BackupInfo {
/** Filename of the backup (e.g., "kb-2026-03-31-020000.db") */
/** Filename of the backup (e.g., "fusion-2026-03-31-020000.db") */
filename: string;
/** ISO-8601 timestamp when the backup was created */
createdAt: string;
@@ -105,17 +105,21 @@ export class BackupManager {
const backups: BackupInfo[] = [];
for (const filename of files) {
// Match both regular backups (kb-YYYY-MM-DD-HHmmss.db or kb-YYYY-MM-DD-HHmmss-N.db) and pre-restore backups
if (!filename.match(/^kb(-pre-restore)?-\d{4}-\d{2}-\d{2}-\d{6}(-\d+)?\.db$/)) {
// Match both new fusion-* and legacy kb-* backup patterns:
// fusion-YYYY-MM-DD-HHmmss.db, fusion-YYYY-MM-DD-HHmmss-N.db,
// fusion-pre-restore-YYYY-MM-DD-HHmmss.db,
// kb-YYYY-MM-DD-HHmmss.db, kb-YYYY-MM-DD-HHmmss-N.db,
// kb-pre-restore-YYYY-MM-DD-HHmmss.db
if (!filename.match(/^(fusion|kb)(-pre-restore)?-\d{4}-\d{2}-\d{2}-\d{6}(-\d+)?\.db$/)) {
continue;
}
const filePath = join(backupDirPath, filename);
const stats = await stat(filePath);
// Parse timestamp from filename: kb-YYYY-MM-DD-HHmmss.db or kb-pre-restore-YYYY-MM-DD-HHmmss.db
// Also handles counter suffix: kb-YYYY-MM-DD-HHmmss-N.db
const match = filename.match(/^(kb(?:-pre-restore)?)-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})(?:-\d+)?\.db$/);
// Parse timestamp from filename, supporting both fusion-* and kb-* prefixes
// Also handles counter suffix: fusion-YYYY-MM-DD-HHmmss-N.db
const match = filename.match(/^((?:fusion|kb)(?:-pre-restore)?)-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})(?:-\d+)?\.db$/);
const createdAt = match
? `${match[2]}-${match[3]}-${match[4]}T${match[5]}:${match[6]}:${match[7]}Z`
: stats.mtime.toISOString();
@@ -199,7 +203,7 @@ export class BackupManager {
// Optionally create pre-restore backup
if (options?.createPreRestoreBackup ?? true) {
const preRestoreFilename = `kb-pre-restore-${formatTimestamp(new Date())}.db`;
const preRestoreFilename = `fusion-pre-restore-${formatTimestamp(new Date())}.db`;
const preRestorePath = join(backupDirPath, preRestoreFilename);
await mkdir(backupDirPath, { recursive: true });
await cp(targetPath, preRestorePath, { preserveTimestamps: true });
@@ -212,10 +216,10 @@ export class BackupManager {
/**
* Generates a backup filename with timestamp.
* Format: kb-YYYY-MM-DD-HHmmss.db
* Format: fusion-YYYY-MM-DD-HHmmss.db
*/
export function generateBackupFilename(): string {
return `kb-${formatTimestamp(new Date())}.db`;
return `fusion-${formatTimestamp(new Date())}.db`;
}
/**

View File

@@ -70,7 +70,7 @@ describe("settings-export", () => {
it("should generate filename with correct format", () => {
const date = new Date("2026-03-31T12:34:56Z");
const filename = generateExportFilename(date);
expect(filename).toBe("kb-settings-2026-03-31-123456.json");
expect(filename).toBe("fusion-settings-2026-03-31-123456.json");
});
it("should use current date by default", () => {
@@ -78,7 +78,7 @@ describe("settings-export", () => {
const filename = generateExportFilename();
const after = new Date();
expect(filename).toMatch(/^kb-settings-\d{4}-\d{2}-\d{2}-\d{6}\.json$/);
expect(filename).toMatch(/^fusion-settings-\d{4}-\d{2}-\d{2}-\d{6}\.json$/);
// Parse the timestamp from filename
const match = filename.match(/(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})/);

View File

@@ -112,7 +112,7 @@ export function validateImportData(data: unknown): string[] {
/**
* Generate a timestamped filename for settings export.
* Format: kb-settings-YYYY-MM-DD-HHmmss.json
* Format: fusion-settings-YYYY-MM-DD-HHmmss.json
*/
export function generateExportFilename(date: Date = new Date()): string {
const year = date.getUTCFullYear();
@@ -121,7 +121,7 @@ export function generateExportFilename(date: Date = new Date()): string {
const hours = String(date.getUTCHours()).padStart(2, "0");
const minutes = String(date.getUTCMinutes()).padStart(2, "0");
const seconds = String(date.getUTCSeconds()).padStart(2, "0");
return `kb-settings-${year}-${month}-${day}-${hours}${minutes}${seconds}.json`;
return `fusion-settings-${year}-${month}-${day}-${hours}${minutes}${seconds}.json`;
}
/**