feat: disable plugins by default, require admin approval

This commit is contained in:
Linus Rath
2026-04-02 13:50:02 +02:00
parent 9ee25c930e
commit 6ee0f6a40a
8 changed files with 84 additions and 21 deletions
+7 -7
View File
@@ -27,7 +27,7 @@ import {
import { toast as appToast } from '@/stores/toast-store';
import { useAuthStore } from '@/stores/auth-store';
// ─── Permission helpers ──────────────────────────────────────
// --- Permission helpers --------------------------------------
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function getPluginExternals(): any {
@@ -61,7 +61,7 @@ function guardedHook<T extends (...args: never[]) => unknown>(
return bus.register(plugin.id, handler, order);
}
// ─── Plugin-scoped storage ───────────────────────────────────
// --- Plugin-scoped storage -----------------------------------
function createPluginStorage(pluginId: string) {
const prefix = `plugin:${pluginId}:`;
@@ -93,7 +93,7 @@ function createPluginStorage(pluginId: string) {
};
}
// ─── Plugin-scoped logger ────────────────────────────────────
// --- Plugin-scoped logger ------------------------------------
function createPluginLogger(pluginId: string) {
const tag = `[plugin:${pluginId}]`;
@@ -105,7 +105,7 @@ function createPluginLogger(pluginId: string) {
};
}
// ─── PluginAPI interface ─────────────────────────────────────
// --- PluginAPI interface -------------------------------------
export interface PluginAPI {
plugin: { id: string; version: string; settings: Record<string, unknown> };
@@ -316,7 +316,7 @@ export interface PluginHooksAPI {
onSidebarAppChange: (handler: (...args: unknown[]) => unknown) => Disposable;
}
// ─── Permission mapping for hooks ────────────────────────────
// --- Permission mapping for hooks ----------------------------
const HOOK_PERMISSIONS: Record<string, Permission> = {
// Email
@@ -465,7 +465,7 @@ const HOOK_BUSES: Record<string, { register: (pluginId: string, handler: (...arg
...Object.fromEntries(Object.entries(sidebarAppHooks)),
};
// ─── Slot registration bridge ────────────────────────────────
// --- Slot registration bridge --------------------------------
// Lazy import to avoid circular dependency — plugin-store imports plugin-api indirectly
let registerSlotFn: ((name: SlotName, reg: { pluginId: string; component: React.ComponentType<Record<string, unknown>>; order: number }) => Disposable) | null = null;
@@ -487,7 +487,7 @@ function registerSlot(
return registerSlotFn(slotName, { pluginId, component, order });
}
// ─── Factory ─────────────────────────────────────────────────
// --- Factory -------------------------------------------------
export function createPluginAPI(plugin: InstalledPlugin): PluginAPI {
// Build hooks proxy — each hook method checks permission and registers on the right bus