feat: convert file manager into a plugin

This commit is contained in:
LukeGus committed 2026-09-23 16:27:29 -05:00
1 parent b76da9ab52
commit c85c1fdb86
259 files changed
+54155 -16791

No files matched your search

@@ -1,193 +0,0 @@
import { afterEach, describe, expect, it } from "vitest";
import { TestSqliteDatabase } from "./test-support.js";
import { FileManagerBookmarkRepository } from "../../../database/repositories/file-manager-bookmark-repository.js";
describe("FileManagerBookmarkRepository", () => {
let adapter: TestSqliteDatabase | null = null;
afterEach(async () => {
if (adapter) {
await adapter.close();
adapter = null;
}
});
async function createRepository(
onWrite?: () => void | Promise<void>,
): Promise<FileManagerBookmarkRepository> {
adapter = new TestSqliteDatabase();
const context = await adapter.connect();
await adapter.exec(`
INSERT INTO users (id, username, password_hash)
VALUES ('user-1', 'alice', 'hash'), ('user-2', 'bob', 'hash');
INSERT INTO ssh_data (id, user_id, name, ip, port, username, auth_type)
VALUES (1, 'user-1', 'one', '10.0.0.1', 22, 'root', 'password'), (2, 'user-1', 'two', '10.0.0.1', 22, 'root', 'password'), (3, 'user-2', 'other', '10.0.0.1', 22, 'root', 'password');
`);
return new FileManagerBookmarkRepository(context, onWrite);
}
it("upserts and lists recent files by last-opened time", async () => {
let writeCount = 0;
const repo = await createRepository(() => {
writeCount += 1;
});
await repo.upsertRecent(
"user-1",
{ hostId: 1, path: "/var/log/app.log" },
"2026-01-01T00:00:00.000Z",
);
await repo.upsertRecent(
"user-1",
{ hostId: 1, path: "/opt/app/config.json", name: "Config" },
"2026-01-02T00:00:00.000Z",
);
await repo.upsertRecent(
"user-1",
{ hostId: 1, path: "/var/log/app.log" },
"2026-01-03T00:00:00.000Z",
);
const recent = await repo.listRecentForHost("user-1", 1);
expect(recent.map((entry) => entry.path)).toEqual([
"/var/log/app.log",
"/opt/app/config.json",
]);
expect(recent[0].lastOpened).toBe("2026-01-03T00:00:00.000Z");
expect(recent[0].name).toBe("app.log");
expect(writeCount).toBe(3);
expect(
await repo.deleteRecentForHostPath("user-1", {
hostId: 1,
path: "/var/log/app.log",
}),
).toBe(1);
expect(writeCount).toBe(4);
});
it("creates pinned files and shortcuts without duplicating paths", async () => {
let writeCount = 0;
const repo = await createRepository(() => {
writeCount += 1;
});
expect(
await repo.createPinned(
"user-1",
{ hostId: 1, path: "/srv/www", name: "Web" },
"2026-01-01T00:00:00.000Z",
),
).toBe(true);
expect(
await repo.createPinned("user-1", { hostId: 1, path: "/srv/www" }),
).toBe(false);
expect((await repo.listPinnedForHost("user-1", 1))[0]).toMatchObject({
name: "Web",
path: "/srv/www",
});
expect(
await repo.createShortcut(
"user-1",
{ hostId: 1, path: "/etc/nginx" },
"2026-01-02T00:00:00.000Z",
),
).toBe(true);
expect(
await repo.createShortcut("user-1", { hostId: 1, path: "/etc/nginx" }),
).toBe(false);
expect((await repo.listShortcutsForHost("user-1", 1))[0]).toMatchObject({
name: "nginx",
path: "/etc/nginx",
});
expect(writeCount).toBe(2);
expect(
await repo.deletePinnedForHostPath("user-1", {
hostId: 1,
path: "/srv/www",
}),
).toBe(1);
expect(
await repo.deleteShortcutForHostPath("user-1", {
hostId: 1,
path: "/etc/nginx",
}),
).toBe(1);
expect(writeCount).toBe(4);
});
it("creates import bookmarks without duplicating user path/name pairs", async () => {
const repo = await createRepository();
await expect(
repo.createRecentForImport(
"user-1",
{ hostId: 1, path: "/tmp/a.txt", name: "A" },
"2026-01-01T00:00:00.000Z",
),
).resolves.toBe(true);
await expect(
repo.createRecentForImport("user-1", {
hostId: 2,
path: "/tmp/a.txt",
name: "A",
}),
).resolves.toBe(false);
await expect(
repo.createPinnedForImport("user-1", {
hostId: 1,
path: "/srv/www",
name: "Web",
}),
).resolves.toBe(true);
await expect(
repo.createPinnedForImport("user-1", {
hostId: 2,
path: "/srv/www",
name: "Web",
}),
).resolves.toBe(false);
await expect(
repo.createShortcutForImport("user-1", {
hostId: 1,
path: "/etc/nginx",
name: "Nginx",
}),
).resolves.toBe(true);
await expect(
repo.createShortcutForImport("user-1", {
hostId: 2,
path: "/etc/nginx",
name: "Nginx",
}),
).resolves.toBe(false);
});
it("deletes bookmarks by user, host, and host list", async () => {
let writeCount = 0;
const repo = await createRepository(() => {
writeCount += 1;
});
await repo.upsertRecent("user-1", { hostId: 1, path: "/one" });
await repo.createPinned("user-1", { hostId: 2, path: "/two" });
await repo.createShortcut("user-2", { hostId: 3, path: "/three" });
expect(writeCount).toBe(3);
expect(await repo.deleteByHostId(1)).toBe(1);
expect(await repo.deleteByHostId(1)).toBe(0);
expect(await repo.deleteByHostIds([])).toBe(0);
expect(await repo.deleteByHostIds([2])).toBe(1);
expect(writeCount).toBe(5);
expect(await repo.deleteByUserId("user-2")).toBe(1);
expect(await repo.deleteByUserId("user-2")).toBe(0);
expect(writeCount).toBe(6);
});
});
@@ -1,119 +0,0 @@
import { afterEach, describe, expect, it } from "vitest";
import { TestSqliteDatabase } from "./test-support.js";
import { TransferRecentRepository } from "../../../database/repositories/transfer-recent-repository.js";
describe("TransferRecentRepository", () => {
let adapter: TestSqliteDatabase | null = null;
afterEach(async () => {
if (adapter) {
await adapter.close();
adapter = null;
}
});
async function createRepository(
onWrite?: () => void | Promise<void>,
): Promise<TransferRecentRepository> {
adapter = new TestSqliteDatabase();
const context = await adapter.connect();
await adapter.exec(`
INSERT INTO users (id, username, password_hash)
VALUES ('user-1', 'alice', 'hash'), ('user-2', 'bob', 'hash');
INSERT INTO ssh_data (id, user_id, name, ip, port, username, auth_type)
VALUES (1, 'user-1', 'source', '10.0.0.1', 22, 'root', 'password'), (2, 'user-1', 'dest-a', '10.0.0.1', 22, 'root', 'password'), (3, 'user-1', 'dest-b', '10.0.0.1', 22, 'root', 'password'), (4, 'user-2', 'other', '10.0.0.1', 22, 'root', 'password');
`);
return new TransferRecentRepository(context, onWrite);
}
it("upserts, lists, and prunes recent transfer destinations", async () => {
let writeCount = 0;
const repo = await createRepository(() => {
writeCount += 1;
});
await repo.upsertForDestination(
"user-1",
{ sourceHostId: 1, destHostId: 2, destPath: "/var/www" },
"2026-01-01T00:00:00.000Z",
);
await repo.upsertForDestination(
"user-1",
{
sourceHostId: 1,
destHostId: 3,
destPath: "/opt/app",
destPathLabel: "App",
},
"2026-01-02T00:00:00.000Z",
);
await repo.upsertForDestination(
"user-1",
{ sourceHostId: 1, destHostId: 2, destPath: "/var/www" },
"2026-01-03T00:00:00.000Z",
);
const recent = await repo.listBySourceHost("user-1", 1);
expect(recent).toHaveLength(2);
expect(recent.map((entry) => entry.destPath)).toEqual([
"/var/www",
"/opt/app",
]);
expect(recent[0].lastUsed).toBe("2026-01-03T00:00:00.000Z");
expect(recent[0].destPathLabel).toBe("/var/www");
expect(writeCount).toBe(3);
expect(await repo.pruneSourceHost("user-1", 1, 1)).toBe(1);
expect(await repo.pruneSourceHost("user-1", 1, 1)).toBe(0);
expect(await repo.listBySourceHost("user-1", 1)).toHaveLength(1);
expect(writeCount).toBe(4);
});
it("deletes recent transfer destinations by user and host references", async () => {
let writeCount = 0;
const repo = await createRepository(() => {
writeCount += 1;
});
await repo.upsertForDestination("user-1", {
sourceHostId: 1,
destHostId: 2,
destPath: "/one",
});
await repo.upsertForDestination("user-1", {
sourceHostId: 2,
destHostId: 3,
destPath: "/two",
});
await repo.upsertForDestination("user-2", {
sourceHostId: 4,
destHostId: 1,
destPath: "/other",
});
expect(writeCount).toBe(3);
expect(await repo.deleteByHostId(1)).toBe(2);
expect(await repo.deleteByHostId(1)).toBe(0);
expect(writeCount).toBe(4);
await repo.upsertForDestination("user-1", {
sourceHostId: 1,
destHostId: 2,
destPath: "/three",
});
expect(await repo.deleteByHostIds([])).toBe(0);
expect(await repo.deleteByHostIds([2, 3])).toBe(2);
expect(writeCount).toBe(6);
await repo.upsertForDestination("user-2", {
sourceHostId: 4,
destHostId: 1,
destPath: "/last",
});
expect(await repo.deleteByUserId("user-2")).toBe(1);
expect(await repo.deleteByUserId("user-2")).toBe(0);
expect(writeCount).toBe(8);
});
});
@@ -27,7 +27,6 @@ vi.mock("../../../database/repositories/factory.js", () => ({
}),
createCurrentSettingsRepository: () => ({}),
createCurrentSshCredentialUsageRepository: deletingRepo("usage"),
createCurrentFileManagerBookmarkRepository: deletingRepo("bookmarks"),
createCurrentRecentActivityRepository: deletingRepo("activity"),
createCurrentDismissedAlertRepository: deletingRepo("alerts"),
createCurrentHostRepository: deletingRepo("hosts"),
@@ -117,7 +116,6 @@ describe("resetUserPassword", () => {
expect(outcome).toEqual({ status: "reset", dataWiped: true });
expect(calls.deletedFor).toEqual([
"usage:user-1",
"bookmarks:user-1",
"activity:user-1",
"alerts:user-1",
"hosts:user-1",