This commit is contained in:
jarek committed 2026-02-16 12:59:42 +01:00
1 parent 3a7b856047
commit ae3aea2296
6 files changed
+233 -27

No files matched your search

+5
View File
@@ -62,6 +62,11 @@ export const PUT: RequestHandler = async ({ request, cookies }) => {
}
}
// Enforce minimum session timeout of 1 hour
if (data.sessionTimeout !== undefined) {
data.sessionTimeout = Math.max(3600, Math.min(604800, parseInt(data.sessionTimeout) || 86400));
}
const settings = await updateAuthSettings(data);
return json(settings);
} catch (error) {
@@ -0,0 +1,64 @@
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { authorize } from '$lib/server/authorize';
import { getEnvironment, getEnvSetting, setEnvSetting } from '$lib/server/db';
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);
const env = await getEnvironment(id);
if (!env) {
return json({ error: 'Environment not found' }, { status: 404 });
}
const enabled = (await getEnvSetting('disk_warning_enabled', id)) ?? true;
const mode = (await getEnvSetting('disk_warning_mode', id)) ?? 'percentage';
const threshold = (await getEnvSetting('disk_warning_threshold', id)) ?? 80;
const thresholdGb = (await getEnvSetting('disk_warning_threshold_gb', id)) ?? 50;
return json({ enabled, mode, threshold, thresholdGb });
} catch (error) {
console.error('Failed to get disk warning settings:', error);
return json({ error: 'Failed to get disk warning settings' }, { status: 500 });
}
};
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);
const env = await getEnvironment(id);
if (!env) {
return json({ error: 'Environment not found' }, { status: 404 });
}
const data = await request.json();
if (typeof data.enabled === 'boolean') {
await setEnvSetting('disk_warning_enabled', data.enabled, id);
}
if (data.mode === 'percentage' || data.mode === 'absolute') {
await setEnvSetting('disk_warning_mode', data.mode, id);
}
if (typeof data.threshold === 'number' && data.threshold >= 1 && data.threshold <= 100) {
await setEnvSetting('disk_warning_threshold', data.threshold, id);
}
if (typeof data.thresholdGb === 'number' && data.thresholdGb >= 1) {
await setEnvSetting('disk_warning_threshold_gb', data.thresholdGb, id);
}
return json({ success: true });
} catch (error) {
console.error('Failed to save disk warning settings:', error);
return json({ error: 'Failed to save disk warning settings' }, { status: 500 });
}
};
+5 -1
View File
@@ -264,7 +264,11 @@
value={sessionTimeout}
min={3600}
max={604800}
onchange={(e) => (sessionTimeout = parseInt(e.currentTarget.value))}
onchange={(e) => {
const val = parseInt(e.currentTarget.value);
sessionTimeout = Math.max(3600, Math.min(604800, isNaN(val) ? 86400 : val));
e.currentTarget.value = String(sessionTimeout);
}}
class="w-32"
disabled={!$canAccess('settings', 'edit')}
/>
@@ -262,6 +262,10 @@
let formCollectActivity = $state(true);
let formCollectMetrics = $state(true);
let formHighlightChanges = $state(true);
let formDiskWarningEnabled = $state(true);
let formDiskWarningMode = $state<'percentage' | 'absolute'>('percentage');
let formDiskWarningThreshold = $state(80);
let formDiskWarningThresholdGb = $state(50);
let formConnectionType = $state<ConnectionType>('socket');
let formHawserToken = $state('');
let formLabels = $state<string[]>([]);
@@ -445,6 +449,7 @@
loadUpdateCheckSettings(environment.id);
loadImagePruneSettings(environment.id);
loadTimezone(environment.id);
loadDiskWarningSettings(environment.id);
// Load Hawser token if edge mode
if (formConnectionType === 'hawser-edge') {
loadHawserToken(environment.id);
@@ -463,6 +468,10 @@
formCollectActivity = true;
formCollectMetrics = true;
formHighlightChanges = true;
formDiskWarningEnabled = true;
formDiskWarningMode = 'percentage';
formDiskWarningThreshold = 80;
formDiskWarningThresholdGb = 50;
formConnectionType = 'socket';
formHawserToken = '';
formLabels = [];
@@ -701,6 +710,7 @@
// Save timezone if not default
if (newEnv?.id) {
await saveTimezone(newEnv.id);
await saveDiskWarningSettings(newEnv.id);
}
onSaved();
onClose();
@@ -774,6 +784,7 @@
await saveUpdateCheckSettings(environment.id);
await saveImagePruneSettings(environment.id);
await saveTimezone(environment.id);
await saveDiskWarningSettings(environment.id);
toast.success(`Updated environment: ${formName}`);
onSaved();
onClose();
@@ -788,6 +799,39 @@
}
}
// === Disk Warning Functions ===
async function loadDiskWarningSettings(envId: number) {
try {
const response = await fetch(`/api/environments/${envId}/disk-warning`);
if (response.ok) {
const data = await response.json();
formDiskWarningEnabled = data.enabled ?? true;
formDiskWarningMode = data.mode ?? 'percentage';
formDiskWarningThreshold = data.threshold ?? 80;
formDiskWarningThresholdGb = data.thresholdGb ?? 50;
}
} catch (error) {
console.error('Failed to load disk warning settings:', error);
}
}
async function saveDiskWarningSettings(envId: number) {
try {
await fetch(`/api/environments/${envId}/disk-warning`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
enabled: formDiskWarningEnabled,
mode: formDiskWarningMode,
threshold: formDiskWarningThreshold,
thresholdGb: formDiskWarningThresholdGb
})
});
} catch (error) {
console.error('Failed to save disk warning settings:', error);
}
}
// === Timezone Functions ===
async function loadTimezone(envId: number) {
try {
@@ -2063,6 +2107,10 @@
bind:collectActivity={formCollectActivity}
bind:collectMetrics={formCollectMetrics}
bind:highlightChanges={formHighlightChanges}
bind:diskWarningEnabled={formDiskWarningEnabled}
bind:diskWarningMode={formDiskWarningMode}
bind:diskWarningThreshold={formDiskWarningThreshold}
bind:diskWarningThresholdGb={formDiskWarningThresholdGb}
/>
</Tabs.Content>
@@ -1,17 +1,28 @@
<script lang="ts">
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import { TogglePill } from '$lib/components/ui/toggle-pill';
import * as Select from '$lib/components/ui/select';
import { Percent, HardDrive } from 'lucide-svelte';
interface Props {
collectActivity: boolean;
collectMetrics: boolean;
highlightChanges: boolean;
diskWarningEnabled: boolean;
diskWarningMode: 'percentage' | 'absolute';
diskWarningThreshold: number;
diskWarningThresholdGb: number;
}
let {
collectActivity = $bindable(),
collectMetrics = $bindable(),
highlightChanges = $bindable()
highlightChanges = $bindable(),
diskWarningEnabled = $bindable(),
diskWarningMode = $bindable(),
diskWarningThreshold = $bindable(),
diskWarningThresholdGb = $bindable()
}: Props = $props();
</script>
@@ -36,3 +47,64 @@
</div>
<TogglePill bind:checked={highlightChanges} />
</div>
<div class="border-t pt-4 mt-2 space-y-3">
<div class="flex items-start gap-3">
<div class="flex-1">
<Label>Disk space warnings</Label>
<p class="text-xs text-muted-foreground">Send notifications when Docker disk usage exceeds the threshold</p>
</div>
<TogglePill bind:checked={diskWarningEnabled} />
</div>
{#if diskWarningEnabled}
<div class="flex items-center gap-3">
<Select.Root type="single" value={diskWarningMode} onValueChange={(v) => { if (v) diskWarningMode = v as 'percentage' | 'absolute'; }}>
<Select.Trigger class="w-48">
<div class="flex items-center gap-2">
{#if diskWarningMode === 'percentage'}
<Percent class="w-3.5 h-3.5" />
<span>Percentage</span>
{:else}
<HardDrive class="w-3.5 h-3.5" />
<span>Absolute (GB)</span>
{/if}
</div>
</Select.Trigger>
<Select.Content>
<Select.Item value="percentage">
<div class="flex items-center gap-2">
<Percent class="w-3.5 h-3.5" />
Percentage
</div>
</Select.Item>
<Select.Item value="absolute">
<div class="flex items-center gap-2">
<HardDrive class="w-3.5 h-3.5" />
Absolute (GB)
</div>
</Select.Item>
</Select.Content>
</Select.Root>
{#if diskWarningMode === 'percentage'}
<Input
type="number"
min={1}
max={100}
bind:value={diskWarningThreshold}
class="w-24"
/>
<span class="text-sm text-muted-foreground">%</span>
{:else}
<Input
type="number"
min={1}
bind:value={diskWarningThresholdGb}
class="w-24"
/>
<span class="text-sm text-muted-foreground">GB</span>
{/if}
</div>
{/if}
</div>