1.0.13
This commit is contained in:
1 parent
a88d3d5788
commit
8f3a7eb435
96 files changed
+5235
-1870
No files matched your search
@@ -1,7 +1,8 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { getEnvironments, getEnvironmentByName, createEnvironment, assignUserRole, getRoleByName, getEnvironmentPublicIps, setEnvironmentPublicIp, getEnvUpdateCheckSettings, getEnvironmentTimezone, type Environment } from '$lib/server/db';
|
||||
import { getEnvironments, getEnvironmentByName, createEnvironment, assignUserRole, getRoleByName, getEnvironmentPublicIps, setEnvironmentPublicIp, getEnvUpdateCheckSettings, getEnvironmentTimezone, getImagePruneSettings, type Environment } from '$lib/server/db';
|
||||
import { authorize } from '$lib/server/authorize';
|
||||
import { auditEnvironment } from '$lib/server/audit';
|
||||
import { refreshSubprocessEnvironments } from '$lib/server/subprocess-manager';
|
||||
import { serializeLabels, parseLabels, MAX_LABELS } from '$lib/utils/label-colors';
|
||||
import { cleanPem } from '$lib/utils/pem';
|
||||
@@ -36,16 +37,18 @@ export const GET: RequestHandler = async ({ cookies }) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse labels from JSON string to array, add public IPs, update check settings, and timezone
|
||||
// Parse labels from JSON string to array, add public IPs, update check settings, image prune settings, and timezone
|
||||
const envWithParsedLabels = await Promise.all(environments.map(async env => {
|
||||
const updateSettings = updateCheckSettingsMap.get(env.id);
|
||||
const timezone = await getEnvironmentTimezone(env.id);
|
||||
const imagePruneSettings = await getImagePruneSettings(env.id);
|
||||
return {
|
||||
...env,
|
||||
labels: parseLabels(env.labels as string | null),
|
||||
publicIp: publicIps[env.id.toString()] || null,
|
||||
updateCheckEnabled: updateSettings?.enabled || false,
|
||||
updateCheckAutoUpdate: updateSettings?.autoUpdate || false,
|
||||
imagePruneEnabled: imagePruneSettings?.enabled || false,
|
||||
timezone
|
||||
};
|
||||
}));
|
||||
@@ -57,7 +60,8 @@ export const GET: RequestHandler = async ({ cookies }) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const POST: RequestHandler = async ({ request, cookies }) => {
|
||||
export const POST: RequestHandler = async (event) => {
|
||||
const { request, cookies } = event;
|
||||
const auth = await authorize(cookies);
|
||||
if (auth.authEnabled && !await auth.can('environments', 'create')) {
|
||||
return json({ error: 'Permission denied' }, { status: 403 });
|
||||
@@ -128,6 +132,9 @@ export const POST: RequestHandler = async ({ request, cookies }) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Audit log
|
||||
await auditEnvironment(event, 'create', env.id, env.name);
|
||||
|
||||
return json(env);
|
||||
} catch (error) {
|
||||
console.error('Failed to create environment:', error);
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { getEnvironment, updateEnvironment, deleteEnvironment, getEnvironmentPublicIps, setEnvironmentPublicIp, deleteEnvironmentPublicIp, deleteEnvUpdateCheckSettings, getGitStacksForEnvironmentOnly, deleteGitStack } from '$lib/server/db';
|
||||
import { getEnvironment, updateEnvironment, deleteEnvironment, getEnvironmentPublicIps, setEnvironmentPublicIp, deleteEnvironmentPublicIp, deleteEnvUpdateCheckSettings, deleteImagePruneSettings, getGitStacksForEnvironmentOnly, deleteGitStack } from '$lib/server/db';
|
||||
import { clearDockerClientCache } from '$lib/server/docker';
|
||||
import { deleteGitStackFiles } from '$lib/server/git';
|
||||
import { authorize } from '$lib/server/authorize';
|
||||
import { auditEnvironment } from '$lib/server/audit';
|
||||
import { refreshSubprocessEnvironments } from '$lib/server/subprocess-manager';
|
||||
import { serializeLabels, parseLabels, MAX_LABELS } from '$lib/utils/label-colors';
|
||||
import { cleanPem } from '$lib/utils/pem';
|
||||
import { unregisterSchedule } from '$lib/server/scheduler';
|
||||
import { closeEdgeConnection } from '$lib/server/hawser';
|
||||
import { computeAuditDiff } from '$lib/utils/diff';
|
||||
|
||||
export const GET: RequestHandler = async ({ params, cookies }) => {
|
||||
const auth = await authorize(cookies);
|
||||
@@ -40,7 +42,8 @@ export const GET: RequestHandler = async ({ params, cookies }) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const PUT: RequestHandler = async ({ params, request, cookies }) => {
|
||||
export const PUT: RequestHandler = async (event) => {
|
||||
const { params, request, cookies } = event;
|
||||
const auth = await authorize(cookies);
|
||||
if (auth.authEnabled && !await auth.can('environments', 'edit')) {
|
||||
return json({ error: 'Permission denied' }, { status: 403 });
|
||||
@@ -48,6 +51,13 @@ export const PUT: RequestHandler = async ({ params, request, cookies }) => {
|
||||
|
||||
try {
|
||||
const id = parseInt(params.id);
|
||||
|
||||
// Get old values before update for diff
|
||||
const oldEnv = await getEnvironment(id);
|
||||
if (!oldEnv) {
|
||||
return json({ error: 'Environment not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const data = await request.json();
|
||||
|
||||
// Clear cached Docker client before updating
|
||||
@@ -95,6 +105,14 @@ export const PUT: RequestHandler = async ({ params, request, cookies }) => {
|
||||
const publicIps = await getEnvironmentPublicIps();
|
||||
const publicIp = publicIps[id.toString()] || null;
|
||||
|
||||
// Compute diff for audit (exclude sensitive TLS fields)
|
||||
const diff = computeAuditDiff(oldEnv, env, {
|
||||
excludeFields: ['tlsCa', 'tlsCert', 'tlsKey', 'hawserToken', 'labels']
|
||||
});
|
||||
|
||||
// Audit log
|
||||
await auditEnvironment(event, 'update', env.id, env.name, diff);
|
||||
|
||||
// Parse labels from JSON string to array
|
||||
return json({
|
||||
...env,
|
||||
@@ -107,7 +125,8 @@ export const PUT: RequestHandler = async ({ params, request, cookies }) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const DELETE: RequestHandler = async ({ params, cookies }) => {
|
||||
export const DELETE: RequestHandler = async (event) => {
|
||||
const { params, cookies } = event;
|
||||
const auth = await authorize(cookies);
|
||||
if (auth.authEnabled && !await auth.can('environments', 'delete')) {
|
||||
return json({ error: 'Permission denied' }, { status: 403 });
|
||||
@@ -116,6 +135,12 @@ export const DELETE: RequestHandler = async ({ params, cookies }) => {
|
||||
try {
|
||||
const id = parseInt(params.id);
|
||||
|
||||
// Get environment name before deletion for audit log
|
||||
const env = await getEnvironment(id);
|
||||
if (!env) {
|
||||
return json({ error: 'Environment not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
// Close Edge connection if this is a Hawser Edge environment
|
||||
// This rejects any pending requests and closes the WebSocket
|
||||
closeEdgeConnection(id);
|
||||
@@ -131,7 +156,7 @@ export const DELETE: RequestHandler = async ({ params, cookies }) => {
|
||||
unregisterSchedule(stack.id, 'git_stack_sync');
|
||||
}
|
||||
// Delete git stack files from filesystem
|
||||
deleteGitStackFiles(stack.id);
|
||||
await deleteGitStackFiles(stack.id, stack.stackName, stack.environmentId);
|
||||
// Delete git stack from database
|
||||
await deleteGitStack(stack.id);
|
||||
}
|
||||
@@ -149,9 +174,16 @@ export const DELETE: RequestHandler = async ({ params, cookies }) => {
|
||||
await deleteEnvUpdateCheckSettings(id);
|
||||
unregisterSchedule(id, 'env_update_check');
|
||||
|
||||
// Clean up image prune settings and unregister schedule
|
||||
await deleteImagePruneSettings(id);
|
||||
unregisterSchedule(id, 'image_prune');
|
||||
|
||||
// Notify subprocesses to stop collecting from deleted environment
|
||||
refreshSubprocessEnvironments();
|
||||
|
||||
// Audit log
|
||||
await auditEnvironment(event, 'delete', id, env.name);
|
||||
|
||||
return json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Failed to delete environment:', error);
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { authorize } from '$lib/server/authorize';
|
||||
import {
|
||||
getImagePruneSettings,
|
||||
setImagePruneSettings,
|
||||
getEnvironment
|
||||
} from '$lib/server/db';
|
||||
import { registerSchedule, unregisterSchedule, triggerImagePrune } from '$lib/server/scheduler';
|
||||
|
||||
/**
|
||||
* Get image prune settings for an environment.
|
||||
*/
|
||||
export const GET: RequestHandler = async ({ params, cookies }) => {
|
||||
const auth = await authorize(cookies);
|
||||
if (auth.authEnabled && !await auth.can('environments', 'view')) {
|
||||
return json({ error: 'Permission denied' }, { status: 403 });
|
||||
}
|
||||
|
||||
try {
|
||||
const id = parseInt(params.id);
|
||||
|
||||
// Verify environment exists
|
||||
const env = await getEnvironment(id);
|
||||
if (!env) {
|
||||
return json({ error: 'Environment not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const settings = await getImagePruneSettings(id);
|
||||
|
||||
return json({
|
||||
settings: settings || {
|
||||
enabled: false,
|
||||
cronExpression: '0 3 * * 0', // Default: 3 AM Sunday
|
||||
pruneMode: 'dangling'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to get image prune settings:', error);
|
||||
return json({ error: 'Failed to get image prune settings' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Save image prune settings for an environment.
|
||||
*/
|
||||
export const POST: RequestHandler = async ({ params, request, cookies }) => {
|
||||
const auth = await authorize(cookies);
|
||||
if (auth.authEnabled && !await auth.can('environments', 'edit')) {
|
||||
return json({ error: 'Permission denied' }, { status: 403 });
|
||||
}
|
||||
|
||||
try {
|
||||
const id = parseInt(params.id);
|
||||
|
||||
// Verify environment exists
|
||||
const env = await getEnvironment(id);
|
||||
if (!env) {
|
||||
return json({ error: 'Environment not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const data = await request.json();
|
||||
|
||||
// Get existing settings to preserve lastPruned and lastResult
|
||||
const existingSettings = await getImagePruneSettings(id);
|
||||
|
||||
const settings = {
|
||||
enabled: data.enabled ?? false,
|
||||
cronExpression: data.cronExpression || '0 3 * * 0',
|
||||
pruneMode: data.pruneMode || 'dangling',
|
||||
lastPruned: existingSettings?.lastPruned,
|
||||
lastResult: existingSettings?.lastResult
|
||||
};
|
||||
|
||||
// Save settings to database
|
||||
await setImagePruneSettings(id, settings);
|
||||
|
||||
// Register or unregister schedule based on enabled state
|
||||
if (settings.enabled) {
|
||||
await registerSchedule(id, 'image_prune', id);
|
||||
} else {
|
||||
unregisterSchedule(id, 'image_prune');
|
||||
}
|
||||
|
||||
return json({ success: true, settings });
|
||||
} catch (error) {
|
||||
console.error('Failed to save image prune settings:', error);
|
||||
return json({ error: 'Failed to save image prune settings' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Manually trigger image prune for an environment.
|
||||
*/
|
||||
export const PUT: RequestHandler = async ({ params, cookies }) => {
|
||||
const auth = await authorize(cookies);
|
||||
if (auth.authEnabled && !await auth.can('environments', 'edit')) {
|
||||
return json({ error: 'Permission denied' }, { status: 403 });
|
||||
}
|
||||
|
||||
try {
|
||||
const id = parseInt(params.id);
|
||||
|
||||
// Verify environment exists
|
||||
const env = await getEnvironment(id);
|
||||
if (!env) {
|
||||
return json({ error: 'Environment not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const result = await triggerImagePrune(id);
|
||||
|
||||
if (!result.success) {
|
||||
return json({ error: result.error }, { status: 400 });
|
||||
}
|
||||
|
||||
return json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Failed to trigger image prune:', error);
|
||||
return json({ error: 'Failed to trigger image prune' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
Reference in new issue
Block a user