feat(api,generator): preview endpoint + spec cache + audit-log writes

- POST /v1/servers/preview runs Claude synchronously, validates output, caches spec
  in Redis under preview:<id> with 5min TTL, returns previewId+spec+detectedSecrets.
- POST /v1/servers accepts optional previewId; worker reuses the cached spec if
  the entry is still present, otherwise regenerates fresh. Skips the second
  Claude round-trip (~30s saved on the demoable path).
- audit() helper writes auth.login, auth.logout, server.create, server.iterate,
  server.delete to audit_log with ip, metadata, resourceId.
- GET /v1/me/org returns organization + members list for the settings page.
- GET /v1/audit?limit=&action=&resourceType= returns scoped audit entries.
This commit is contained in:
Marco Sadjadi
2026-05-19 18:08:29 +02:00
parent bb0d9c2cda
commit 1c92964bbd
9 changed files with 270 additions and 6 deletions

View File

@@ -120,9 +120,34 @@ export const CreateServerInput = z.object({
.regex(/^[a-z][a-z0-9-]*$/, 'lowercase, hyphenated'),
prompt: z.string().min(10).max(8000),
secrets: z.record(z.string(), z.string()).default({}),
previewId: z.string().min(1).max(64).optional(),
});
export type CreateServerInput = z.infer<typeof CreateServerInput>;
export const PreviewInput = z.object({
prompt: z.string().min(10).max(8000),
});
export type PreviewInput = z.infer<typeof PreviewInput>;
export const PreviewResult = z.object({
previewId: z.string(),
source: z.enum(['claude', 'mock']),
spec: z.object({
name: z.string(),
description: z.string().optional(),
tools: z.array(
z.object({
name: z.string(),
description: z.string(),
inputSchema: z.record(z.string(), z.unknown()),
}),
),
requiredSecrets: z.array(z.string()),
scopes: z.array(z.string()),
}),
});
export type PreviewResult = z.infer<typeof PreviewResult>;
export const IterateServerInput = z.object({
prompt: z.string().min(10).max(8000),
secrets: z.record(z.string(), z.string()).default({}),