feat: finish the fleets plugin, add ctx.hosts and host-commands SDK (B4)

This commit is contained in:
LukeGus committed 2026-09-23 13:10:18 -05:00
1 parent 37c015a760
commit c381955ed0
43 files changed
+835 -1999

No files matched your search

+95
View File
@@ -10,6 +10,9 @@
import type {
PluginHosts,
PluginHostSummary,
PluginHostRecord,
PluginHostCreateInput,
PluginHostUpdateInput,
PluginHostAccess,
PluginHostShareLevel,
PluginHostShareResult,
@@ -56,6 +59,34 @@ function toSummary(host: {
};
}
function toRecord(host: Record<string, unknown>): PluginHostRecord {
return {
id: host.id as number,
userId: host.userId as string,
name: (host.name as string | null) ?? null,
ip: host.ip as string,
port: host.port as number,
username: host.username as string,
authType: host.authType as string,
credentialId: (host.credentialId as number | null) ?? null,
overrideCredentialUsername:
(host.overrideCredentialUsername as boolean | null) ?? null,
connectionType: (host.connectionType as string | null) ?? null,
tags: (host.tags as string | null) ?? null,
folder: (host.folder as string | null) ?? null,
jumpHosts: host.jumpHosts,
enableSsh: (host.enableSsh as boolean | null) ?? null,
enableRdp: (host.enableRdp as boolean | null) ?? null,
enableTerminal: (host.enableTerminal as boolean | null) ?? null,
enableFileManager: (host.enableFileManager as boolean | null) ?? null,
enableTunnel: (host.enableTunnel as boolean | null) ?? null,
enableDocker: (host.enableDocker as boolean | null) ?? null,
createdAt: (host.createdAt as string | null) ?? null,
updatedAt: (host.updatedAt as string | null) ?? null,
...host,
};
}
function actingUser(): string {
const actor = getActor();
if (!actor) {
@@ -154,6 +185,70 @@ export function createPluginHosts({ manifest, audit }: Deps): PluginHosts {
};
},
create: async (host: PluginHostCreateInput): Promise<PluginHostRecord> => {
try {
await requireWrite();
} catch (error) {
await audit("hosts_create", host.name ?? host.ip, {
success: false,
errorMessage: error instanceof Error ? error.message : String(error),
});
throw error;
}
const userId = actingUser();
const { createCurrentHostRepository } =
await import("../database/repositories/factory.js");
const created =
await createCurrentHostRepository().createEncryptedForUser(userId, {
...host,
userId,
});
await audit("hosts_create", `host ${created.id}`, { success: true });
return toRecord(created as unknown as Record<string, unknown>);
},
update: async (
hostId: number,
patch: PluginHostUpdateInput,
): Promise<PluginHostRecord | null> => {
try {
await requireWrite();
} catch (error) {
await audit("hosts_update", `host ${hostId}`, {
success: false,
errorMessage: error instanceof Error ? error.message : String(error),
});
throw error;
}
const userId = actingUser();
const { createCurrentHostRepository } =
await import("../database/repositories/factory.js");
const updated =
await createCurrentHostRepository().updateEncryptedForUser(
userId,
hostId,
patch,
);
await audit("hosts_update", `host ${hostId}`, {
success: updated !== null,
});
return updated
? toRecord(updated as unknown as Record<string, unknown>)
: null;
},
listOwned: async (): Promise<PluginHostRecord[]> => {
await requireWrite();
const userId = actingUser();
const { createCurrentHostRepository } =
await import("../database/repositories/factory.js");
const rows =
await createCurrentHostRepository().listDecryptedByUserId(userId);
return rows.map((row) =>
toRecord(row as unknown as Record<string, unknown>),
);
},
share: async (
hostId: number,
targets: PluginShareTarget[],