mirror of
https://github.com/khoaliber/dockhand.git
synced 2026-03-06 13:21:53 +00:00
Initial commit
This commit is contained in:
195
routes/api/settings/scanner/+server.ts
Normal file
195
routes/api/settings/scanner/+server.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
import { json, type RequestHandler } from '@sveltejs/kit';
|
||||
import { getEnvSetting, setEnvSetting, getEnvironment } from '$lib/server/db';
|
||||
import {
|
||||
checkScannerAvailability,
|
||||
getScannerVersions,
|
||||
checkScannerUpdates,
|
||||
cleanupScannerVolumes,
|
||||
getGlobalScannerDefaults,
|
||||
type ScannerType
|
||||
} from '$lib/server/scanner';
|
||||
import { removeImage, listImages } from '$lib/server/docker';
|
||||
import { authorize } from '$lib/server/authorize';
|
||||
|
||||
export interface ScannerSettings {
|
||||
scanner: ScannerType;
|
||||
grypeArgs: string;
|
||||
trivyArgs: string;
|
||||
}
|
||||
|
||||
export const GET: RequestHandler = async ({ url, cookies }) => {
|
||||
const auth = await authorize(cookies);
|
||||
|
||||
const envId = url.searchParams.get('env');
|
||||
const parsedEnvId = envId ? parseInt(envId) : undefined;
|
||||
const checkUpdates = url.searchParams.get('checkUpdates') === 'true';
|
||||
const settingsOnly = url.searchParams.get('settingsOnly') === 'true';
|
||||
|
||||
// Permission check with environment context
|
||||
if (auth.authEnabled && !await auth.can('settings', 'view', parsedEnvId)) {
|
||||
return json({ error: 'Permission denied' }, { status: 403 });
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// Get global defaults from general settings (used for reset to defaults)
|
||||
const globalDefaults = await getGlobalScannerDefaults();
|
||||
|
||||
// Get environment-specific settings (falls back to global defaults if not set)
|
||||
const settings: ScannerSettings = {
|
||||
scanner: await getEnvSetting('vulnerability_scanner', parsedEnvId) || 'none',
|
||||
grypeArgs: await getEnvSetting('grype_cli_args', parsedEnvId) || globalDefaults.grypeArgs,
|
||||
trivyArgs: await getEnvSetting('trivy_cli_args', parsedEnvId) || globalDefaults.trivyArgs
|
||||
};
|
||||
|
||||
// Fast path: return just settings without Docker checks
|
||||
if (settingsOnly) {
|
||||
return json({
|
||||
settings,
|
||||
defaults: globalDefaults
|
||||
});
|
||||
}
|
||||
|
||||
// Check scanner availability and versions in parallel
|
||||
const [availability, versions] = await Promise.all([
|
||||
checkScannerAvailability(parsedEnvId),
|
||||
getScannerVersions(parsedEnvId)
|
||||
]);
|
||||
|
||||
// Optionally check for updates (slower operation)
|
||||
let updates = undefined;
|
||||
if (checkUpdates) {
|
||||
updates = await checkScannerUpdates(parsedEnvId);
|
||||
}
|
||||
|
||||
return json({
|
||||
settings,
|
||||
availability,
|
||||
versions,
|
||||
updates,
|
||||
defaults: globalDefaults
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to get scanner settings:', error);
|
||||
return json({ error: 'Failed to get scanner settings' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
|
||||
export const POST: RequestHandler = async ({ request, url, cookies }) => {
|
||||
const auth = await authorize(cookies);
|
||||
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { scanner, grypeArgs, trivyArgs, envId } = body;
|
||||
const parsedEnvId = envId ? parseInt(envId) : undefined;
|
||||
|
||||
// Permission check with environment context
|
||||
if (auth.authEnabled && !await auth.can('settings', 'edit', parsedEnvId)) {
|
||||
return json({ error: 'Permission denied' }, { status: 403 });
|
||||
}
|
||||
|
||||
// Validate scanner type
|
||||
const validScanners: ScannerType[] = ['none', 'grype', 'trivy', 'both'];
|
||||
if (scanner && !validScanners.includes(scanner)) {
|
||||
return json({ error: 'Invalid scanner type' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Save environment-specific settings
|
||||
if (scanner !== undefined) {
|
||||
await setEnvSetting('vulnerability_scanner', scanner, parsedEnvId);
|
||||
}
|
||||
if (grypeArgs !== undefined) {
|
||||
await setEnvSetting('grype_cli_args', grypeArgs, parsedEnvId);
|
||||
}
|
||||
if (trivyArgs !== undefined) {
|
||||
await setEnvSetting('trivy_cli_args', trivyArgs, parsedEnvId);
|
||||
}
|
||||
|
||||
// Get global defaults for fallback
|
||||
const globalDefaults = await getGlobalScannerDefaults();
|
||||
|
||||
return json({
|
||||
success: true,
|
||||
settings: {
|
||||
scanner: await getEnvSetting('vulnerability_scanner', parsedEnvId) || 'none',
|
||||
grypeArgs: await getEnvSetting('grype_cli_args', parsedEnvId) || globalDefaults.grypeArgs,
|
||||
trivyArgs: await getEnvSetting('trivy_cli_args', parsedEnvId) || globalDefaults.trivyArgs
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to save scanner settings:', error);
|
||||
return json({ error: 'Failed to save scanner settings' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
|
||||
export const DELETE: RequestHandler = async ({ url, cookies }) => {
|
||||
const auth = await authorize(cookies);
|
||||
|
||||
const removeImagesFlag = url.searchParams.get('removeImages') === 'true';
|
||||
const scanner = url.searchParams.get('scanner'); // 'grype', 'trivy', or null for both
|
||||
const envId = url.searchParams.get('env');
|
||||
const parsedEnvId = envId ? parseInt(envId) : undefined;
|
||||
|
||||
// Permission check with environment context
|
||||
if (auth.authEnabled && !await auth.can('settings', 'edit', parsedEnvId)) {
|
||||
return json({ error: 'Permission denied' }, { status: 403 });
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if (!removeImagesFlag) {
|
||||
return json({ error: 'removeImages parameter required' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (!parsedEnvId) {
|
||||
return json({ error: 'Environment ID required' }, { status: 400 });
|
||||
}
|
||||
const env = await getEnvironment(parsedEnvId);
|
||||
if (!env) {
|
||||
return json({ error: 'Environment not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const images = await listImages(parsedEnvId);
|
||||
|
||||
const removed: string[] = [];
|
||||
const errors: string[] = [];
|
||||
|
||||
// Determine which images to remove
|
||||
const scannersToRemove: ('grype' | 'trivy')[] =
|
||||
scanner === 'grype' ? ['grype'] :
|
||||
scanner === 'trivy' ? ['trivy'] :
|
||||
['grype', 'trivy'];
|
||||
|
||||
for (const scannerType of scannersToRemove) {
|
||||
const imageName = scannerType === 'grype' ? 'anchore/grype' : 'aquasec/trivy';
|
||||
|
||||
// Find the image
|
||||
const image = images.find((img) =>
|
||||
img.tags?.some((tag: string) => tag.includes(imageName))
|
||||
);
|
||||
|
||||
if (image) {
|
||||
try {
|
||||
await removeImage(image.id, true, parsedEnvId);
|
||||
removed.push(scannerType);
|
||||
} catch (err) {
|
||||
const errMsg = err instanceof Error ? err.message : String(err);
|
||||
console.error(`Failed to remove ${scannerType} image:`, err);
|
||||
errors.push(`${scannerType}: ${errMsg}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also cleanup scanner database volumes
|
||||
await cleanupScannerVolumes(parsedEnvId);
|
||||
|
||||
return json({
|
||||
success: true,
|
||||
removed,
|
||||
errors: errors.length > 0 ? errors : undefined
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to remove scanner images:', error);
|
||||
return json({ error: 'Failed to remove scanner images' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user