From da8c83e20e27c3b92d9733f91fa475f2f9af1948 Mon Sep 17 00:00:00 2001 From: jarek Date: Sun, 9 Aug 2026 10:31:28 +0200 Subject: [PATCH] internal cleanups and build config --- Dockerfile | 7 ++++++- Dockerfile.backup | 1 + Dockerfile.baseline | 6 ++++-- .../ui/sidebar/sidebar-provider.svelte | 19 ++++++++++--------- src/lib/config/grid-columns.ts | 6 +++--- src/lib/server/backups/operations-core.ts | 13 +------------ src/lib/stores/environment.ts | 8 ++++++-- src/routes/backups/BackupLogModal.svelte | 10 ++++++++-- 8 files changed, 39 insertions(+), 31 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1cf9d28..a7fe9ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -66,7 +66,12 @@ RUN apko build apko.yaml dockhand-base:latest output.tar \ # ----------------------------------------------------------------------------- # Stage 2: Application Builder (pure Node.js) # ----------------------------------------------------------------------------- -FROM --platform=$TARGETPLATFORM node:24-slim AS app-builder +# Pinned to node 24.18.1 (the exact node v1.0.40 shipped with). node 24.19.0 has a napi +# teardown bug that crashes the better-sqlite3 native addon during `npm run build` +# (Statement::~Statement -> Assert(env != nullptr), exit 134). The `24-slim` moving tag +# picked up 24.19.0, and once the build cache was pruned every build recompiled the addon +# on the broken node. Pinning the digest restores the known-good v1.0.40 toolchain. +FROM --platform=$TARGETPLATFORM node:24.18.1-slim@sha256:235600a8101ab264e117b1768e925532262668dc9b581ef1dd7d96ced463b8e7 AS app-builder WORKDIR /app diff --git a/Dockerfile.backup b/Dockerfile.backup index b3efc87..c4df3d9 100644 --- a/Dockerfile.backup +++ b/Dockerfile.backup @@ -9,6 +9,7 @@ # (remote ARM login: pipe password over stdin, no quoted interpolation) # (use write-capable dockerhub token so the Docker Hub push authorizes) # (revert rclone — the rclone: backend is unfinished/removed; see vault rclone.md) +# (rebuild: registry GC orphaned a helper-image blob; force a clean multi-arch republish) FROM alpine:3.21 AS os-builder ARG TARGETARCH diff --git a/Dockerfile.baseline b/Dockerfile.baseline index 615a376..b2d678e 100644 --- a/Dockerfile.baseline +++ b/Dockerfile.baseline @@ -13,7 +13,9 @@ # ----------------------------------------------------------------------------- # IMPORTANT: Must use alpine builder so native addons (better-sqlite3) are # compiled against musl libc, not glibc. Cross-ABI copies would not work. -FROM node:24-alpine AS app-builder +# Pinned to 24.18.1 like the main Dockerfile: node 24.19.0 has a napi regression that +# aborts the vite build with "Assertion failed: (env) != nullptr" (SIGABRT). +FROM node:24.18.1-alpine AS app-builder WORKDIR /app @@ -52,7 +54,7 @@ RUN cd collector && CGO_ENABLED=0 go build -o /app/bin/collection-worker . # ----------------------------------------------------------------------------- # Stage 3: Final Image (Alpine-based runtime) # ----------------------------------------------------------------------------- -FROM node:24-alpine +FROM node:24.18.1-alpine # Install runtime packages RUN apk add --no-cache \ diff --git a/src/lib/components/ui/sidebar/sidebar-provider.svelte b/src/lib/components/ui/sidebar/sidebar-provider.svelte index c82f864..77d072b 100644 --- a/src/lib/components/ui/sidebar/sidebar-provider.svelte +++ b/src/lib/components/ui/sidebar/sidebar-provider.svelte @@ -1,5 +1,4 @@ diff --git a/src/lib/config/grid-columns.ts b/src/lib/config/grid-columns.ts index f87de0f..facf07c 100644 --- a/src/lib/config/grid-columns.ts +++ b/src/lib/config/grid-columns.ts @@ -166,12 +166,12 @@ export const repoSnapshotColumns: ColumnConfig[] = [ // Backup destination grid columns export const backupDestinationColumns: ColumnConfig[] = [ - { id: 'type', label: 'Type', width: 80, minWidth: 60, resizable: false }, + { id: 'type', label: 'Type', sortable: true, sortField: 'type', width: 130, minWidth: 100, resizable: false }, { id: 'name', label: 'Name', sortable: true, sortField: 'name', width: 180, minWidth: 120, grow: true }, - { id: 'repository', label: 'Repository', width: 280, minWidth: 150, grow: true }, + { id: 'repository', label: 'Repository', sortable: true, sortField: 'repository', width: 280, minWidth: 150, grow: true }, { id: 'usage', label: 'Usage', width: 100, minWidth: 70 }, { id: 'stats', label: 'Stats', width: 140, minWidth: 100 }, - { id: 'status', label: 'Status', width: 110, minWidth: 80 }, + { id: 'status', label: 'Status', sortable: true, sortField: 'status', width: 110, minWidth: 80 }, { id: 'actions', label: '', fixed: 'end', width: 300, resizable: false } ]; diff --git a/src/lib/server/backups/operations-core.ts b/src/lib/server/backups/operations-core.ts index 275bd5c..109c5c4 100644 --- a/src/lib/server/backups/operations-core.ts +++ b/src/lib/server/backups/operations-core.ts @@ -57,7 +57,7 @@ export type ExecutionStatus = 'queued' | 'running' | 'success' | 'warning' | 'fa * "failed" filter) speak `failed`. A concurrency reject is flagged by the caller * via `skipped` and lands as `skipped`. Keeping this in ONE named, testable place * stops a failed backup from being written as `error` — a value the schedules UI - * doesn't recognise, which made failed backups invisible under its filter. + * filter does not recognise. */ export function toExecutionStatus( terminalStatus: TerminalState['status'], @@ -72,17 +72,6 @@ export function toExecutionStatus( } } -/** - * The startup scrub: any operation still `running` when the process restarted - * could not have completed, so it is rewritten to `stale` (distinct from - * `error` — `error` means it ran and failed, `stale` means the process died and - * it should just be re-run). Returns the status to write, or null to leave the - * row untouched. - */ -export function scrubStatus(current: OperationStatus): OperationStatus | null { - return current === 'running' ? 'stale' : null; -} - /** * Debounce decision for progress writes. Persisting every restic status line * would hammer the DB, so we write the first update immediately and then at most diff --git a/src/lib/stores/environment.ts b/src/lib/stores/environment.ts index 9512f49..4f30bb2 100644 --- a/src/lib/stores/environment.ts +++ b/src/lib/stores/environment.ts @@ -22,7 +22,7 @@ export interface Environment { const STORAGE_KEY = 'dockhand:environment'; -// Load initial state from localStorage +// Load initial state from localStorage (the last-used environment). function getInitialEnvironment(): CurrentEnvironment | null { if (browser) { const stored = localStorage.getItem(STORAGE_KEY); @@ -131,7 +131,11 @@ function createEnvironmentsStore() { name: firstEnv.name }); } else { - console.log(`[EnvStore] Current env ${currentId} still exists, keeping selection`); + // Keep selection; refresh its name from the list (covers a renamed env). + const match = data.find((e) => Number(e.id) === currentId); + if (match && match.name !== current!.name) { + currentEnvironment.set({ id: match.id, name: match.name }); + } } } else { // Clear environments on permission denied or other errors diff --git a/src/routes/backups/BackupLogModal.svelte b/src/routes/backups/BackupLogModal.svelte index 3eb68be..433af7f 100644 --- a/src/routes/backups/BackupLogModal.svelte +++ b/src/routes/backups/BackupLogModal.svelte @@ -3,6 +3,7 @@ import { Button } from '$lib/components/ui/button'; import { Badge } from '$lib/components/ui/badge'; import { Loader2, CheckCircle, XCircle, Archive, Copy, Check, Square } from 'lucide-svelte'; + import type { Component } from 'svelte'; import LogConsole from '$lib/components/LogConsole.svelte'; // Backup-only progress window: shown for the Play-on-a-config path, which has no @@ -15,9 +16,14 @@ logs: string[]; error?: string; onStop?: () => void; + // Optional title icon (e.g. the repo-type icon for a destination action). + // Defaults to the generic Archive icon when omitted. + icon?: Component; } - let { open = $bindable(), title, status, progress = 0, logs, error, onStop }: Props = $props(); + let { open = $bindable(), title, status, progress = 0, logs, error, onStop, icon }: Props = $props(); + + const TitleIcon = $derived(icon ?? Archive); let copied = $state(false); @@ -33,7 +39,7 @@ e.preventDefault()}> - + {title} {#if status === 'running'} Running