feat(desktop): implement workspace foundation + desktop-client skeleton
Plan 0 (workspaces): add workspaces + workspace_memberships schema, extend JWT/Actor/claims with primary_workspace_id, seed default workspace per tenant, thread workspace_id through tenant monitoring quota. Plan A (desktop skeleton): new Electron app (apps/desktop-client) with main/ preload/renderer, shared Vue component package (packages/ui-shared), and server surface — desktop client registration + token rotation + heartbeat, SSE task event stream, desktop accounts/tasks/content handlers, publish job endpoint, and supporting repositories, services, sqlc queries, and migrations. Hard cutover per plan: remove browser-extension monitoring callback endpoints, stub legacy media API in admin-web, and delete monitoring_callback_handler.go.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { WebContentsView } from "electron/main";
|
||||
import type { WebContentsView as ElectronWebContentsView } from "electron/main";
|
||||
|
||||
export interface HotViewHandle {
|
||||
accountId: string;
|
||||
view: ElectronWebContentsView;
|
||||
activatedAt: number;
|
||||
}
|
||||
|
||||
const hotViews = new Map<string, HotViewHandle>();
|
||||
|
||||
export function acquireHotView(accountId: string): HotViewHandle {
|
||||
const existing = hotViews.get(accountId);
|
||||
if (existing) {
|
||||
existing.activatedAt = Date.now();
|
||||
return existing;
|
||||
}
|
||||
|
||||
const handle: HotViewHandle = {
|
||||
accountId,
|
||||
view: new WebContentsView({
|
||||
webPreferences: {
|
||||
sandbox: true,
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
},
|
||||
}),
|
||||
activatedAt: Date.now(),
|
||||
};
|
||||
hotViews.set(accountId, handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
export function demoteView(accountId: string): void {
|
||||
const handle = hotViews.get(accountId);
|
||||
if (!handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!handle.view.webContents.isDestroyed()) {
|
||||
handle.view.webContents.close();
|
||||
}
|
||||
hotViews.delete(accountId);
|
||||
}
|
||||
|
||||
export function listHotViews(): Array<Pick<HotViewHandle, "accountId" | "activatedAt">> {
|
||||
return [...hotViews.values()].map(({ accountId, activatedAt }) => ({
|
||||
accountId,
|
||||
activatedAt,
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user