feat: Implement temporary API key based on system UUID for UI access

This commit introduces a new authentication mechanism for the web UI.
Instead of requiring a pre-configured API key, a temporary API key is
generated based on the system's UUID. This key is passed to the UI
as a URL parameter and used for API requests.

Changes:
- Added a new utility to get the system UUID and generate a temporary API key.
- Modified the `ccr ui` command to generate and pass the temporary API key.
- Updated the authentication middleware to validate the temporary API key.
- Adjusted the frontend to use the temporary API key from the URL.
- Added a dedicated endpoint to test API access without modifying data.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
musistudio
2025-08-07 15:00:42 +08:00
parent 4334f40926
commit 9cd5587f52
10 changed files with 288 additions and 17 deletions

View File

@@ -8,8 +8,18 @@ import fastifyStatic from "@fastify/static";
export const createServer = (config: any): Server => {
const server = new Server(config);
// Add endpoint to read config.json
server.app.get("/api/config", async () => {
// Add endpoint to read config.json with access control
server.app.get("/api/config", async (req, reply) => {
// Get access level from request (set by auth middleware)
const accessLevel = (req as any).accessLevel || "restricted";
// If restricted access, return 401
if (accessLevel === "restricted") {
reply.status(401).send("API key required to access configuration");
return;
}
// For full access (including temp API key), return complete config
return await readConfigFile();
});
@@ -25,8 +35,15 @@ export const createServer = (config: any): Server => {
return { transformers: transformerList };
});
// Add endpoint to save config.json
server.app.post("/api/config", async (req) => {
// Add endpoint to save config.json with access control
server.app.post("/api/config", async (req, reply) => {
// Only allow full access users to save config
const accessLevel = (req as any).accessLevel || "restricted";
if (accessLevel !== "full") {
reply.status(403).send("Full access required to modify configuration");
return;
}
const newConfig = req.body;
// Backup existing config file if it exists
@@ -39,9 +56,29 @@ export const createServer = (config: any): Server => {
await writeConfigFile(newConfig);
return { success: true, message: "Config saved successfully" };
});
// Add endpoint for testing full access without modifying config
server.app.post("/api/config/test", async (req, reply) => {
// Only allow full access users to test config access
const accessLevel = (req as any).accessLevel || "restricted";
if (accessLevel !== "full") {
reply.status(403).send("Full access required to test configuration access");
return;
}
// Return success without modifying anything
return { success: true, message: "Access granted" };
});
// Add endpoint to restart the service
server.app.post("/api/restart", async (_, reply) => {
// Add endpoint to restart the service with access control
server.app.post("/api/restart", async (req, reply) => {
// Only allow full access users to restart service
const accessLevel = (req as any).accessLevel || "restricted";
if (accessLevel !== "full") {
reply.status(403).send("Full access required to restart service");
return;
}
reply.send({ success: true, message: "Service restart initiated" });
// Restart the service after a short delay to allow response to be sent