feat: move snippets backend and tables into a plugin (B2)

This commit is contained in:
LukeGus committed 2026-09-23 08:58:53 -05:00
1 parent fdedca39fd
commit e07221eaf3
51 files changed
+28426 -3553

No files matched your search

+15 -2
View File
@@ -135,7 +135,11 @@ export function createPluginSsh({ manifest, bag, audit }: Deps): PluginSsh {
};
open.add(dispose);
connection.client.once("close", () => open.delete(dispose));
return { ...connection, dispose };
return {
...connection,
host: connection.host as unknown as PluginSshHost,
dispose,
};
} catch (error) {
await audit("ssh_connect", describeHost(host), {
success: false,
@@ -156,6 +160,7 @@ export function createPluginSsh({ manifest, bag, audit }: Deps): PluginSsh {
return {
client: connection.client as never,
jumpClient: connection.jumpClient as never,
host: connection.host,
dispose: connection.dispose,
};
},
@@ -201,7 +206,15 @@ export function createPluginSsh({ manifest, bag, audit }: Deps): PluginSsh {
};
open.add(dispose);
client.once("close", () => open.delete(dispose));
return { client: client as never, jumpClient: null, dispose };
// A jump chain ends at a forwarding client, not a single resolved SSH
// host: report the caller's own host if it gave one, else the last hop.
const host: PluginSshHost = chainOptions?.forHost ?? {
id: jumpHosts[jumpHosts.length - 1]?.hostId ?? 0,
ip: "",
port: 22,
username: "",
};
return { client: client as never, jumpClient: null, host, dispose };
},
poolKey,
+16 -1
View File
@@ -288,7 +288,12 @@ export function createPluginContext(
// write through them. The capability and the audit line are the record
// that it did. See the header of this file.
const schema = await import("../database/db/schema.js");
return { users: schema.users, hosts: schema.hosts };
return {
users: schema.users,
hosts: schema.hosts,
roles: schema.roles,
userRoles: schema.userRoles,
};
},
{ action: "db_refs" },
);
@@ -353,6 +358,16 @@ export function createPluginContext(
const dispose = syncRegistry.registerEntity(pluginId, entity);
handle.bag.add(dispose, `sync entity ${entity.type}`);
},
recordTombstone: async (userId, entityType, syncId) => {
if (!syncId) return;
const { createCurrentSyncTombstoneRepository } =
await import("../database/repositories/factory.js");
await createCurrentSyncTombstoneRepository().record(
userId,
entityType,
syncId,
);
},
},
kv: {
+7
View File
@@ -40,6 +40,13 @@ export const TOPICS = {
hostDeleted: "host.deleted",
/** Someone logged in over SSH to a host. */
hostLogin: "host.login",
/**
* A user's encrypted data was wiped because their DEK could not be
* recovered on password reset. The user row itself survives, unlike
* user.deleted, so a plugin with a refUser() cascade still needs this:
* the row is not gone, only unrecoverable, and should go with it.
*/
userDataWiped: "user.data_wiped",
} as const;
export type EventListener = (payload: unknown) => void;