feat: give plugins their own settings and one settings screen

This commit is contained in:
LukeGus committed 2026-09-22 18:31:19 -05:00
1 parent cc15559ea1
commit 49f0286a14
66 files changed
+36497 -451

No files matched your search

@@ -338,120 +338,6 @@ export function registerUserSettingsRoutes(
}
});
/**
* @openapi
* /users/tailscale-settings:
* get:
* summary: Get Tailscale settings
* description: Returns whether a Tailscale API key is configured (value is masked).
* tags:
* - Users
* responses:
* 200:
* description: Tailscale settings.
* content:
* application/json:
* schema:
* type: object
* properties:
* apiKey:
* type: string
* description: Masked API key or empty string if not set.
* hasApiKey:
* type: boolean
* apiBaseUrl:
* type: string
* description: Custom control-plane API base URL (e.g. a Headscale instance), or empty string for the Tailscale default.
*/
router.get("/tailscale-settings", authenticateJWT, async (_req, res) => {
try {
const settingsRepo = createCurrentSettingsRepository();
const apiKey = (await settingsRepo.get("tailscale_api_key")) ?? "";
const apiBaseUrl =
(await settingsRepo.get("tailscale_api_base_url")) ?? "";
res.json({
apiKey: apiKey
? `${apiKey.slice(0, 6)}${"*".repeat(Math.max(0, apiKey.length - 6))}`
: "",
hasApiKey: !!apiKey,
apiBaseUrl,
});
} catch (err) {
authLogger.error("Failed to get Tailscale settings", err);
res.status(500).json({ error: "Failed to get Tailscale settings" });
}
});
/**
* @openapi
* /users/tailscale-settings:
* patch:
* summary: Update Tailscale settings (admin only)
* description: Saves or clears the Tailscale API key used for device discovery.
* tags:
* - Users
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* apiKey:
* type: string
* apiBaseUrl:
* type: string
* description: Optional custom control-plane API base URL (e.g. a Headscale instance). Leave empty to use the Tailscale default.
* responses:
* 200:
* description: Tailscale settings updated.
* 403:
* description: Not authorized.
* 500:
* description: Failed to update Tailscale settings.
*/
router.patch("/tailscale-settings", authenticateJWT, async (req, res) => {
const userId = (req as AuthenticatedRequest).userId;
try {
const actor = await getAdminActor(userId);
if (!actor) {
return res.status(403).json({ error: "Not authorized" });
}
const { apiKey, apiBaseUrl } = req.body;
if (typeof apiKey !== "string") {
return res.status(400).json({ error: "apiKey must be a string" });
}
if (apiBaseUrl !== undefined && typeof apiBaseUrl !== "string") {
return res.status(400).json({ error: "apiBaseUrl must be a string" });
}
const settingsRepo = createCurrentSettingsRepository();
await settingsRepo.set("tailscale_api_key", apiKey);
if (apiBaseUrl !== undefined) {
await settingsRepo.set(
"tailscale_api_base_url",
apiBaseUrl.trim().replace(/\/+$/, ""),
);
}
const { ipAddress, userAgent } = getRequestMeta(req);
await logAudit({
userId,
username: actor.username ?? userId,
action: "update_tailscale_settings",
resourceType: "setting",
details: JSON.stringify({ hasApiKey: !!apiKey }),
ipAddress,
userAgent,
success: true,
});
res.json({ hasApiKey: !!apiKey });
} catch (err) {
authLogger.error("Failed to update Tailscale settings", err);
res.status(500).json({ error: "Failed to update Tailscale settings" });
}
});
/**
* @openapi
* /users/command-history-enabled: