mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 22:55:24 +00:00
feat: add plugin worker protocol, bootstrap and plugin_storage table
Worker-side ctx proxy plus the message envelope it talks over, and the per-plugin key/value table behind ctx.storage with migrations for all three dialects. Rows cascade with the plugin so uninstalling leaves nothing behind.
This commit is contained in:
1 parent
a1805cb7ae
commit
248698f62d
19 files changed
+30035
No files matched your search
@@ -33,6 +33,7 @@ import { NetworkTopologyRepository } from "./network-topology-repository.js";
|
||||
import { OpenTabRepository } from "./open-tab-repository.js";
|
||||
import { OpksshTokenRepository } from "./opkssh-token-repository.js";
|
||||
import { PluginRepository } from "./plugin-repository.js";
|
||||
import { PluginStorageRepository } from "./plugin-storage-repository.js";
|
||||
import { PluginPermissionGrantRepository } from "./plugin-permission-grant-repository.js";
|
||||
import { PluginRegistryRepository } from "./plugin-registry-repository.js";
|
||||
import { PluginInstallCountRepository } from "./plugin-install-count-repository.js";
|
||||
@@ -385,6 +386,13 @@ export function createCurrentPluginPermissionGrantRepository(): PluginPermission
|
||||
);
|
||||
}
|
||||
|
||||
export function createCurrentPluginStorageRepository(): PluginStorageRepository {
|
||||
return new PluginStorageRepository(
|
||||
createCurrentRepositoryContext(),
|
||||
createCurrentRepositoryWriteHook("plugin_storage_repository_write"),
|
||||
);
|
||||
}
|
||||
|
||||
export function createCurrentPluginRegistryRepository(): PluginRegistryRepository {
|
||||
return new PluginRegistryRepository(
|
||||
createCurrentRepositoryContext(),
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { pluginStorage } from "../db/schema.js";
|
||||
import type { DatabaseContext } from "./database-context.js";
|
||||
import { rowsAffected } from "./mutation-result.js";
|
||||
import { insertReturning, updateReturning } from "./returning.js";
|
||||
|
||||
export type PluginStorageRecord = typeof pluginStorage.$inferSelect;
|
||||
|
||||
export class PluginStorageRepository {
|
||||
constructor(
|
||||
private readonly context: DatabaseContext,
|
||||
private readonly onWrite?: () => void | Promise<void>,
|
||||
) {}
|
||||
|
||||
async get(pluginId: string, key: string): Promise<string | null> {
|
||||
const rows = await this.context.drizzle
|
||||
.select()
|
||||
.from(pluginStorage)
|
||||
.where(
|
||||
and(eq(pluginStorage.pluginId, pluginId), eq(pluginStorage.key, key)),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
return rows[0]?.value ?? null;
|
||||
}
|
||||
|
||||
async listKeys(pluginId: string): Promise<string[]> {
|
||||
const rows = await this.context.drizzle
|
||||
.select()
|
||||
.from(pluginStorage)
|
||||
.where(eq(pluginStorage.pluginId, pluginId));
|
||||
|
||||
return rows.map((row) => row.key);
|
||||
}
|
||||
|
||||
async set(
|
||||
pluginId: string,
|
||||
key: string,
|
||||
value: string,
|
||||
now = new Date().toISOString(),
|
||||
): Promise<void> {
|
||||
const existing = await this.context.drizzle
|
||||
.select()
|
||||
.from(pluginStorage)
|
||||
.where(
|
||||
and(eq(pluginStorage.pluginId, pluginId), eq(pluginStorage.key, key)),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (existing[0]) {
|
||||
await updateReturning(
|
||||
this.context,
|
||||
pluginStorage,
|
||||
{ value, updatedAt: now },
|
||||
and(eq(pluginStorage.pluginId, pluginId), eq(pluginStorage.key, key))!,
|
||||
);
|
||||
} else {
|
||||
await insertReturning(this.context, pluginStorage, {
|
||||
pluginId,
|
||||
key,
|
||||
value,
|
||||
updatedAt: now,
|
||||
});
|
||||
}
|
||||
|
||||
await this.afterWrite();
|
||||
}
|
||||
|
||||
async delete(pluginId: string, key: string): Promise<boolean> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(pluginStorage)
|
||||
.where(
|
||||
and(eq(pluginStorage.pluginId, pluginId), eq(pluginStorage.key, key)),
|
||||
);
|
||||
|
||||
const affected = rowsAffected(result) > 0;
|
||||
if (affected) await this.afterWrite();
|
||||
return affected;
|
||||
}
|
||||
|
||||
async deleteByPlugin(pluginId: string): Promise<number> {
|
||||
const result = await this.context.drizzle
|
||||
.delete(pluginStorage)
|
||||
.where(eq(pluginStorage.pluginId, pluginId));
|
||||
|
||||
if (rowsAffected(result) > 0) await this.afterWrite();
|
||||
return rowsAffected(result);
|
||||
}
|
||||
|
||||
private async afterWrite(): Promise<void> {
|
||||
await this.onWrite?.();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user