mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 06:05:24 +00:00
Require auth on /plugin-api, gate plugin enable/grant/revoke routes behind admin.plugins.manage, and thread the calling user through worker ctx.hosts/ctx.ssh calls instead of always using the install owner. Also adds the plugin permissions grant/revoke UI.
74 lines
2.4 KiB
TypeScript
74 lines
2.4 KiB
TypeScript
/**
|
|
* Wire format between a plugin worker and the main-thread broker.
|
|
*
|
|
* Everything crossing this boundary must be structured-cloneable. That is not
|
|
* just a serialisation detail: it is the reason a plugin cannot be handed a db
|
|
* handle, an ssh2 Client or a live socket by accident. Live objects stay in the
|
|
* main thread and the worker refers to them by opaque handle strings.
|
|
*/
|
|
|
|
export interface PluginRequest {
|
|
/** Correlates a reply to its request. Worker-generated, monotonic. */
|
|
id: number;
|
|
/** Dotted ctx path, e.g. "hosts.get" or "storage.set". */
|
|
method: string;
|
|
args: unknown[];
|
|
/**
|
|
* The user whose HTTP request is currently executing inside the worker, if
|
|
* any. Set by the worker's own call() from its AsyncLocalStorage context,
|
|
* never by plugin code -- no ctx method accepts a userId argument, so a
|
|
* plugin has no way to set or forge this itself. Absent for a ctx call made
|
|
* from a timer or event listener, which has no inbound request to derive an
|
|
* actor from; the broker falls back to the plugin's ownerUserId then.
|
|
*/
|
|
callerUserId?: string;
|
|
}
|
|
|
|
export interface PluginResponseOk {
|
|
id: number;
|
|
ok: true;
|
|
value: unknown;
|
|
}
|
|
|
|
export interface PluginResponseErr {
|
|
id: number;
|
|
ok: false;
|
|
error: { message: string; code?: string };
|
|
}
|
|
|
|
export type PluginResponse = PluginResponseOk | PluginResponseErr;
|
|
|
|
/** Main thread -> worker, pushed rather than requested. */
|
|
export interface PluginPush {
|
|
kind: "event" | "http" | "timer" | "deactivate";
|
|
/** Event topic, route key, or timer id depending on kind. */
|
|
key: string;
|
|
payload: unknown;
|
|
/** Set when the push expects a reply (an HTTP route invocation). */
|
|
replyTo?: number;
|
|
}
|
|
|
|
/** First message the worker receives, before any plugin code runs. */
|
|
export interface PluginBootstrapData {
|
|
pluginId: string;
|
|
pluginDir: string;
|
|
entryPath: string;
|
|
manifest: unknown;
|
|
}
|
|
|
|
export function isPluginRequest(value: unknown): value is PluginRequest {
|
|
if (!value || typeof value !== "object") return false;
|
|
const v = value as Record<string, unknown>;
|
|
return (
|
|
typeof v.id === "number" &&
|
|
typeof v.method === "string" &&
|
|
Array.isArray(v.args)
|
|
);
|
|
}
|
|
|
|
export function isPluginResponse(value: unknown): value is PluginResponse {
|
|
if (!value || typeof value !== "object") return false;
|
|
const v = value as Record<string, unknown>;
|
|
return typeof v.id === "number" && typeof v.ok === "boolean";
|
|
}
|