395ed821b7
- CRITICAL: Fix command injection in discoveryService (execFile instead of exec, path validation regex) - CRITICAL: Add Zod validation on discover API endpoint - HIGH: Add Zod validation on discover/approve endpoint - HIGH: Add array length limits to import schema (1000/100/100) - HIGH: Fix theme broadcast echo loop (setTimeout vs queueMicrotask) - MEDIUM: Singleton BroadcastChannel instead of create-per-send - MEDIUM: Exclude sensitive APIs from service worker cache - MEDIUM: Fix TypeScript cast errors in exportService tests
53 lines
1.6 KiB
TypeScript
53 lines
1.6 KiB
TypeScript
import { json } from '@sveltejs/kit';
|
|
import { z } from 'zod';
|
|
import type { RequestHandler } from './$types';
|
|
import { requireAdmin } from '$lib/server/middleware/authorize.js';
|
|
import { discoverAll, type DiscoveryConfig } from '$lib/server/services/discoveryService.js';
|
|
import { success, error } from '$lib/server/utils/response.js';
|
|
|
|
const discoverConfigSchema = z.object({
|
|
dockerSocketPath: z.string().regex(/^[\w/.:-]+$/).optional(),
|
|
traefikApiUrl: z.string().url().optional()
|
|
});
|
|
|
|
/**
|
|
* POST /api/admin/discover — Scan Docker and Traefik for services. Admin only.
|
|
*
|
|
* Body: { dockerSocketPath?: string, traefikApiUrl?: string }
|
|
*/
|
|
export const POST: RequestHandler = async (event) => {
|
|
requireAdmin(event);
|
|
|
|
let rawBody: unknown;
|
|
try {
|
|
rawBody = await event.request.json();
|
|
} catch {
|
|
return json(error('Invalid JSON body'), { status: 400 });
|
|
}
|
|
|
|
const parsed = discoverConfigSchema.safeParse(rawBody);
|
|
if (!parsed.success) {
|
|
return json(error(`Validation failed: ${parsed.error.issues.map((i) => i.message).join(', ')}`), { status: 400 });
|
|
}
|
|
|
|
const config: DiscoveryConfig = {
|
|
dockerSocketPath: parsed.data.dockerSocketPath || undefined,
|
|
traefikApiUrl: parsed.data.traefikApiUrl || undefined
|
|
};
|
|
|
|
if (!config.dockerSocketPath && !config.traefikApiUrl) {
|
|
return json(
|
|
error('At least one discovery source must be configured (dockerSocketPath or traefikApiUrl)'),
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
try {
|
|
const result = await discoverAll(config);
|
|
return json(success(result));
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : 'Discovery scan failed';
|
|
return json(error(message), { status: 500 });
|
|
}
|
|
};
|