From 33a8a20d959cbb420ce6fae34f65739256b8fb88 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 05:13:17 +0000 Subject: [PATCH 1/3] Move the server's modules into domain folders apps/server/src had 45 modules side by side. They now sit in folders by what they are about: auth, config, disks, http, integrations, notifications, schedule, smart, snapraid and system; main.ts, bin.ts and bin-cli.ts stay at src/. The tests follow their module into a __tests__ folder of its domain, as parsers/__tests__ already did. Only git mv, no file changes its content; the imports follow in the next commit, so this commit alone does not build. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Y3rdfbWb1xDUdPRng7TX9Z --- apps/server/src/{ => auth}/__tests__/auth.test.ts | 0 apps/server/src/{ => auth}/auth.ts | 0 apps/server/src/{ => auth}/credentials.ts | 0 apps/server/src/{ => auth}/passkeys.ts | 0 apps/server/src/{ => config}/__tests__/backup.test.ts | 0 apps/server/src/{ => config}/__tests__/config.test.ts | 0 .../src/{ => config}/__tests__/maintenance-settings.test.ts | 0 apps/server/src/{ => config}/__tests__/setup.test.ts | 0 apps/server/src/{ => config}/backup.ts | 0 apps/server/src/{ => config}/config-parser.ts | 0 apps/server/src/{ => config}/config.ts | 0 apps/server/src/{ => config}/maintenance-settings.ts | 0 apps/server/src/{ => config}/setup.ts | 0 apps/server/src/{ => disks}/__tests__/disk-check.test.ts | 0 apps/server/src/{ => disks}/__tests__/disk-power.test.ts | 0 apps/server/src/{ => disks}/__tests__/disk-removal.test.ts | 0 apps/server/src/{ => disks}/__tests__/disk-replacement.test.ts | 0 apps/server/src/{ => disks}/__tests__/spindown.test.ts | 0 apps/server/src/{ => disks}/disk-check.ts | 0 apps/server/src/{ => disks}/disk-power.ts | 0 apps/server/src/{ => disks}/disk-removal.ts | 0 apps/server/src/{ => disks}/disk-replacement.ts | 0 apps/server/src/{ => disks}/parity-usage.ts | 0 apps/server/src/{ => disks}/spindown.ts | 0 apps/server/src/{ => engine}/__tests__/daemon-engine.test.ts | 0 apps/server/src/{ => engine}/__tests__/engine-contract.test.ts | 0 apps/server/src/{ => engine}/__tests__/engine-contract.ts | 0 apps/server/src/{ => engine}/__tests__/engine-settings.test.ts | 0 apps/server/src/{ => engine}/__tests__/fake-engine.ts | 0 apps/server/src/{ => http}/__tests__/frontend.test.ts | 0 apps/server/src/{ => http}/frontend.ts | 0 apps/server/src/{ => http}/websocket.ts | 0 .../src/{ => integrations}/__tests__/home-assistant.test.ts | 0 apps/server/src/{ => integrations}/__tests__/mcp.test.ts | 0 apps/server/src/{ => integrations}/__tests__/metrics.test.ts | 0 apps/server/src/{ => integrations}/array-snapshot.ts | 0 apps/server/src/{ => integrations}/home-assistant-service.ts | 0 apps/server/src/{ => integrations}/home-assistant.ts | 0 apps/server/src/{ => integrations}/mcp.ts | 0 apps/server/src/{ => integrations}/metrics.ts | 0 .../src/{ => notifications}/__tests__/notifications.test.ts | 0 apps/server/src/{ => notifications}/notification-events.ts | 0 apps/server/src/{ => notifications}/notifications.ts | 0 apps/server/src/{ => routes}/__tests__/disk-management.test.ts | 0 apps/server/src/{ => routes}/__tests__/heal.test.ts | 0 apps/server/src/{ => schedule}/__tests__/scheduler.test.ts | 0 apps/server/src/{ => schedule}/scheduler.ts | 0 apps/server/src/{ => smart}/__tests__/smart-baseline.test.ts | 0 apps/server/src/{ => smart}/__tests__/smart-history.test.ts | 0 apps/server/src/{ => smart}/__tests__/smart-ignore.test.ts | 0 apps/server/src/{ => smart}/__tests__/smart-selftest.test.ts | 0 apps/server/src/{ => smart}/smart-baseline.ts | 0 apps/server/src/{ => smart}/smart-history.ts | 0 apps/server/src/{ => smart}/smart-ignore.ts | 0 apps/server/src/{ => smart}/smart-selftest.ts | 0 apps/server/src/{ => snapraid}/__tests__/duplicates.test.ts | 0 apps/server/src/{ => snapraid}/__tests__/force-option.test.ts | 0 apps/server/src/{ => snapraid}/__tests__/log-manager.test.ts | 0 apps/server/src/{ => snapraid}/__tests__/recovery.test.ts | 0 .../server/src/{ => snapraid}/__tests__/restore-ownership.test.ts | 0 apps/server/src/{ => snapraid}/__tests__/run-result.test.ts | 0 apps/server/src/{ => snapraid}/__tests__/usage-history.test.ts | 0 apps/server/src/{ => snapraid}/duplicates.ts | 0 apps/server/src/{ => snapraid}/log-manager.ts | 0 apps/server/src/{ => snapraid}/recovery.ts | 0 apps/server/src/{ => snapraid}/restore-ownership.ts | 0 apps/server/src/{ => snapraid}/run-report.ts | 0 apps/server/src/{ => snapraid}/snapraid-runner.ts | 0 apps/server/src/{ => snapraid}/status-cache.ts | 0 apps/server/src/{ => snapraid}/usage-history.ts | 0 apps/server/src/{ => system}/__tests__/atomic-write.test.ts | 0 apps/server/src/{ => system}/__tests__/demo.test.ts | 0 apps/server/src/{ => system}/__tests__/docker.test.ts | 0 apps/server/src/{ => system}/__tests__/version.test.ts | 0 apps/server/src/{ => system}/app-files.ts | 0 apps/server/src/{ => system}/atomic-write.ts | 0 apps/server/src/{ => system}/command.ts | 0 apps/server/src/{ => system}/container-pause.ts | 0 apps/server/src/{ => system}/demo.ts | 0 apps/server/src/{ => system}/docker.ts | 0 apps/server/src/{ => system}/fs.ts | 0 apps/server/src/{ => system}/version.ts | 0 82 files changed, 0 insertions(+), 0 deletions(-) rename apps/server/src/{ => auth}/__tests__/auth.test.ts (100%) rename apps/server/src/{ => auth}/auth.ts (100%) rename apps/server/src/{ => auth}/credentials.ts (100%) rename apps/server/src/{ => auth}/passkeys.ts (100%) rename apps/server/src/{ => config}/__tests__/backup.test.ts (100%) rename apps/server/src/{ => config}/__tests__/config.test.ts (100%) rename apps/server/src/{ => config}/__tests__/maintenance-settings.test.ts (100%) rename apps/server/src/{ => config}/__tests__/setup.test.ts (100%) rename apps/server/src/{ => config}/backup.ts (100%) rename apps/server/src/{ => config}/config-parser.ts (100%) rename apps/server/src/{ => config}/config.ts (100%) rename apps/server/src/{ => config}/maintenance-settings.ts (100%) rename apps/server/src/{ => config}/setup.ts (100%) rename apps/server/src/{ => disks}/__tests__/disk-check.test.ts (100%) rename apps/server/src/{ => disks}/__tests__/disk-power.test.ts (100%) rename apps/server/src/{ => disks}/__tests__/disk-removal.test.ts (100%) rename apps/server/src/{ => disks}/__tests__/disk-replacement.test.ts (100%) rename apps/server/src/{ => disks}/__tests__/spindown.test.ts (100%) rename apps/server/src/{ => disks}/disk-check.ts (100%) rename apps/server/src/{ => disks}/disk-power.ts (100%) rename apps/server/src/{ => disks}/disk-removal.ts (100%) rename apps/server/src/{ => disks}/disk-replacement.ts (100%) rename apps/server/src/{ => disks}/parity-usage.ts (100%) rename apps/server/src/{ => disks}/spindown.ts (100%) rename apps/server/src/{ => engine}/__tests__/daemon-engine.test.ts (100%) rename apps/server/src/{ => engine}/__tests__/engine-contract.test.ts (100%) rename apps/server/src/{ => engine}/__tests__/engine-contract.ts (100%) rename apps/server/src/{ => engine}/__tests__/engine-settings.test.ts (100%) rename apps/server/src/{ => engine}/__tests__/fake-engine.ts (100%) rename apps/server/src/{ => http}/__tests__/frontend.test.ts (100%) rename apps/server/src/{ => http}/frontend.ts (100%) rename apps/server/src/{ => http}/websocket.ts (100%) rename apps/server/src/{ => integrations}/__tests__/home-assistant.test.ts (100%) rename apps/server/src/{ => integrations}/__tests__/mcp.test.ts (100%) rename apps/server/src/{ => integrations}/__tests__/metrics.test.ts (100%) rename apps/server/src/{ => integrations}/array-snapshot.ts (100%) rename apps/server/src/{ => integrations}/home-assistant-service.ts (100%) rename apps/server/src/{ => integrations}/home-assistant.ts (100%) rename apps/server/src/{ => integrations}/mcp.ts (100%) rename apps/server/src/{ => integrations}/metrics.ts (100%) rename apps/server/src/{ => notifications}/__tests__/notifications.test.ts (100%) rename apps/server/src/{ => notifications}/notification-events.ts (100%) rename apps/server/src/{ => notifications}/notifications.ts (100%) rename apps/server/src/{ => routes}/__tests__/disk-management.test.ts (100%) rename apps/server/src/{ => routes}/__tests__/heal.test.ts (100%) rename apps/server/src/{ => schedule}/__tests__/scheduler.test.ts (100%) rename apps/server/src/{ => schedule}/scheduler.ts (100%) rename apps/server/src/{ => smart}/__tests__/smart-baseline.test.ts (100%) rename apps/server/src/{ => smart}/__tests__/smart-history.test.ts (100%) rename apps/server/src/{ => smart}/__tests__/smart-ignore.test.ts (100%) rename apps/server/src/{ => smart}/__tests__/smart-selftest.test.ts (100%) rename apps/server/src/{ => smart}/smart-baseline.ts (100%) rename apps/server/src/{ => smart}/smart-history.ts (100%) rename apps/server/src/{ => smart}/smart-ignore.ts (100%) rename apps/server/src/{ => smart}/smart-selftest.ts (100%) rename apps/server/src/{ => snapraid}/__tests__/duplicates.test.ts (100%) rename apps/server/src/{ => snapraid}/__tests__/force-option.test.ts (100%) rename apps/server/src/{ => snapraid}/__tests__/log-manager.test.ts (100%) rename apps/server/src/{ => snapraid}/__tests__/recovery.test.ts (100%) rename apps/server/src/{ => snapraid}/__tests__/restore-ownership.test.ts (100%) rename apps/server/src/{ => snapraid}/__tests__/run-result.test.ts (100%) rename apps/server/src/{ => snapraid}/__tests__/usage-history.test.ts (100%) rename apps/server/src/{ => snapraid}/duplicates.ts (100%) rename apps/server/src/{ => snapraid}/log-manager.ts (100%) rename apps/server/src/{ => snapraid}/recovery.ts (100%) rename apps/server/src/{ => snapraid}/restore-ownership.ts (100%) rename apps/server/src/{ => snapraid}/run-report.ts (100%) rename apps/server/src/{ => snapraid}/snapraid-runner.ts (100%) rename apps/server/src/{ => snapraid}/status-cache.ts (100%) rename apps/server/src/{ => snapraid}/usage-history.ts (100%) rename apps/server/src/{ => system}/__tests__/atomic-write.test.ts (100%) rename apps/server/src/{ => system}/__tests__/demo.test.ts (100%) rename apps/server/src/{ => system}/__tests__/docker.test.ts (100%) rename apps/server/src/{ => system}/__tests__/version.test.ts (100%) rename apps/server/src/{ => system}/app-files.ts (100%) rename apps/server/src/{ => system}/atomic-write.ts (100%) rename apps/server/src/{ => system}/command.ts (100%) rename apps/server/src/{ => system}/container-pause.ts (100%) rename apps/server/src/{ => system}/demo.ts (100%) rename apps/server/src/{ => system}/docker.ts (100%) rename apps/server/src/{ => system}/fs.ts (100%) rename apps/server/src/{ => system}/version.ts (100%) diff --git a/apps/server/src/__tests__/auth.test.ts b/apps/server/src/auth/__tests__/auth.test.ts similarity index 100% rename from apps/server/src/__tests__/auth.test.ts rename to apps/server/src/auth/__tests__/auth.test.ts diff --git a/apps/server/src/auth.ts b/apps/server/src/auth/auth.ts similarity index 100% rename from apps/server/src/auth.ts rename to apps/server/src/auth/auth.ts diff --git a/apps/server/src/credentials.ts b/apps/server/src/auth/credentials.ts similarity index 100% rename from apps/server/src/credentials.ts rename to apps/server/src/auth/credentials.ts diff --git a/apps/server/src/passkeys.ts b/apps/server/src/auth/passkeys.ts similarity index 100% rename from apps/server/src/passkeys.ts rename to apps/server/src/auth/passkeys.ts diff --git a/apps/server/src/__tests__/backup.test.ts b/apps/server/src/config/__tests__/backup.test.ts similarity index 100% rename from apps/server/src/__tests__/backup.test.ts rename to apps/server/src/config/__tests__/backup.test.ts diff --git a/apps/server/src/__tests__/config.test.ts b/apps/server/src/config/__tests__/config.test.ts similarity index 100% rename from apps/server/src/__tests__/config.test.ts rename to apps/server/src/config/__tests__/config.test.ts diff --git a/apps/server/src/__tests__/maintenance-settings.test.ts b/apps/server/src/config/__tests__/maintenance-settings.test.ts similarity index 100% rename from apps/server/src/__tests__/maintenance-settings.test.ts rename to apps/server/src/config/__tests__/maintenance-settings.test.ts diff --git a/apps/server/src/__tests__/setup.test.ts b/apps/server/src/config/__tests__/setup.test.ts similarity index 100% rename from apps/server/src/__tests__/setup.test.ts rename to apps/server/src/config/__tests__/setup.test.ts diff --git a/apps/server/src/backup.ts b/apps/server/src/config/backup.ts similarity index 100% rename from apps/server/src/backup.ts rename to apps/server/src/config/backup.ts diff --git a/apps/server/src/config-parser.ts b/apps/server/src/config/config-parser.ts similarity index 100% rename from apps/server/src/config-parser.ts rename to apps/server/src/config/config-parser.ts diff --git a/apps/server/src/config.ts b/apps/server/src/config/config.ts similarity index 100% rename from apps/server/src/config.ts rename to apps/server/src/config/config.ts diff --git a/apps/server/src/maintenance-settings.ts b/apps/server/src/config/maintenance-settings.ts similarity index 100% rename from apps/server/src/maintenance-settings.ts rename to apps/server/src/config/maintenance-settings.ts diff --git a/apps/server/src/setup.ts b/apps/server/src/config/setup.ts similarity index 100% rename from apps/server/src/setup.ts rename to apps/server/src/config/setup.ts diff --git a/apps/server/src/__tests__/disk-check.test.ts b/apps/server/src/disks/__tests__/disk-check.test.ts similarity index 100% rename from apps/server/src/__tests__/disk-check.test.ts rename to apps/server/src/disks/__tests__/disk-check.test.ts diff --git a/apps/server/src/__tests__/disk-power.test.ts b/apps/server/src/disks/__tests__/disk-power.test.ts similarity index 100% rename from apps/server/src/__tests__/disk-power.test.ts rename to apps/server/src/disks/__tests__/disk-power.test.ts diff --git a/apps/server/src/__tests__/disk-removal.test.ts b/apps/server/src/disks/__tests__/disk-removal.test.ts similarity index 100% rename from apps/server/src/__tests__/disk-removal.test.ts rename to apps/server/src/disks/__tests__/disk-removal.test.ts diff --git a/apps/server/src/__tests__/disk-replacement.test.ts b/apps/server/src/disks/__tests__/disk-replacement.test.ts similarity index 100% rename from apps/server/src/__tests__/disk-replacement.test.ts rename to apps/server/src/disks/__tests__/disk-replacement.test.ts diff --git a/apps/server/src/__tests__/spindown.test.ts b/apps/server/src/disks/__tests__/spindown.test.ts similarity index 100% rename from apps/server/src/__tests__/spindown.test.ts rename to apps/server/src/disks/__tests__/spindown.test.ts diff --git a/apps/server/src/disk-check.ts b/apps/server/src/disks/disk-check.ts similarity index 100% rename from apps/server/src/disk-check.ts rename to apps/server/src/disks/disk-check.ts diff --git a/apps/server/src/disk-power.ts b/apps/server/src/disks/disk-power.ts similarity index 100% rename from apps/server/src/disk-power.ts rename to apps/server/src/disks/disk-power.ts diff --git a/apps/server/src/disk-removal.ts b/apps/server/src/disks/disk-removal.ts similarity index 100% rename from apps/server/src/disk-removal.ts rename to apps/server/src/disks/disk-removal.ts diff --git a/apps/server/src/disk-replacement.ts b/apps/server/src/disks/disk-replacement.ts similarity index 100% rename from apps/server/src/disk-replacement.ts rename to apps/server/src/disks/disk-replacement.ts diff --git a/apps/server/src/parity-usage.ts b/apps/server/src/disks/parity-usage.ts similarity index 100% rename from apps/server/src/parity-usage.ts rename to apps/server/src/disks/parity-usage.ts diff --git a/apps/server/src/spindown.ts b/apps/server/src/disks/spindown.ts similarity index 100% rename from apps/server/src/spindown.ts rename to apps/server/src/disks/spindown.ts diff --git a/apps/server/src/__tests__/daemon-engine.test.ts b/apps/server/src/engine/__tests__/daemon-engine.test.ts similarity index 100% rename from apps/server/src/__tests__/daemon-engine.test.ts rename to apps/server/src/engine/__tests__/daemon-engine.test.ts diff --git a/apps/server/src/__tests__/engine-contract.test.ts b/apps/server/src/engine/__tests__/engine-contract.test.ts similarity index 100% rename from apps/server/src/__tests__/engine-contract.test.ts rename to apps/server/src/engine/__tests__/engine-contract.test.ts diff --git a/apps/server/src/__tests__/engine-contract.ts b/apps/server/src/engine/__tests__/engine-contract.ts similarity index 100% rename from apps/server/src/__tests__/engine-contract.ts rename to apps/server/src/engine/__tests__/engine-contract.ts diff --git a/apps/server/src/__tests__/engine-settings.test.ts b/apps/server/src/engine/__tests__/engine-settings.test.ts similarity index 100% rename from apps/server/src/__tests__/engine-settings.test.ts rename to apps/server/src/engine/__tests__/engine-settings.test.ts diff --git a/apps/server/src/__tests__/fake-engine.ts b/apps/server/src/engine/__tests__/fake-engine.ts similarity index 100% rename from apps/server/src/__tests__/fake-engine.ts rename to apps/server/src/engine/__tests__/fake-engine.ts diff --git a/apps/server/src/__tests__/frontend.test.ts b/apps/server/src/http/__tests__/frontend.test.ts similarity index 100% rename from apps/server/src/__tests__/frontend.test.ts rename to apps/server/src/http/__tests__/frontend.test.ts diff --git a/apps/server/src/frontend.ts b/apps/server/src/http/frontend.ts similarity index 100% rename from apps/server/src/frontend.ts rename to apps/server/src/http/frontend.ts diff --git a/apps/server/src/websocket.ts b/apps/server/src/http/websocket.ts similarity index 100% rename from apps/server/src/websocket.ts rename to apps/server/src/http/websocket.ts diff --git a/apps/server/src/__tests__/home-assistant.test.ts b/apps/server/src/integrations/__tests__/home-assistant.test.ts similarity index 100% rename from apps/server/src/__tests__/home-assistant.test.ts rename to apps/server/src/integrations/__tests__/home-assistant.test.ts diff --git a/apps/server/src/__tests__/mcp.test.ts b/apps/server/src/integrations/__tests__/mcp.test.ts similarity index 100% rename from apps/server/src/__tests__/mcp.test.ts rename to apps/server/src/integrations/__tests__/mcp.test.ts diff --git a/apps/server/src/__tests__/metrics.test.ts b/apps/server/src/integrations/__tests__/metrics.test.ts similarity index 100% rename from apps/server/src/__tests__/metrics.test.ts rename to apps/server/src/integrations/__tests__/metrics.test.ts diff --git a/apps/server/src/array-snapshot.ts b/apps/server/src/integrations/array-snapshot.ts similarity index 100% rename from apps/server/src/array-snapshot.ts rename to apps/server/src/integrations/array-snapshot.ts diff --git a/apps/server/src/home-assistant-service.ts b/apps/server/src/integrations/home-assistant-service.ts similarity index 100% rename from apps/server/src/home-assistant-service.ts rename to apps/server/src/integrations/home-assistant-service.ts diff --git a/apps/server/src/home-assistant.ts b/apps/server/src/integrations/home-assistant.ts similarity index 100% rename from apps/server/src/home-assistant.ts rename to apps/server/src/integrations/home-assistant.ts diff --git a/apps/server/src/mcp.ts b/apps/server/src/integrations/mcp.ts similarity index 100% rename from apps/server/src/mcp.ts rename to apps/server/src/integrations/mcp.ts diff --git a/apps/server/src/metrics.ts b/apps/server/src/integrations/metrics.ts similarity index 100% rename from apps/server/src/metrics.ts rename to apps/server/src/integrations/metrics.ts diff --git a/apps/server/src/__tests__/notifications.test.ts b/apps/server/src/notifications/__tests__/notifications.test.ts similarity index 100% rename from apps/server/src/__tests__/notifications.test.ts rename to apps/server/src/notifications/__tests__/notifications.test.ts diff --git a/apps/server/src/notification-events.ts b/apps/server/src/notifications/notification-events.ts similarity index 100% rename from apps/server/src/notification-events.ts rename to apps/server/src/notifications/notification-events.ts diff --git a/apps/server/src/notifications.ts b/apps/server/src/notifications/notifications.ts similarity index 100% rename from apps/server/src/notifications.ts rename to apps/server/src/notifications/notifications.ts diff --git a/apps/server/src/__tests__/disk-management.test.ts b/apps/server/src/routes/__tests__/disk-management.test.ts similarity index 100% rename from apps/server/src/__tests__/disk-management.test.ts rename to apps/server/src/routes/__tests__/disk-management.test.ts diff --git a/apps/server/src/__tests__/heal.test.ts b/apps/server/src/routes/__tests__/heal.test.ts similarity index 100% rename from apps/server/src/__tests__/heal.test.ts rename to apps/server/src/routes/__tests__/heal.test.ts diff --git a/apps/server/src/__tests__/scheduler.test.ts b/apps/server/src/schedule/__tests__/scheduler.test.ts similarity index 100% rename from apps/server/src/__tests__/scheduler.test.ts rename to apps/server/src/schedule/__tests__/scheduler.test.ts diff --git a/apps/server/src/scheduler.ts b/apps/server/src/schedule/scheduler.ts similarity index 100% rename from apps/server/src/scheduler.ts rename to apps/server/src/schedule/scheduler.ts diff --git a/apps/server/src/__tests__/smart-baseline.test.ts b/apps/server/src/smart/__tests__/smart-baseline.test.ts similarity index 100% rename from apps/server/src/__tests__/smart-baseline.test.ts rename to apps/server/src/smart/__tests__/smart-baseline.test.ts diff --git a/apps/server/src/__tests__/smart-history.test.ts b/apps/server/src/smart/__tests__/smart-history.test.ts similarity index 100% rename from apps/server/src/__tests__/smart-history.test.ts rename to apps/server/src/smart/__tests__/smart-history.test.ts diff --git a/apps/server/src/__tests__/smart-ignore.test.ts b/apps/server/src/smart/__tests__/smart-ignore.test.ts similarity index 100% rename from apps/server/src/__tests__/smart-ignore.test.ts rename to apps/server/src/smart/__tests__/smart-ignore.test.ts diff --git a/apps/server/src/__tests__/smart-selftest.test.ts b/apps/server/src/smart/__tests__/smart-selftest.test.ts similarity index 100% rename from apps/server/src/__tests__/smart-selftest.test.ts rename to apps/server/src/smart/__tests__/smart-selftest.test.ts diff --git a/apps/server/src/smart-baseline.ts b/apps/server/src/smart/smart-baseline.ts similarity index 100% rename from apps/server/src/smart-baseline.ts rename to apps/server/src/smart/smart-baseline.ts diff --git a/apps/server/src/smart-history.ts b/apps/server/src/smart/smart-history.ts similarity index 100% rename from apps/server/src/smart-history.ts rename to apps/server/src/smart/smart-history.ts diff --git a/apps/server/src/smart-ignore.ts b/apps/server/src/smart/smart-ignore.ts similarity index 100% rename from apps/server/src/smart-ignore.ts rename to apps/server/src/smart/smart-ignore.ts diff --git a/apps/server/src/smart-selftest.ts b/apps/server/src/smart/smart-selftest.ts similarity index 100% rename from apps/server/src/smart-selftest.ts rename to apps/server/src/smart/smart-selftest.ts diff --git a/apps/server/src/__tests__/duplicates.test.ts b/apps/server/src/snapraid/__tests__/duplicates.test.ts similarity index 100% rename from apps/server/src/__tests__/duplicates.test.ts rename to apps/server/src/snapraid/__tests__/duplicates.test.ts diff --git a/apps/server/src/__tests__/force-option.test.ts b/apps/server/src/snapraid/__tests__/force-option.test.ts similarity index 100% rename from apps/server/src/__tests__/force-option.test.ts rename to apps/server/src/snapraid/__tests__/force-option.test.ts diff --git a/apps/server/src/__tests__/log-manager.test.ts b/apps/server/src/snapraid/__tests__/log-manager.test.ts similarity index 100% rename from apps/server/src/__tests__/log-manager.test.ts rename to apps/server/src/snapraid/__tests__/log-manager.test.ts diff --git a/apps/server/src/__tests__/recovery.test.ts b/apps/server/src/snapraid/__tests__/recovery.test.ts similarity index 100% rename from apps/server/src/__tests__/recovery.test.ts rename to apps/server/src/snapraid/__tests__/recovery.test.ts diff --git a/apps/server/src/__tests__/restore-ownership.test.ts b/apps/server/src/snapraid/__tests__/restore-ownership.test.ts similarity index 100% rename from apps/server/src/__tests__/restore-ownership.test.ts rename to apps/server/src/snapraid/__tests__/restore-ownership.test.ts diff --git a/apps/server/src/__tests__/run-result.test.ts b/apps/server/src/snapraid/__tests__/run-result.test.ts similarity index 100% rename from apps/server/src/__tests__/run-result.test.ts rename to apps/server/src/snapraid/__tests__/run-result.test.ts diff --git a/apps/server/src/__tests__/usage-history.test.ts b/apps/server/src/snapraid/__tests__/usage-history.test.ts similarity index 100% rename from apps/server/src/__tests__/usage-history.test.ts rename to apps/server/src/snapraid/__tests__/usage-history.test.ts diff --git a/apps/server/src/duplicates.ts b/apps/server/src/snapraid/duplicates.ts similarity index 100% rename from apps/server/src/duplicates.ts rename to apps/server/src/snapraid/duplicates.ts diff --git a/apps/server/src/log-manager.ts b/apps/server/src/snapraid/log-manager.ts similarity index 100% rename from apps/server/src/log-manager.ts rename to apps/server/src/snapraid/log-manager.ts diff --git a/apps/server/src/recovery.ts b/apps/server/src/snapraid/recovery.ts similarity index 100% rename from apps/server/src/recovery.ts rename to apps/server/src/snapraid/recovery.ts diff --git a/apps/server/src/restore-ownership.ts b/apps/server/src/snapraid/restore-ownership.ts similarity index 100% rename from apps/server/src/restore-ownership.ts rename to apps/server/src/snapraid/restore-ownership.ts diff --git a/apps/server/src/run-report.ts b/apps/server/src/snapraid/run-report.ts similarity index 100% rename from apps/server/src/run-report.ts rename to apps/server/src/snapraid/run-report.ts diff --git a/apps/server/src/snapraid-runner.ts b/apps/server/src/snapraid/snapraid-runner.ts similarity index 100% rename from apps/server/src/snapraid-runner.ts rename to apps/server/src/snapraid/snapraid-runner.ts diff --git a/apps/server/src/status-cache.ts b/apps/server/src/snapraid/status-cache.ts similarity index 100% rename from apps/server/src/status-cache.ts rename to apps/server/src/snapraid/status-cache.ts diff --git a/apps/server/src/usage-history.ts b/apps/server/src/snapraid/usage-history.ts similarity index 100% rename from apps/server/src/usage-history.ts rename to apps/server/src/snapraid/usage-history.ts diff --git a/apps/server/src/__tests__/atomic-write.test.ts b/apps/server/src/system/__tests__/atomic-write.test.ts similarity index 100% rename from apps/server/src/__tests__/atomic-write.test.ts rename to apps/server/src/system/__tests__/atomic-write.test.ts diff --git a/apps/server/src/__tests__/demo.test.ts b/apps/server/src/system/__tests__/demo.test.ts similarity index 100% rename from apps/server/src/__tests__/demo.test.ts rename to apps/server/src/system/__tests__/demo.test.ts diff --git a/apps/server/src/__tests__/docker.test.ts b/apps/server/src/system/__tests__/docker.test.ts similarity index 100% rename from apps/server/src/__tests__/docker.test.ts rename to apps/server/src/system/__tests__/docker.test.ts diff --git a/apps/server/src/__tests__/version.test.ts b/apps/server/src/system/__tests__/version.test.ts similarity index 100% rename from apps/server/src/__tests__/version.test.ts rename to apps/server/src/system/__tests__/version.test.ts diff --git a/apps/server/src/app-files.ts b/apps/server/src/system/app-files.ts similarity index 100% rename from apps/server/src/app-files.ts rename to apps/server/src/system/app-files.ts diff --git a/apps/server/src/atomic-write.ts b/apps/server/src/system/atomic-write.ts similarity index 100% rename from apps/server/src/atomic-write.ts rename to apps/server/src/system/atomic-write.ts diff --git a/apps/server/src/command.ts b/apps/server/src/system/command.ts similarity index 100% rename from apps/server/src/command.ts rename to apps/server/src/system/command.ts diff --git a/apps/server/src/container-pause.ts b/apps/server/src/system/container-pause.ts similarity index 100% rename from apps/server/src/container-pause.ts rename to apps/server/src/system/container-pause.ts diff --git a/apps/server/src/demo.ts b/apps/server/src/system/demo.ts similarity index 100% rename from apps/server/src/demo.ts rename to apps/server/src/system/demo.ts diff --git a/apps/server/src/docker.ts b/apps/server/src/system/docker.ts similarity index 100% rename from apps/server/src/docker.ts rename to apps/server/src/system/docker.ts diff --git a/apps/server/src/fs.ts b/apps/server/src/system/fs.ts similarity index 100% rename from apps/server/src/fs.ts rename to apps/server/src/system/fs.ts diff --git a/apps/server/src/version.ts b/apps/server/src/system/version.ts similarity index 100% rename from apps/server/src/version.ts rename to apps/server/src/system/version.ts From 9e6b9c0c13732cc8ae196257664e5f005c336ac3 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 05:16:36 +0000 Subject: [PATCH 2/3] Point the imports and paths at the new domain folders Only specifiers and paths change: the relative imports, the fixtures the moved tests read (../../parsers/__tests__/fixtures), the repository root app-files.ts resolves from (one level deeper now), and the server paths named in comments, the Dockerfile, build-binary.sh, the upstream-release workflow and the docs. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Y3rdfbWb1xDUdPRng7TX9Z --- .github/workflows/upstream-release.yml | 2 +- apps/server/scripts/seed-demo.ts | 6 +- apps/server/src/auth/__tests__/auth.test.ts | 6 +- apps/server/src/auth/auth.ts | 4 +- apps/server/src/auth/credentials.ts | 4 +- apps/server/src/bin-cli.ts | 2 +- .../src/config/__tests__/backup.test.ts | 2 +- apps/server/src/config/backup.ts | 4 +- apps/server/src/config/config-parser.ts | 6 +- .../server/src/config/maintenance-settings.ts | 4 +- apps/server/src/config/setup.ts | 4 +- .../src/disks/__tests__/disk-removal.test.ts | 4 +- .../disks/__tests__/disk-replacement.test.ts | 2 +- apps/server/src/disks/disk-check.ts | 4 +- apps/server/src/disks/disk-power.ts | 4 +- apps/server/src/disks/disk-removal.ts | 2 +- apps/server/src/disks/disk-replacement.ts | 8 +-- apps/server/src/disks/parity-usage.ts | 6 +- apps/server/src/disks/spindown.ts | 14 ++-- .../engine/__tests__/daemon-engine.test.ts | 6 +- .../engine/__tests__/engine-contract.test.ts | 12 ++-- .../src/engine/__tests__/engine-contract.ts | 4 +- .../engine/__tests__/engine-settings.test.ts | 2 +- .../src/engine/__tests__/fake-engine.ts | 2 +- apps/server/src/engine/cli-engine.ts | 14 ++-- apps/server/src/engine/daemon-engine.ts | 2 +- apps/server/src/engine/engine-settings.ts | 6 +- apps/server/src/engine/engine.ts | 2 +- apps/server/src/executors/command-executor.ts | 8 +-- apps/server/src/http/frontend.ts | 2 +- apps/server/src/http/websocket.ts | 2 +- .../src/integrations/__tests__/mcp.test.ts | 10 +-- .../server/src/integrations/array-snapshot.ts | 14 ++-- .../integrations/home-assistant-service.ts | 6 +- .../server/src/integrations/home-assistant.ts | 4 +- apps/server/src/integrations/mcp.ts | 10 +-- apps/server/src/integrations/metrics.ts | 6 +- apps/server/src/main.ts | 28 ++++---- .../__tests__/notifications.test.ts | 8 +-- .../src/notifications/notification-events.ts | 10 +-- .../server/src/notifications/notifications.ts | 6 +- .../src/parsers/__tests__/parsers.test.ts | 2 +- .../routes/__tests__/disk-management.test.ts | 2 +- apps/server/src/routes/__tests__/heal.test.ts | 4 +- apps/server/src/routes/config-operations.ts | 6 +- apps/server/src/routes/config.ts | 8 +-- apps/server/src/routes/disk-management.ts | 4 +- apps/server/src/routes/disk-replacement.ts | 4 +- apps/server/src/routes/filesystem.ts | 2 +- apps/server/src/routes/hardware.ts | 14 ++-- apps/server/src/routes/home-assistant.ts | 2 +- apps/server/src/routes/logs.ts | 2 +- apps/server/src/routes/maintenance.ts | 6 +- apps/server/src/routes/mcp.ts | 24 +++---- apps/server/src/routes/metrics.ts | 6 +- apps/server/src/routes/notifications.ts | 2 +- apps/server/src/routes/reports.ts | 8 +-- apps/server/src/routes/schedules.ts | 2 +- apps/server/src/routes/setup.ts | 2 +- apps/server/src/routes/snapraid.ts | 28 ++++---- .../src/schedule/__tests__/scheduler.test.ts | 4 +- apps/server/src/schedule/scheduler.ts | 24 +++---- .../src/smart/__tests__/smart-ignore.test.ts | 4 +- apps/server/src/smart/smart-baseline.ts | 6 +- apps/server/src/smart/smart-history.ts | 6 +- apps/server/src/smart/smart-selftest.ts | 8 +-- .../snapraid/__tests__/log-manager.test.ts | 2 +- .../src/snapraid/__tests__/recovery.test.ts | 2 +- .../__tests__/restore-ownership.test.ts | 2 +- .../src/snapraid/__tests__/run-result.test.ts | 2 +- apps/server/src/snapraid/duplicates.ts | 2 +- apps/server/src/snapraid/log-manager.ts | 2 +- apps/server/src/snapraid/recovery.ts | 2 +- apps/server/src/snapraid/restore-ownership.ts | 6 +- apps/server/src/snapraid/run-report.ts | 2 +- apps/server/src/snapraid/snapraid-runner.ts | 10 +-- apps/server/src/snapraid/usage-history.ts | 6 +- .../src/system/__tests__/atomic-write.test.ts | 2 +- apps/server/src/system/__tests__/demo.test.ts | 4 +- .../src/system/__tests__/docker.test.ts | 2 +- apps/server/src/system/app-files.ts | 2 +- apps/server/src/system/container-pause.ts | 4 +- apps/server/src/system/demo.ts | 2 +- apps/web/src/components/AccountDialog.tsx | 2 +- .../src/components/RemoveDataDiskWizard.tsx | 2 +- docker/Dockerfile | 4 +- docs/architecture.md | 64 +++++++++---------- docs/development.md | 4 +- packages/shared/src/i18n.ts | 2 +- scripts/build-binary.sh | 2 +- 90 files changed, 283 insertions(+), 283 deletions(-) diff --git a/.github/workflows/upstream-release.yml b/.github/workflows/upstream-release.yml index 4b2321e..760231f 100644 --- a/.github/workflows/upstream-release.yml +++ b/.github/workflows/upstream-release.yml @@ -48,7 +48,7 @@ jobs: snapraid-daemon [v$newest](https://github.com/amadvance/snapraid-daemon/tree/v$newest) is out, the SnapRAID engine was last compared with v$reviewed. - [ ] Compare the API: [snapraidd.yaml v$reviewed...v$newest](https://github.com/amadvance/snapraid-daemon/compare/v$reviewed...v$newest) and the [HISTORY](https://github.com/amadvance/snapraid-daemon/blob/v$newest/HISTORY) - - [ ] An endpoint for something CLI-only today (\`dup\`, \`list\`, \`devices\`, config, ...)? Add a method to \`SnapRaidEngine\` in \`apps/server/src/engine/engine.ts\`, implement it in \`cli-engine.ts\`, move the callers and extend \`apps/server/src/__tests__/engine-contract.ts\` + - [ ] An endpoint for something CLI-only today (\`dup\`, \`list\`, \`devices\`, config, ...)? Add a method to \`SnapRaidEngine\` in \`apps/server/src/engine/engine.ts\`, implement it in \`cli-engine.ts\`, move the callers and extend \`apps/server/src/engine/__tests__/engine-contract.ts\` - [ ] Update the tables in [docs/architecture.md](https://github.com/${{ github.repository }}/blob/master/docs/architecture.md#snapraid-engine) - [ ] Put $newest into \`apps/server/src/engine/REVIEWED_DAEMON_VERSION\` - [ ] The end-to-end tests build and run against that version; if it needs a newer SnapRAID, raise \`SNAPRAID15_REF\` in \`e2e/scripts/setup-tools.sh\` diff --git a/apps/server/scripts/seed-demo.ts b/apps/server/scripts/seed-demo.ts index 56c54ff..17c9c71 100644 --- a/apps/server/scripts/seed-demo.ts +++ b/apps/server/scripts/seed-demo.ts @@ -10,8 +10,8 @@ import { readdir, readFile, rm, utimes, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { Cron } from "croner"; import type { MaintenanceSettings, Schedule, UsagePoint } from "@shared/types.ts"; -import { parseSnapRaidConfig } from "../src/config-parser.ts"; -import { demoDataDiskUsage } from "../src/demo.ts"; +import { parseSnapRaidConfig } from "../src/config/config-parser.ts"; +import { demoDataDiskUsage } from "../src/system/demo.ts"; const sandbox = process.argv[2]; if (!sandbox) throw new Error("Usage: seed-demo.ts "); @@ -123,7 +123,7 @@ const schedules: Schedule[] = [ ]; await writeFile(join(sandbox, "schedules.json"), JSON.stringify({ schedules }, null, 2)); -// The demo answers for Docker itself (apps/server/src/demo.ts), spindown watches the demo disks +// The demo answers for Docker itself (apps/server/src/system/demo.ts), spindown watches the demo disks // The rest (metrics, MCP) takes its defaults when the backend loads the file const maintenance: Pick = { dockerPause: { diff --git a/apps/server/src/auth/__tests__/auth.test.ts b/apps/server/src/auth/__tests__/auth.test.ts index f9fa9bb..72a7f55 100644 --- a/apps/server/src/auth/__tests__/auth.test.ts +++ b/apps/server/src/auth/__tests__/auth.test.ts @@ -1,13 +1,13 @@ import { expect, test } from "bun:test"; -import { assertThrows } from "./assert-error.ts"; +import { assertThrows } from "../../__tests__/assert-error.ts"; import { join } from "node:path"; import type { Server } from "bun"; import { Hono } from "hono"; import { createAuth, SESSION_COOKIE } from "../auth.ts"; import { type CredentialStore, hashPassword, loadCredentials, verifyPassword } from "../credentials.ts"; import { AuthError, createPasskeys, relyingParty } from "../passkeys.ts"; -import { broadcast, closeAllClients, handleWebSocketUpgrade, websocket } from "../websocket.ts"; -import { setEngine } from "../engine/engine.ts"; +import { broadcast, closeAllClients, handleWebSocketUpgrade, websocket } from "../../http/websocket.ts"; +import { setEngine } from "../../engine/engine.ts"; import { mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; diff --git a/apps/server/src/auth/auth.ts b/apps/server/src/auth/auth.ts index 0c0e96a..b6e7833 100644 --- a/apps/server/src/auth/auth.ts +++ b/apps/server/src/auth/auth.ts @@ -6,11 +6,11 @@ import { sign, verify } from "hono/jwt"; import type { AuthenticationResponseJSON, RegistrationResponseJSON } from "@simplewebauthn/server"; import type { AuthSession, PasskeyInfo } from "@shared/types.ts"; import { msg } from "@shared/i18n.ts"; -import { BASE_PATH, resolveFromBase } from "./config.ts"; +import { BASE_PATH, resolveFromBase } from "../config/config.ts"; import { type CredentialStore, hashPassword, verifyPassword } from "./credentials.ts"; import { AuthError, createPasskeys } from "./passkeys.ts"; import { mkdir, readFile } from "node:fs/promises"; -import { writeTextFile } from "./fs.ts"; +import { writeTextFile } from "../system/fs.ts"; export const SESSION_COOKIE = "snapraid_session"; diff --git a/apps/server/src/auth/credentials.ts b/apps/server/src/auth/credentials.ts index ad5ed56..6f7d01a 100644 --- a/apps/server/src/auth/credentials.ts +++ b/apps/server/src/auth/credentials.ts @@ -5,9 +5,9 @@ // SNAPRAID_UI_USERNAME or SNAPRAID_UI_PASSWORD change, the file is dropped at startup: the // password from the environment applies again and the passkeys are gone. That is the way back // in after a forgotten password, and it removes a passkey someone else added. -import { writeTextFileAtomic } from "./atomic-write.ts"; +import { writeTextFileAtomic } from "../system/atomic-write.ts"; import { readFile } from "node:fs/promises"; -import { isNotFound, remove } from "./fs.ts"; +import { isNotFound, remove } from "../system/fs.ts"; export interface StoredPasskey { id: string; diff --git a/apps/server/src/bin-cli.ts b/apps/server/src/bin-cli.ts index 491e777..a8bbf1d 100644 --- a/apps/server/src/bin-cli.ts +++ b/apps/server/src/bin-cli.ts @@ -1,7 +1,7 @@ // The command line of the standalone binary (bin.ts): --version and --help, and the data directory // when none is set, before main.ts's modules read the environment import { join } from "node:path"; -import { APP_VERSION } from "./version.ts"; +import { APP_VERSION } from "./system/version.ts"; const HELP = `SnapRAID UI ${APP_VERSION}, web interface for SnapRAID diff --git a/apps/server/src/config/__tests__/backup.test.ts b/apps/server/src/config/__tests__/backup.test.ts index 466765f..5997d1b 100644 --- a/apps/server/src/config/__tests__/backup.test.ts +++ b/apps/server/src/config/__tests__/backup.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { assertThrows } from "./assert-error.ts"; +import { assertThrows } from "../../__tests__/assert-error.ts"; import { BackupError, planRestore } from "../backup.ts"; const configJson = JSON.stringify({ diff --git a/apps/server/src/config/backup.ts b/apps/server/src/config/backup.ts index 60fe256..5f2912b 100644 --- a/apps/server/src/config/backup.ts +++ b/apps/server/src/config/backup.ts @@ -1,9 +1,9 @@ -import { existsSync } from "./fs.ts"; +import { existsSync } from "../system/fs.ts"; import { dirname, isAbsolute, normalize } from "node:path"; import type { AppConfig, SettingsBackup } from "@shared/types.ts"; import { msg } from "@shared/i18n.ts"; import { resolveFromBase } from "./config.ts"; -import { writeTextFileAtomic } from "./atomic-write.ts"; +import { writeTextFileAtomic } from "../system/atomic-write.ts"; import { mkdir, readFile, writeFile } from "node:fs/promises"; // Settings and history next to config.json; logs and replacement runs stay behind diff --git a/apps/server/src/config/config-parser.ts b/apps/server/src/config/config-parser.ts index 38ba62f..c187916 100644 --- a/apps/server/src/config/config-parser.ts +++ b/apps/server/src/config/config-parser.ts @@ -1,10 +1,10 @@ import { basename, dirname, join } from "node:path"; import type { AppConfig, ParityLevel, ParsedSnapRaidConfig } from "@shared/types.ts"; import { resolveFromBase } from "./config.ts"; -import { effectiveSmartIgnore, parseSmartIgnoreLine } from "./smart-ignore.ts"; -import { writeTextFileAtomic } from "./atomic-write.ts"; +import { effectiveSmartIgnore, parseSmartIgnoreLine } from "../smart/smart-ignore.ts"; +import { writeTextFileAtomic } from "../system/atomic-write.ts"; import { mkdir, readFile } from "node:fs/promises"; -import { isNotFound } from "./fs.ts"; +import { isNotFound } from "../system/fs.ts"; export const MAX_PARITY_LEVEL = 6; diff --git a/apps/server/src/config/maintenance-settings.ts b/apps/server/src/config/maintenance-settings.ts index 42944dc..30198c3 100644 --- a/apps/server/src/config/maintenance-settings.ts +++ b/apps/server/src/config/maintenance-settings.ts @@ -1,7 +1,7 @@ -import { existsSync } from "./fs.ts"; +import { existsSync } from "../system/fs.ts"; import type { MaintenanceSettings, SnapRaidCommand } from "@shared/types.ts"; import { resolveFromBase } from "./config.ts"; -import { writeTextFileAtomic } from "./atomic-write.ts"; +import { writeTextFileAtomic } from "../system/atomic-write.ts"; import { readFile } from "node:fs/promises"; const SETTINGS_FILE = "maintenance.json"; diff --git a/apps/server/src/config/setup.ts b/apps/server/src/config/setup.ts index 0f144a7..7825f34 100644 --- a/apps/server/src/config/setup.ts +++ b/apps/server/src/config/setup.ts @@ -6,8 +6,8 @@ import type { MountCandidate } from "@shared/types.ts"; import { loadAppConfig, saveAppConfig } from "./config-parser.ts"; import { BASE_PATH, resolveFromBase } from "./config.ts"; import { readdir, stat, writeFile } from "node:fs/promises"; -import { isAlreadyExists } from "./fs.ts"; -import { runCommand } from "./command.ts"; +import { isAlreadyExists } from "../system/fs.ts"; +import { runCommand } from "../system/command.ts"; // Filesystems that never hold data disks const VIRTUAL_FS = new Set([ diff --git a/apps/server/src/disks/__tests__/disk-removal.test.ts b/apps/server/src/disks/__tests__/disk-removal.test.ts index 4afbdb8..62e16c8 100644 --- a/apps/server/src/disks/__tests__/disk-removal.test.ts +++ b/apps/server/src/disks/__tests__/disk-removal.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { assertRejects, assertThrows } from "./assert-error.ts"; +import { assertRejects, assertThrows } from "../../__tests__/assert-error.ts"; import { join } from "node:path"; import { DiskRemovalError, @@ -7,7 +7,7 @@ import { finalizeDataDiskRemoval, prepareDataDiskRemoval, } from "../disk-removal.ts"; -import { parseSnapRaidConfig } from "../config-parser.ts"; +import { parseSnapRaidConfig } from "../../config/config-parser.ts"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; diff --git a/apps/server/src/disks/__tests__/disk-replacement.test.ts b/apps/server/src/disks/__tests__/disk-replacement.test.ts index c0f9604..00278cf 100644 --- a/apps/server/src/disks/__tests__/disk-replacement.test.ts +++ b/apps/server/src/disks/__tests__/disk-replacement.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { assertThrows } from "./assert-error.ts"; +import { assertThrows } from "../../__tests__/assert-error.ts"; import { applyReplacementPath, DiskReplacementError, requiredDirectory } from "../disk-replacement.ts"; const CONFIG = `parity /mnt/parity1/snapraid.parity diff --git a/apps/server/src/disks/disk-check.ts b/apps/server/src/disks/disk-check.ts index 1029a69..e6fdaaa 100644 --- a/apps/server/src/disks/disk-check.ts +++ b/apps/server/src/disks/disk-check.ts @@ -2,9 +2,9 @@ // An unmounted disk leaves an empty mount point behind; SnapRAID then sees all its // files as deleted, and a sync would drop them from parity. import type { DiskIssue } from "@shared/types.ts"; -import { parseLogTags, toInt, unescapeTagValue } from "./parsers/structured-log.ts"; +import { parseLogTags, toInt, unescapeTagValue } from "../parsers/structured-log.ts"; import { readdir, stat } from "node:fs/promises"; -import { isNotFound } from "./fs.ts"; +import { isNotFound } from "../system/fs.ts"; // Parity levels in the structured log, split parity files (`parity/1`) are not tag lines const PARITY_LEVEL = /^(?:[2-6]-)?parity(?:\/\d+)?$|^z-parity(?:\/\d+)?$/; diff --git a/apps/server/src/disks/disk-power.ts b/apps/server/src/disks/disk-power.ts index 18ab292..7ced507 100644 --- a/apps/server/src/disks/disk-power.ts +++ b/apps/server/src/disks/disk-power.ts @@ -1,7 +1,7 @@ // Spin disks up or down by hand, with SnapRAID's `up` and `down` (smartctl and hdparm under the // hood). Like the automatic spindown this calls SnapRAID directly, also in daemon mode. -import { deviceCommandSucceeded, executeSnapraidCommand } from "./executors/command-executor.ts"; -import { DEMO_MODE } from "./demo.ts"; +import { deviceCommandSucceeded, executeSnapraidCommand } from "../executors/command-executor.ts"; +import { DEMO_MODE } from "../system/demo.ts"; export type PowerAction = "up" | "down"; diff --git a/apps/server/src/disks/disk-removal.ts b/apps/server/src/disks/disk-removal.ts index f0b9b91..a7a6033 100644 --- a/apps/server/src/disks/disk-removal.ts +++ b/apps/server/src/disks/disk-removal.ts @@ -1,5 +1,5 @@ import { dirname, join } from "node:path"; -import { isPendingRemoval, parseParityLine, REMOVAL_DIR } from "./config-parser.ts"; +import { isPendingRemoval, parseParityLine, REMOVAL_DIR } from "../config/config-parser.ts"; import { msg } from "@shared/i18n.ts"; import { mkdir, readdir } from "node:fs/promises"; diff --git a/apps/server/src/disks/disk-replacement.ts b/apps/server/src/disks/disk-replacement.ts index 2ee3bee..c8c232d 100644 --- a/apps/server/src/disks/disk-replacement.ts +++ b/apps/server/src/disks/disk-replacement.ts @@ -1,10 +1,10 @@ import { dirname } from "node:path"; -import { existsSync } from "./fs.ts"; +import { existsSync } from "../system/fs.ts"; import type { DiskReplacement, ReplacementStep, ReplacementStepResult } from "@shared/types.ts"; -import { parseParityLine } from "./config-parser.ts"; -import { resolveFromBase } from "./config.ts"; +import { parseParityLine } from "../config/config-parser.ts"; +import { resolveFromBase } from "../config/config.ts"; import { msg } from "@shared/i18n.ts"; -import { writeTextFileAtomic } from "./atomic-write.ts"; +import { writeTextFileAtomic } from "../system/atomic-write.ts"; import { readFile } from "node:fs/promises"; /** diff --git a/apps/server/src/disks/parity-usage.ts b/apps/server/src/disks/parity-usage.ts index 40e8cca..c285f90 100644 --- a/apps/server/src/disks/parity-usage.ts +++ b/apps/server/src/disks/parity-usage.ts @@ -5,10 +5,10 @@ import type { ParityLevelUsage, ParsedSnapRaidConfig, } from "@shared/types.ts"; -import { type FsUsage, parseDfOutput } from "./parsers/df-parser.ts"; -import { DEMO_MODE, demoDataDiskUsage, demoParityUsage } from "./demo.ts"; +import { type FsUsage, parseDfOutput } from "../parsers/df-parser.ts"; +import { DEMO_MODE, demoDataDiskUsage, demoParityUsage } from "../system/demo.ts"; import { stat } from "node:fs/promises"; -import { runCommand } from "./command.ts"; +import { runCommand } from "../system/command.ts"; const toGB = (bytes: number): number => Math.round(bytes / 1e9 * 10) / 10; diff --git a/apps/server/src/disks/spindown.ts b/apps/server/src/disks/spindown.ts index 6e1cb56..8d4d1b0 100644 --- a/apps/server/src/disks/spindown.ts +++ b/apps/server/src/disks/spindown.ts @@ -3,13 +3,13 @@ // are preferred: SMART reads and power state probes count as I/O of the whole disk only, // they would otherwise keep it awake forever. import type { DeviceInfo, SpindownDisk, SpindownStatus } from "@shared/types.ts"; -import { loadAppConfig } from "./config-parser.ts"; -import { resolveFromBase } from "./config.ts"; -import { executeSnapraidCommand } from "./executors/command-executor.ts"; -import { parseDevicesOutput } from "./parsers/devices-parser.ts"; -import { loadMaintenanceSettings } from "./maintenance-settings.ts"; -import { DEMO_MODE, demoDevices, demoDiskstats } from "./demo.ts"; -import { selfTestRunning } from "./smart-selftest.ts"; +import { loadAppConfig } from "../config/config-parser.ts"; +import { resolveFromBase } from "../config/config.ts"; +import { executeSnapraidCommand } from "../executors/command-executor.ts"; +import { parseDevicesOutput } from "../parsers/devices-parser.ts"; +import { loadMaintenanceSettings } from "../config/maintenance-settings.ts"; +import { DEMO_MODE, demoDevices, demoDiskstats } from "../system/demo.ts"; +import { selfTestRunning } from "../smart/smart-selftest.ts"; import { readFile } from "node:fs/promises"; const TICK_MS = 60_000; diff --git a/apps/server/src/engine/__tests__/daemon-engine.test.ts b/apps/server/src/engine/__tests__/daemon-engine.test.ts index 1a71019..1f8464e 100644 --- a/apps/server/src/engine/__tests__/daemon-engine.test.ts +++ b/apps/server/src/engine/__tests__/daemon-engine.test.ts @@ -1,9 +1,9 @@ // The daemon engine against a stand-in for snapraid-daemon 2.0. The responses follow // what a real daemon answered (fields trimmed); a task advances one step per poll. import { expect, test } from "bun:test"; -import { assertRejects } from "./assert-error.ts"; +import { assertRejects } from "../../__tests__/assert-error.ts"; import type { JobProgress } from "@shared/types.ts"; -import { EngineBusyError } from "../engine/engine.ts"; +import { EngineBusyError } from "../engine.ts"; import { createDaemonEngine, type DaemonArray, @@ -17,7 +17,7 @@ import { toRunReport, toSmartDisks, toStatus, -} from "../engine/daemon-engine.ts"; +} from "../daemon-engine.ts"; import { assessSmart } from "@shared/smart-health.ts"; import { createFakeEngine } from "./fake-engine.ts"; diff --git a/apps/server/src/engine/__tests__/engine-contract.test.ts b/apps/server/src/engine/__tests__/engine-contract.test.ts index 1543a5c..eb7dccd 100644 --- a/apps/server/src/engine/__tests__/engine-contract.test.ts +++ b/apps/server/src/engine/__tests__/engine-contract.test.ts @@ -1,10 +1,10 @@ import { join } from "node:path"; -import { runCommand } from "../command.ts"; -import { SNAPRAID_BIN, SNAPRAID_EXTRA_ARGS } from "../config.ts"; -import { createCliEngine } from "../engine/cli-engine.ts"; -import { createDaemonEngine } from "../engine/daemon-engine.ts"; -import { parseSnapRaidConfig } from "../config-parser.ts"; -import { createLogManager } from "../log-manager.ts"; +import { runCommand } from "../../system/command.ts"; +import { SNAPRAID_BIN, SNAPRAID_EXTRA_ARGS } from "../../config/config.ts"; +import { createCliEngine } from "../cli-engine.ts"; +import { createDaemonEngine } from "../daemon-engine.ts"; +import { parseSnapRaidConfig } from "../../config/config-parser.ts"; +import { createLogManager } from "../../snapraid/log-manager.ts"; import { engineContract } from "./engine-contract.ts"; import { createFakeEngine } from "./fake-engine.ts"; import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; diff --git a/apps/server/src/engine/__tests__/engine-contract.ts b/apps/server/src/engine/__tests__/engine-contract.ts index cc33418..cff3aa9 100644 --- a/apps/server/src/engine/__tests__/engine-contract.ts +++ b/apps/server/src/engine/__tests__/engine-contract.ts @@ -2,8 +2,8 @@ // the fake one always, the CLI one when a SnapRAID binary is available. // A new engine, e.g. one on snapraid-daemon's REST API, gets the same tests. import { expect, test } from "bun:test"; -import { assertRejects } from "./assert-error.ts"; -import { EngineBusyError, type SnapRaidEngine } from "../engine/engine.ts"; +import { assertRejects } from "../../__tests__/assert-error.ts"; +import { EngineBusyError, type SnapRaidEngine } from "../engine.ts"; export interface ContractSetup { engine: SnapRaidEngine; diff --git a/apps/server/src/engine/__tests__/engine-settings.test.ts b/apps/server/src/engine/__tests__/engine-settings.test.ts index 8005828..98f8de9 100644 --- a/apps/server/src/engine/__tests__/engine-settings.test.ts +++ b/apps/server/src/engine/__tests__/engine-settings.test.ts @@ -6,7 +6,7 @@ import { DEFAULT_ENGINE_SETTINGS, maskEngineSecrets, normalizeEngineSettings, -} from "../engine/engine-settings.ts"; +} from "../engine-settings.ts"; import { createFakeEngine } from "./fake-engine.ts"; const target = (changes = {}) => ({ diff --git a/apps/server/src/engine/__tests__/fake-engine.ts b/apps/server/src/engine/__tests__/fake-engine.ts index 36ef39f..6f8f3bf 100644 --- a/apps/server/src/engine/__tests__/fake-engine.ts +++ b/apps/server/src/engine/__tests__/fake-engine.ts @@ -9,7 +9,7 @@ import type { SnapRaidCommand, SnapRaidStatus, } from "@shared/types.ts"; -import { EngineBusyError, type JobOutcome, type SnapRaidEngine } from "../engine/engine.ts"; +import { EngineBusyError, type JobOutcome, type SnapRaidEngine } from "../engine.ts"; export interface FakeEngineOptions { disks?: string[]; diff --git a/apps/server/src/engine/cli-engine.ts b/apps/server/src/engine/cli-engine.ts index b9814fa..01a00ec 100644 --- a/apps/server/src/engine/cli-engine.ts +++ b/apps/server/src/engine/cli-engine.ts @@ -1,8 +1,8 @@ // The engine that runs the SnapRAID CLI itself and reads its structured log import type { CommandOutput, SnapRaidCommand } from "@shared/types.ts"; import { msg } from "@shared/i18n.ts"; -import { snapraidCommand } from "../config.ts"; -import { runCommand } from "../command.ts"; +import { snapraidCommand } from "../config/config.ts"; +import { runCommand } from "../system/command.ts"; import { abortCommand, deviceCommandSucceeded, @@ -13,11 +13,11 @@ import { setLogManager, setProgressListener, } from "../executors/command-executor.ts"; -import type { LogManager } from "../log-manager.ts"; -import { readRunReport } from "../run-report.ts"; -import { findDiskIssues } from "../disk-check.ts"; -import { DEMO_MODE, demoProbeLog, demoSmartLog, demoStatusLog } from "../demo.ts"; -import { parseSnapRaidConfig } from "../config-parser.ts"; +import type { LogManager } from "../snapraid/log-manager.ts"; +import { readRunReport } from "../snapraid/run-report.ts"; +import { findDiskIssues } from "../disks/disk-check.ts"; +import { DEMO_MODE, demoProbeLog, demoSmartLog, demoStatusLog } from "../system/demo.ts"; +import { parseSnapRaidConfig } from "../config/config-parser.ts"; import { parseDiffOutput } from "../parsers/diff-parser.ts"; import { parseDegradedDisks, parseProbeOutput } from "../parsers/probe-parser.ts"; import { parseSmartArrayFailure, parseSmartOutput } from "../parsers/smart-parser.ts"; diff --git a/apps/server/src/engine/daemon-engine.ts b/apps/server/src/engine/daemon-engine.ts index 0b09dc1..f4899a1 100644 --- a/apps/server/src/engine/daemon-engine.ts +++ b/apps/server/src/engine/daemon-engine.ts @@ -20,7 +20,7 @@ import type { SnapRaidCommand, SnapRaidStatus, } from "@shared/types.ts"; -import { parseRunReport, type RunReport } from "../run-report.ts"; +import { parseRunReport, type RunReport } from "../snapraid/run-report.ts"; import { EngineBusyError, type JobOutcome, diff --git a/apps/server/src/engine/engine-settings.ts b/apps/server/src/engine/engine-settings.ts index 3d64a4c..6d093f1 100644 --- a/apps/server/src/engine/engine-settings.ts +++ b/apps/server/src/engine/engine-settings.ts @@ -1,10 +1,10 @@ // Which engine runs SnapRAID (engine.json), and the engine built from it -import { existsSync } from "../fs.ts"; +import { existsSync } from "../system/fs.ts"; import { type DaemonCheck, type DaemonTarget, type DaemonWarning, type EngineSettings, SECRET_MASK } from "@shared/types.ts"; -import { resolveFromBase } from "../config.ts"; +import { resolveFromBase } from "../config/config.ts"; import { createDaemonClient, createDaemonEngine, type DaemonArray, DaemonError } from "./daemon-engine.ts"; import type { SnapRaidEngine } from "./engine.ts"; -import { writeTextFileAtomic } from "../atomic-write.ts"; +import { writeTextFileAtomic } from "../system/atomic-write.ts"; import { readFile } from "node:fs/promises"; const SETTINGS_FILE = "engine.json"; diff --git a/apps/server/src/engine/engine.ts b/apps/server/src/engine/engine.ts index 794ffba..333ce0d 100644 --- a/apps/server/src/engine/engine.ts +++ b/apps/server/src/engine/engine.ts @@ -14,7 +14,7 @@ import type { SnapRaidCommand, SnapRaidStatus, } from "@shared/types.ts"; -import type { RunReport } from "../run-report.ts"; +import type { RunReport } from "../snapraid/run-report.ts"; export interface JobRequest { command: SnapRaidCommand; diff --git a/apps/server/src/executors/command-executor.ts b/apps/server/src/executors/command-executor.ts index f9192bd..e323453 100644 --- a/apps/server/src/executors/command-executor.ts +++ b/apps/server/src/executors/command-executor.ts @@ -1,13 +1,13 @@ import type { SnapRaidCommand, CommandOutput, RunningJob, FinishedJob, JobProgress } from "@shared/types.ts"; import { detectForceOption } from "@shared/force-option.ts"; -import type { LogManager } from "../log-manager.ts"; +import type { LogManager } from "../snapraid/log-manager.ts"; import { basename } from "node:path"; import type { Subprocess } from "bun"; -import { snapraidCommand } from "../config.ts"; -import { runCommand } from "../command.ts"; +import { snapraidCommand } from "../config/config.ts"; +import { runCommand } from "../system/command.ts"; import { parseRunPos } from "../parsers/progress-parser.ts"; import { createLogTail } from "./log-tail.ts"; -import { holdContainers } from "../container-pause.ts"; +import { holdContainers } from "../system/container-pause.ts"; // A running SnapRAID with its output piped type SnapraidProcess = Subprocess<"inherit", "pipe", "pipe">; diff --git a/apps/server/src/http/frontend.ts b/apps/server/src/http/frontend.ts index b17f194..aa7132d 100644 --- a/apps/server/src/http/frontend.ts +++ b/apps/server/src/http/frontend.ts @@ -3,7 +3,7 @@ // port next to /api and /ws, so the UI runs as a single process without a proxy in front. import { stat } from "node:fs/promises"; -import { appFile } from "./app-files.ts"; +import { appFile } from "../system/app-files.ts"; export type FetchHandler = (request: Request) => Response | Promise; diff --git a/apps/server/src/http/websocket.ts b/apps/server/src/http/websocket.ts index 8413e53..90e4e17 100644 --- a/apps/server/src/http/websocket.ts +++ b/apps/server/src/http/websocket.ts @@ -1,5 +1,5 @@ import type { Server, ServerWebSocket, WebSocketHandler } from "bun"; -import { getEngine } from "./engine/engine.ts"; +import { getEngine } from "../engine/engine.ts"; // WebSocket connection management const wsClients = new Set>(); diff --git a/apps/server/src/integrations/__tests__/mcp.test.ts b/apps/server/src/integrations/__tests__/mcp.test.ts index e45a604..5f751ec 100644 --- a/apps/server/src/integrations/__tests__/mcp.test.ts +++ b/apps/server/src/integrations/__tests__/mcp.test.ts @@ -2,13 +2,13 @@ import { expect, test } from "bun:test"; import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js"; import type { LogFile, MaintenanceSettings, Schedule, SnapRaidConfig } from "@shared/types.ts"; -import { DEFAULT_MAINTENANCE_SETTINGS } from "../maintenance-settings.ts"; +import { DEFAULT_MAINTENANCE_SETTINGS } from "../../config/maintenance-settings.ts"; import { createMcpServer, DOCS_DIR, fileNameOf, type McpDeps } from "../mcp.ts"; import type { ConfigMetrics } from "../metrics.ts"; -import type { LogManager } from "../log-manager.ts"; -import { createMcpRoutes, setMcpSources } from "../routes/mcp.ts"; -import type { Scheduler } from "../scheduler.ts"; -import { createFakeEngine } from "./fake-engine.ts"; +import type { LogManager } from "../../snapraid/log-manager.ts"; +import { createMcpRoutes, setMcpSources } from "../../routes/mcp.ts"; +import type { Scheduler } from "../../schedule/scheduler.ts"; +import { createFakeEngine } from "../../engine/__tests__/fake-engine.ts"; const ARRAYS: SnapRaidConfig[] = [ { name: "Media", path: "media.conf", enabled: true }, diff --git a/apps/server/src/integrations/array-snapshot.ts b/apps/server/src/integrations/array-snapshot.ts index 8a75b96..5140437 100644 --- a/apps/server/src/integrations/array-snapshot.ts +++ b/apps/server/src/integrations/array-snapshot.ts @@ -2,14 +2,14 @@ // the last runs from the logs (or the daemon), the last status read, the usage and SMART // histories. The Prometheus metrics and Home Assistant are built from it. import type { Schedule } from "@shared/types.ts"; -import { loadAppConfig } from "./config-parser.ts"; -import { resolveFromBase } from "./config.ts"; -import { getEngine } from "./engine/engine.ts"; -import type { LogManager } from "./log-manager.ts"; +import { loadAppConfig } from "../config/config-parser.ts"; +import { resolveFromBase } from "../config/config.ts"; +import { getEngine } from "../engine/engine.ts"; +import type { LogManager } from "../snapraid/log-manager.ts"; import type { ConfigMetrics } from "./metrics.ts"; -import { getSmartHistory } from "./smart-history.ts"; -import { lastStatus } from "./status-cache.ts"; -import { getUsageHistory } from "./usage-history.ts"; +import { getSmartHistory } from "../smart/smart-history.ts"; +import { lastStatus } from "../snapraid/status-cache.ts"; +import { getUsageHistory } from "../snapraid/usage-history.ts"; const sources = { logManager: null as LogManager | null, diff --git a/apps/server/src/integrations/home-assistant-service.ts b/apps/server/src/integrations/home-assistant-service.ts index 0ef80bf..bc0cc91 100644 --- a/apps/server/src/integrations/home-assistant-service.ts +++ b/apps/server/src/integrations/home-assistant-service.ts @@ -1,8 +1,8 @@ // The MQTT connection to Home Assistant: publishes discovery and state, runs the buttons' commands import mqtt, { type MqttClient } from "mqtt"; -import { existsSync } from "./fs.ts"; +import { existsSync } from "../system/fs.ts"; import { type HomeAssistantSettings, type HomeAssistantStatus, SECRET_MASK } from "@shared/types.ts"; -import { resolveFromBase } from "./config.ts"; +import { resolveFromBase } from "../config/config.ts"; import type { ConfigMetrics } from "./metrics.ts"; import { arraySlugs, @@ -13,7 +13,7 @@ import { type MqttMessage, parseCommand, } from "./home-assistant.ts"; -import { writeTextFileAtomic } from "./atomic-write.ts"; +import { writeTextFileAtomic } from "../system/atomic-write.ts"; import { readFile } from "node:fs/promises"; const SETTINGS_FILE = "home-assistant.json"; diff --git a/apps/server/src/integrations/home-assistant.ts b/apps/server/src/integrations/home-assistant.ts index b75b8c0..85e8c79 100644 --- a/apps/server/src/integrations/home-assistant.ts +++ b/apps/server/src/integrations/home-assistant.ts @@ -3,8 +3,8 @@ // disks, binary sensors for problems and a running job, and buttons for sync and scrub. // The values come from the array snapshot, publishing never wakes a disk. import type { ConfigMetrics } from "./metrics.ts"; -import { blockingIssues } from "./disk-check.ts"; -import { isSuccessful } from "./run-report.ts"; +import { blockingIssues } from "../disks/disk-check.ts"; +import { isSuccessful } from "../snapraid/run-report.ts"; export type ArrayHealth = "ok" | "errors" | "disk_missing" | "sync_incomplete" | "unknown"; diff --git a/apps/server/src/integrations/mcp.ts b/apps/server/src/integrations/mcp.ts index ca17b38..ebf9e85 100644 --- a/apps/server/src/integrations/mcp.ts +++ b/apps/server/src/integrations/mcp.ts @@ -20,12 +20,12 @@ import type { import { diskFreeSeries, forecastFill } from "@shared/usage-forecast.ts"; import { arrayState } from "./home-assistant.ts"; import type { ConfigMetrics } from "./metrics.ts"; -import { EngineBusyError, type SnapRaidEngine } from "./engine/engine.ts"; -import type { LogManager } from "./log-manager.ts"; -import type { Scheduler } from "./scheduler.ts"; -import type { CreateArrayResult } from "./setup.ts"; +import { EngineBusyError, type SnapRaidEngine } from "../engine/engine.ts"; +import type { LogManager } from "../snapraid/log-manager.ts"; +import type { Scheduler } from "../schedule/scheduler.ts"; +import type { CreateArrayResult } from "../config/setup.ts"; import { readdir, readFile } from "node:fs/promises"; -import { appFile } from "./app-files.ts"; +import { appFile } from "../system/app-files.ts"; export const DOCS_DIR = fileURLToPath(appFile("docs/")); diff --git a/apps/server/src/integrations/metrics.ts b/apps/server/src/integrations/metrics.ts index 5c6a51a..dc8e541 100644 --- a/apps/server/src/integrations/metrics.ts +++ b/apps/server/src/integrations/metrics.ts @@ -2,9 +2,9 @@ // runs in the logs, the last status read, the usage and SMART histories and the schedules. // Nothing here runs SnapRAID or smartctl, a scrape never wakes a disk. import type { LastRuns, RunningJob, Schedule, SmartHistoryPoint, UsagePoint } from "@shared/types.ts"; -import { isSuccessful } from "./run-report.ts"; -import { blockingIssues } from "./disk-check.ts"; -import type { CachedStatus } from "./status-cache.ts"; +import { isSuccessful } from "../snapraid/run-report.ts"; +import { blockingIssues } from "../disks/disk-check.ts"; +import type { CachedStatus } from "../snapraid/status-cache.ts"; export interface ConfigMetrics { name: string; diff --git a/apps/server/src/main.ts b/apps/server/src/main.ts index d49c256..959c13f 100644 --- a/apps/server/src/main.ts +++ b/apps/server/src/main.ts @@ -1,16 +1,16 @@ import type { Server } from "bun"; import { Hono } from "hono"; import { cors } from "hono/cors"; -import { loadAppConfig } from "./config-parser.ts"; -import { broadcast, closeAllClients, handleWebSocketUpgrade, websocket } from "./websocket.ts"; +import { loadAppConfig } from "./config/config-parser.ts"; +import { broadcast, closeAllClients, handleWebSocketUpgrade, websocket } from "./http/websocket.ts"; import { setBroadcast, setSnapraidLogManager } from "./routes/snapraid.ts"; import { activeEngine, type SnapRaidEngine, setEngine, wrapJobs } from "./engine/engine.ts"; import { createCliEngine } from "./engine/cli-engine.ts"; import { buildEngine, loadEngineSettings } from "./engine/engine-settings.ts"; import { engineRoutes, setEngineApplier } from "./routes/engine.ts"; -import { createLogManager } from "./log-manager.ts"; +import { createLogManager } from "./snapraid/log-manager.ts"; import { setLogManager } from "./routes/logs.ts"; -import { createScheduler } from "./scheduler.ts"; +import { createScheduler } from "./schedule/scheduler.ts"; import { setScheduler } from "./routes/schedules.ts"; import { configRoutes } from "./routes/config.ts"; import { filesystemRoutes } from "./routes/filesystem.ts"; @@ -22,18 +22,18 @@ import { maintenanceRoutes, setSpindownMonitor } from "./routes/maintenance.ts"; import { setupRoutes } from "./routes/setup.ts"; import { metricsRoutes } from "./routes/metrics.ts"; import { mcpRoutes, setMcpSources } from "./routes/mcp.ts"; -import { setSnapshotSources, snapshotConfigs } from "./array-snapshot.ts"; +import { setSnapshotSources, snapshotConfigs } from "./integrations/array-snapshot.ts"; import { homeAssistantRoutes, setHomeAssistant } from "./routes/home-assistant.ts"; -import { createHomeAssistant } from "./home-assistant-service.ts"; +import { createHomeAssistant } from "./integrations/home-assistant-service.ts"; import { startExternalJob } from "./routes/snapraid.ts"; -import { createSpindownMonitor } from "./spindown.ts"; -import { resumeLeftoverContainers } from "./container-pause.ts"; -import { resolveFromBase } from "./config.ts"; -import { withRestoredOwnership } from "./restore-ownership.ts"; -import { createAuth, disabledAuthRoutes, loadSessionSecret, readAuthEnv } from "./auth.ts"; -import { loadCredentials } from "./credentials.ts"; -import { APP_VERSION } from "./version.ts"; -import { type FetchHandler, isBackendPath, loadFrontend } from "./frontend.ts"; +import { createSpindownMonitor } from "./disks/spindown.ts"; +import { resumeLeftoverContainers } from "./system/container-pause.ts"; +import { resolveFromBase } from "./config/config.ts"; +import { withRestoredOwnership } from "./snapraid/restore-ownership.ts"; +import { createAuth, disabledAuthRoutes, loadSessionSecret, readAuthEnv } from "./auth/auth.ts"; +import { loadCredentials } from "./auth/credentials.ts"; +import { APP_VERSION } from "./system/version.ts"; +import { type FetchHandler, isBackendPath, loadFrontend } from "./http/frontend.ts"; // The env of each request is the Bun server that received it (Bun.serve passes it to fetch) const app = new Hono<{ Bindings: Server }>(); diff --git a/apps/server/src/notifications/__tests__/notifications.test.ts b/apps/server/src/notifications/__tests__/notifications.test.ts index 7c77ef1..78a9a9f 100644 --- a/apps/server/src/notifications/__tests__/notifications.test.ts +++ b/apps/server/src/notifications/__tests__/notifications.test.ts @@ -8,10 +8,10 @@ import { validateNotificationSettings, } from "../notifications.ts"; import { buildRunNotification, buildSkipNotification, diffDegradedDisks, diffSmartProblems, heartbeatAfterRun } from "../notification-events.ts"; -import { failedReport, parseRunReport } from "../run-report.ts"; -import { combineResults, scheduleSteps } from "../scheduler.ts"; +import { failedReport, parseRunReport } from "../../snapraid/run-report.ts"; +import { combineResults, scheduleSteps } from "../../schedule/scheduler.ts"; import { assessSmart, attributeLevel, smartHints } from "@shared/smart-health.ts"; -import { parseSmartOutput } from "../parsers/smart-parser.ts"; +import { parseSmartOutput } from "../../parsers/smart-parser.ts"; import { readFileSync } from "node:fs"; const SYNC_LOG = `summary:added:12 @@ -248,7 +248,7 @@ test("combineResults - first failure, otherwise the worst of ok and warning", () }); test("assessSmart - a few percent failure probability is normal, sectors and status are not", () => { - const disks = parseSmartOutput(readFileSync(new URL("../parsers/__tests__/fixtures/smart.log", import.meta.url), "utf8")); + const disks = parseSmartOutput(readFileSync(new URL("../../parsers/__tests__/fixtures/smart.log", import.meta.url), "utf8")); const byName = Object.fromEntries(disks.map((d) => [d.name, assessSmart(d)])); // 4.46% per year, no errors diff --git a/apps/server/src/notifications/notification-events.ts b/apps/server/src/notifications/notification-events.ts index 6e128b5..be4ca94 100644 --- a/apps/server/src/notifications/notification-events.ts +++ b/apps/server/src/notifications/notification-events.ts @@ -1,5 +1,5 @@ import { basename } from "node:path"; -import { existsSync } from "./fs.ts"; +import { existsSync } from "../system/fs.ts"; import type { NotificationSettings, ScheduleOutcome, @@ -7,11 +7,11 @@ import type { SnapRaidCommand, } from "@shared/types.ts"; import { assessSmart, type SmartAssessment, type SmartReason, smartHints, smartSignature } from "@shared/smart-health.ts"; -import { resolveFromBase } from "./config.ts"; +import { resolveFromBase } from "../config/config.ts"; import { loadNotificationSettings, type Notification, notify, pingHeartbeat } from "./notifications.ts"; -import { isSuccessful, type RunReport } from "./run-report.ts"; -import { parseLogTags, restOf, unescapeTagValue } from "./parsers/structured-log.ts"; -import { writeTextFileAtomic } from "./atomic-write.ts"; +import { isSuccessful, type RunReport } from "../snapraid/run-report.ts"; +import { parseLogTags, restOf, unescapeTagValue } from "../parsers/structured-log.ts"; +import { writeTextFileAtomic } from "../system/atomic-write.ts"; import { readFile } from "node:fs/promises"; type Language = NotificationSettings["language"]; diff --git a/apps/server/src/notifications/notifications.ts b/apps/server/src/notifications/notifications.ts index f89121b..5c026b6 100644 --- a/apps/server/src/notifications/notifications.ts +++ b/apps/server/src/notifications/notifications.ts @@ -1,5 +1,5 @@ import nodemailer from "nodemailer"; -import { existsSync } from "./fs.ts"; +import { existsSync } from "../system/fs.ts"; import { type NotificationChannel, type NotificationEvent, @@ -7,8 +7,8 @@ import { type NotificationTestResult, SECRET_MASK, } from "@shared/types.ts"; -import { resolveFromBase } from "./config.ts"; -import { writeTextFileAtomic } from "./atomic-write.ts"; +import { resolveFromBase } from "../config/config.ts"; +import { writeTextFileAtomic } from "../system/atomic-write.ts"; import { readFile } from "node:fs/promises"; export type Severity = "info" | "warning" | "error"; diff --git a/apps/server/src/parsers/__tests__/parsers.test.ts b/apps/server/src/parsers/__tests__/parsers.test.ts index a772be2..e952c48 100644 --- a/apps/server/src/parsers/__tests__/parsers.test.ts +++ b/apps/server/src/parsers/__tests__/parsers.test.ts @@ -8,7 +8,7 @@ import { parseSmartArrayFailure, parseSmartOutput } from "../smart-parser.ts"; import { parseDegradedDisks, parseProbeOutput } from "../probe-parser.ts"; import { parseDfOutput } from "../df-parser.ts"; import { isLockedOutput, parseLogTags, splitStructuredOutput, unescapeTagValue } from "../structured-log.ts"; -import { parseParityLine } from "../../config-parser.ts"; +import { parseParityLine } from "../../config/config-parser.ts"; import { readFileSync } from "node:fs"; // Fixtures are the stderr of `snapraid --log ">&2" ` (SnapRAID 14.9) on a demo array. diff --git a/apps/server/src/routes/__tests__/disk-management.test.ts b/apps/server/src/routes/__tests__/disk-management.test.ts index 4a14501..90a2dbd 100644 --- a/apps/server/src/routes/__tests__/disk-management.test.ts +++ b/apps/server/src/routes/__tests__/disk-management.test.ts @@ -1,6 +1,6 @@ import { expect, test } from "bun:test"; import { join } from "node:path"; -import { diskManagementRoutes } from "../routes/disk-management.ts"; +import { diskManagementRoutes } from "../disk-management.ts"; import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; diff --git a/apps/server/src/routes/__tests__/heal.test.ts b/apps/server/src/routes/__tests__/heal.test.ts index 5fa7c4d..1845559 100644 --- a/apps/server/src/routes/__tests__/heal.test.ts +++ b/apps/server/src/routes/__tests__/heal.test.ts @@ -1,7 +1,7 @@ import { expect, test } from "bun:test"; import type { RunResult, SnapRaidCommand } from "@shared/types.ts"; -import type { JobOutcome } from "../engine/engine.ts"; -import { heal } from "../routes/snapraid.ts"; +import type { JobOutcome } from "../../engine/engine.ts"; +import { heal } from "../snapraid.ts"; const outcome = (command: SnapRaidCommand, result: RunResult): JobOutcome => ({ output: { command, output: "", timestamp: "", exitCode: result === "ok" ? 0 : 1 }, diff --git a/apps/server/src/routes/config-operations.ts b/apps/server/src/routes/config-operations.ts index efac12d..102e414 100644 --- a/apps/server/src/routes/config-operations.ts +++ b/apps/server/src/routes/config-operations.ts @@ -1,8 +1,8 @@ import { Hono } from "hono"; import { join } from "node:path"; -import { parseSnapRaidConfig } from "../config-parser.ts"; -import { resolveFromBase } from "../config.ts"; -import { editSmartIgnore, isSmartIgnoreAttribute, SMART_IGNORE_MAX, SmartIgnoreFullError } from "../smart-ignore.ts"; +import { parseSnapRaidConfig } from "../config/config-parser.ts"; +import { resolveFromBase } from "../config/config.ts"; +import { editSmartIgnore, isSmartIgnoreAttribute, SMART_IGNORE_MAX, SmartIgnoreFullError } from "../smart/smart-ignore.ts"; import { msg } from "@shared/i18n.ts"; import { readFile, writeFile } from "node:fs/promises"; diff --git a/apps/server/src/routes/config.ts b/apps/server/src/routes/config.ts index b2ddd06..f35263b 100644 --- a/apps/server/src/routes/config.ts +++ b/apps/server/src/routes/config.ts @@ -1,12 +1,12 @@ import { Hono } from "hono"; import type { ConfigFileCheck } from "@shared/types.ts"; -import { loadAppConfig, parseSnapRaidConfig, saveAppConfig } from "../config-parser.ts"; -import { BASE_PATH, resolveFromBase, toStoredPath } from "../config.ts"; +import { loadAppConfig, parseSnapRaidConfig, saveAppConfig } from "../config/config-parser.ts"; +import { BASE_PATH, resolveFromBase, toStoredPath } from "../config/config.ts"; import { msg } from "@shared/i18n.ts"; -import { BackupError, createBackup, restoreBackup } from "../backup.ts"; +import { BackupError, createBackup, restoreBackup } from "../config/backup.ts"; import { reloadSchedules } from "./schedules.ts"; import { stat, writeFile } from "node:fs/promises"; -import { isAlreadyExists } from "../fs.ts"; +import { isAlreadyExists } from "../system/fs.ts"; const config = new Hono(); diff --git a/apps/server/src/routes/disk-management.ts b/apps/server/src/routes/disk-management.ts index 1364cec..6003cdc 100644 --- a/apps/server/src/routes/disk-management.ts +++ b/apps/server/src/routes/disk-management.ts @@ -5,8 +5,8 @@ import { parityKeyword, parseParityLine, parseSnapRaidConfig, -} from "../config-parser.ts"; -import { resolveFromBase } from "../config.ts"; +} from "../config/config-parser.ts"; +import { resolveFromBase } from "../config/config.ts"; import { msg } from "@shared/i18n.ts"; import { readFile, writeFile } from "node:fs/promises"; diff --git a/apps/server/src/routes/disk-replacement.ts b/apps/server/src/routes/disk-replacement.ts index f7b9517..e03222e 100644 --- a/apps/server/src/routes/disk-replacement.ts +++ b/apps/server/src/routes/disk-replacement.ts @@ -1,6 +1,6 @@ import { Hono } from "hono"; import type { ReplacementStep, SnapRaidCommand } from "@shared/types.ts"; -import { resolveFromBase } from "../config.ts"; +import { resolveFromBase } from "../config/config.ts"; import { applyReplacementPath, clearReplacement, @@ -9,7 +9,7 @@ import { recordReplacementStep, requiredDirectory, saveReplacement, -} from "../disk-replacement.ts"; +} from "../disks/disk-replacement.ts"; import type { JobOutcome } from "../engine/engine.ts"; import { msg } from "@shared/i18n.ts"; import { readFile, stat, writeFile } from "node:fs/promises"; diff --git a/apps/server/src/routes/filesystem.ts b/apps/server/src/routes/filesystem.ts index 075c477..efb1272 100644 --- a/apps/server/src/routes/filesystem.ts +++ b/apps/server/src/routes/filesystem.ts @@ -1,6 +1,6 @@ import { Hono } from "hono"; import { join } from "node:path"; -import { BASE_PATH, resolveFromBase } from "../config.ts"; +import { BASE_PATH, resolveFromBase } from "../config/config.ts"; import { readdir, readFile, writeFile } from "node:fs/promises"; const filesystem = new Hono(); diff --git a/apps/server/src/routes/hardware.ts b/apps/server/src/routes/hardware.ts index bf6a7c6..9f66707 100644 --- a/apps/server/src/routes/hardware.ts +++ b/apps/server/src/routes/hardware.ts @@ -1,12 +1,12 @@ import { Hono } from "hono"; -import { resolveFromBase } from "../config.ts"; -import { parseSnapRaidConfig } from "../config-parser.ts"; -import { getDataDiskUsage, getParityUsage } from "../parity-usage.ts"; -import { withCrcBaseline } from "../smart-baseline.ts"; -import { getSmartHistory, recordSmartHistory } from "../smart-history.ts"; +import { resolveFromBase } from "../config/config.ts"; +import { parseSnapRaidConfig } from "../config/config-parser.ts"; +import { getDataDiskUsage, getParityUsage } from "../disks/parity-usage.ts"; +import { withCrcBaseline } from "../smart/smart-baseline.ts"; +import { getSmartHistory, recordSmartHistory } from "../smart/smart-history.ts"; import { EngineCommandError, EngineUnsupportedError, getEngine } from "../engine/engine.ts"; -import { type PowerAction, setDiskPower } from "../disk-power.ts"; -import { controlSelfTests, readSelfTests } from "../smart-selftest.ts"; +import { type PowerAction, setDiskPower } from "../disks/disk-power.ts"; +import { controlSelfTests, readSelfTests } from "../smart/smart-selftest.ts"; import { msg } from "@shared/i18n.ts"; const hardware = new Hono(); diff --git a/apps/server/src/routes/home-assistant.ts b/apps/server/src/routes/home-assistant.ts index 2592caf..7c00ef8 100644 --- a/apps/server/src/routes/home-assistant.ts +++ b/apps/server/src/routes/home-assistant.ts @@ -7,7 +7,7 @@ import { normalizeHomeAssistantSettings, saveHomeAssistantSettings, testHomeAssistantConnection, -} from "../home-assistant-service.ts"; +} from "../integrations/home-assistant-service.ts"; const homeAssistant = new Hono(); diff --git a/apps/server/src/routes/logs.ts b/apps/server/src/routes/logs.ts index 365dd51..91dbac4 100644 --- a/apps/server/src/routes/logs.ts +++ b/apps/server/src/routes/logs.ts @@ -1,5 +1,5 @@ import { Hono } from "hono"; -import type { LogManager } from "../log-manager.ts"; +import type { LogManager } from "../snapraid/log-manager.ts"; import type { AppConfig } from "@shared/types.ts"; const logs = new Hono(); diff --git a/apps/server/src/routes/maintenance.ts b/apps/server/src/routes/maintenance.ts index 8ccefa1..d881ef6 100644 --- a/apps/server/src/routes/maintenance.ts +++ b/apps/server/src/routes/maintenance.ts @@ -4,9 +4,9 @@ import { loadMaintenanceSettings, normalizeMaintenanceSettings, saveMaintenanceSettings, -} from "../maintenance-settings.ts"; -import { dockerSocket, listContainers, ownContainerId } from "../docker.ts"; -import type { SpindownMonitor } from "../spindown.ts"; +} from "../config/maintenance-settings.ts"; +import { dockerSocket, listContainers, ownContainerId } from "../system/docker.ts"; +import type { SpindownMonitor } from "../disks/spindown.ts"; const maintenance = new Hono(); diff --git a/apps/server/src/routes/mcp.ts b/apps/server/src/routes/mcp.ts index 4bcd445..d1ac734 100644 --- a/apps/server/src/routes/mcp.ts +++ b/apps/server/src/routes/mcp.ts @@ -1,18 +1,18 @@ import { Hono } from "hono"; import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js"; -import { safeEqual } from "../auth.ts"; -import { snapshotConfigs } from "../array-snapshot.ts"; -import { loadAppConfig, parseSnapRaidConfig } from "../config-parser.ts"; -import { BASE_PATH, resolveFromBase } from "../config.ts"; -import { APP_VERSION } from "../version.ts"; +import { safeEqual } from "../auth/auth.ts"; +import { snapshotConfigs } from "../integrations/array-snapshot.ts"; +import { loadAppConfig, parseSnapRaidConfig } from "../config/config-parser.ts"; +import { BASE_PATH, resolveFromBase } from "../config/config.ts"; +import { APP_VERSION } from "../system/version.ts"; import { getEngine } from "../engine/engine.ts"; -import type { LogManager } from "../log-manager.ts"; -import { loadMaintenanceSettings } from "../maintenance-settings.ts"; -import { createMcpServer, DOCS_DIR } from "../mcp.ts"; -import type { Scheduler } from "../scheduler.ts"; -import { createArray, listMountCandidates, missingSetupPaths } from "../setup.ts"; -import { rememberStatus } from "../status-cache.ts"; -import { getUsageHistory, recordUsage } from "../usage-history.ts"; +import type { LogManager } from "../snapraid/log-manager.ts"; +import { loadMaintenanceSettings } from "../config/maintenance-settings.ts"; +import { createMcpServer, DOCS_DIR } from "../integrations/mcp.ts"; +import type { Scheduler } from "../schedule/scheduler.ts"; +import { createArray, listMountCandidates, missingSetupPaths } from "../config/setup.ts"; +import { rememberStatus } from "../snapraid/status-cache.ts"; +import { getUsageHistory, recordUsage } from "../snapraid/usage-history.ts"; import { startExternalJob } from "./snapraid.ts"; // Log manager and scheduler will be injected diff --git a/apps/server/src/routes/metrics.ts b/apps/server/src/routes/metrics.ts index 65479b2..90bd726 100644 --- a/apps/server/src/routes/metrics.ts +++ b/apps/server/src/routes/metrics.ts @@ -1,8 +1,8 @@ import { Hono } from "hono"; -import { snapshotConfigs, snapshotSchedules } from "../array-snapshot.ts"; +import { snapshotConfigs, snapshotSchedules } from "../integrations/array-snapshot.ts"; import { getEngine } from "../engine/engine.ts"; -import { loadMaintenanceSettings } from "../maintenance-settings.ts"; -import { renderMetrics } from "../metrics.ts"; +import { loadMaintenanceSettings } from "../config/maintenance-settings.ts"; +import { renderMetrics } from "../integrations/metrics.ts"; const metrics = new Hono(); diff --git a/apps/server/src/routes/notifications.ts b/apps/server/src/routes/notifications.ts index c07bbfb..3b2dded 100644 --- a/apps/server/src/routes/notifications.ts +++ b/apps/server/src/routes/notifications.ts @@ -8,7 +8,7 @@ import { resolveSecrets, saveNotificationSettings, validateNotificationSettings, -} from "../notifications.ts"; +} from "../notifications/notifications.ts"; import { msg } from "@shared/i18n.ts"; const notifications = new Hono(); diff --git a/apps/server/src/routes/reports.ts b/apps/server/src/routes/reports.ts index 8c89b21..c8da67e 100644 --- a/apps/server/src/routes/reports.ts +++ b/apps/server/src/routes/reports.ts @@ -1,9 +1,9 @@ import { Hono } from "hono"; -import type { SnapRaidRunner } from "../snapraid-runner.ts"; -import { resolveFromBase } from "../config.ts"; +import type { SnapRaidRunner } from "../snapraid/snapraid-runner.ts"; +import { resolveFromBase } from "../config/config.ts"; import { getEngine } from "../engine/engine.ts"; -import { parseSnapRaidConfig } from "../config-parser.ts"; -import { changedPaths, deleteDuplicates, insideDisk, markGone, MAX_DUPLICATE_DELETIONS } from "../duplicates.ts"; +import { parseSnapRaidConfig } from "../config/config-parser.ts"; +import { changedPaths, deleteDuplicates, insideDisk, markGone, MAX_DUPLICATE_DELETIONS } from "../snapraid/duplicates.ts"; import type { DiffReport, DuplicateDeletion } from "@shared/types.ts"; import { msg } from "@shared/i18n.ts"; import { stat } from "node:fs/promises"; diff --git a/apps/server/src/routes/schedules.ts b/apps/server/src/routes/schedules.ts index 8b5bf6c..afbc3a3 100644 --- a/apps/server/src/routes/schedules.ts +++ b/apps/server/src/routes/schedules.ts @@ -1,5 +1,5 @@ import { Hono } from "hono"; -import type { Scheduler } from "../scheduler.ts"; +import type { Scheduler } from "../schedule/scheduler.ts"; import { msg } from "@shared/i18n.ts"; const schedules = new Hono(); diff --git a/apps/server/src/routes/setup.ts b/apps/server/src/routes/setup.ts index dc63ec4..0af9fb9 100644 --- a/apps/server/src/routes/setup.ts +++ b/apps/server/src/routes/setup.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import type { ArraySetup } from "@shared/array-setup.ts"; import { msg } from "@shared/i18n.ts"; -import { createArray, listMountCandidates } from "../setup.ts"; +import { createArray, listMountCandidates } from "../config/setup.ts"; const setup = new Hono(); diff --git a/apps/server/src/routes/snapraid.ts b/apps/server/src/routes/snapraid.ts index 7fd769f..235252f 100644 --- a/apps/server/src/routes/snapraid.ts +++ b/apps/server/src/routes/snapraid.ts @@ -1,29 +1,29 @@ import { Hono } from "hono"; -import { parseSnapRaidConfig } from "../config-parser.ts"; -import { createSnapRaidRunner } from "../snapraid-runner.ts"; -import type { LogManager } from "../log-manager.ts"; +import { parseSnapRaidConfig } from "../config/config-parser.ts"; +import { createSnapRaidRunner } from "../snapraid/snapraid-runner.ts"; +import type { LogManager } from "../snapraid/log-manager.ts"; import type { RestoreFile, SnapRaidCommand } from "@shared/types.ts"; -import { snapraidCommand, resolveFromBase } from "../config.ts"; +import { snapraidCommand, resolveFromBase } from "../config/config.ts"; import {diskManagementRoutes} from "./disk-management.ts"; -import { DiskRemovalError, ensureEmptyDir, finalizeDataDiskRemoval, prepareDataDiskRemoval } from "../disk-removal.ts"; +import { DiskRemovalError, ensureEmptyDir, finalizeDataDiskRemoval, prepareDataDiskRemoval } from "../disks/disk-removal.ts"; import {configOperationsRoutes} from "./config-operations.ts"; import {hardwareRoutes} from "./hardware.ts"; import { setReportsRunner, reportsRoutes } from "./reports.ts"; -import { getUsageHistory, recordUsage } from "../usage-history.ts"; -import { rememberStatus } from "../status-cache.ts"; +import { getUsageHistory, recordUsage } from "../snapraid/usage-history.ts"; +import { rememberStatus } from "../snapraid/status-cache.ts"; import { parseStatusOutput } from "../parsers/status-parser.ts"; import { parseCheckOutput } from "../parsers/check-parser.ts"; import { createDiskReplacementRoutes } from "./disk-replacement.ts"; -import { notifyManualRun, notifySkipped } from "../notification-events.ts"; -import { checkSyncGuard } from "../scheduler.ts"; -import { isSuccessful } from "../run-report.ts"; -import { MAX_RESTORE_FILES, restoreFiles } from "../recovery.ts"; -import { findDiskIssues } from "../disk-check.ts"; +import { notifyManualRun, notifySkipped } from "../notifications/notification-events.ts"; +import { checkSyncGuard } from "../schedule/scheduler.ts"; +import { isSuccessful } from "../snapraid/run-report.ts"; +import { MAX_RESTORE_FILES, restoreFiles } from "../snapraid/recovery.ts"; +import { findDiskIssues } from "../disks/disk-check.ts"; import { msg } from "@shared/i18n.ts"; import { EngineBusyError, getEngine, type JobOutcome } from "../engine/engine.ts"; import { readFile, writeFile } from "node:fs/promises"; -import { remove } from "../fs.ts"; -import { runCommand } from "../command.ts"; +import { remove } from "../system/fs.ts"; +import { runCommand } from "../system/command.ts"; const snapraid = new Hono(); diff --git a/apps/server/src/schedule/__tests__/scheduler.test.ts b/apps/server/src/schedule/__tests__/scheduler.test.ts index 339f056..ee53096 100644 --- a/apps/server/src/schedule/__tests__/scheduler.test.ts +++ b/apps/server/src/schedule/__tests__/scheduler.test.ts @@ -1,9 +1,9 @@ import { expect, test } from "bun:test"; -import { assertRejects } from "./assert-error.ts"; +import { assertRejects } from "../../__tests__/assert-error.ts"; import { join } from "node:path"; import type { Schedule, ScheduleConfig } from "@shared/types.ts"; import { createScheduler, executeScheduledCommand } from "../scheduler.ts"; -import { createFakeEngine, type FakeEngineOptions } from "./fake-engine.ts"; +import { createFakeEngine, type FakeEngineOptions } from "../../engine/__tests__/fake-engine.ts"; import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; diff --git a/apps/server/src/schedule/scheduler.ts b/apps/server/src/schedule/scheduler.ts index d41affd..f05626e 100644 --- a/apps/server/src/schedule/scheduler.ts +++ b/apps/server/src/schedule/scheduler.ts @@ -7,19 +7,19 @@ import type { ScheduleStepOutcome, SnapRaidCommand, } from "@shared/types.ts"; -import type { SnapRaidEngine } from "./engine/engine.ts"; -import { existsSync } from "./fs.ts"; -import { resolveFromBase } from "./config.ts"; -import { failedReport, isSuccessful, type RunReport } from "./run-report.ts"; -import { isReplacementInProgress } from "./disk-replacement.ts"; -import { withCrcBaseline } from "./smart-baseline.ts"; -import { recordSmartHistory } from "./smart-history.ts"; -import { heartbeatAfterRun, notifyRun, notifySkipped, notifySmart } from "./notification-events.ts"; +import type { SnapRaidEngine } from "../engine/engine.ts"; +import { existsSync } from "../system/fs.ts"; +import { resolveFromBase } from "../config/config.ts"; +import { failedReport, isSuccessful, type RunReport } from "../snapraid/run-report.ts"; +import { isReplacementInProgress } from "../disks/disk-replacement.ts"; +import { withCrcBaseline } from "../smart/smart-baseline.ts"; +import { recordSmartHistory } from "../smart/smart-history.ts"; +import { heartbeatAfterRun, notifyRun, notifySkipped, notifySmart } from "../notifications/notification-events.ts"; import { msg } from "@shared/i18n.ts"; -import { blockingIssues } from "./disk-check.ts"; -import { holdContainers } from "./container-pause.ts"; -import { rememberStatus } from "./status-cache.ts"; -import { writeTextFileAtomic } from "./atomic-write.ts"; +import { blockingIssues } from "../disks/disk-check.ts"; +import { holdContainers } from "../system/container-pause.ts"; +import { rememberStatus } from "../snapraid/status-cache.ts"; +import { writeTextFileAtomic } from "../system/atomic-write.ts"; import { readFile } from "node:fs/promises"; // Module-level storage for active jobs diff --git a/apps/server/src/smart/__tests__/smart-ignore.test.ts b/apps/server/src/smart/__tests__/smart-ignore.test.ts index 2874ea7..b04b970 100644 --- a/apps/server/src/smart/__tests__/smart-ignore.test.ts +++ b/apps/server/src/smart/__tests__/smart-ignore.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { assertThrows } from "./assert-error.ts"; +import { assertThrows } from "../../__tests__/assert-error.ts"; import { join } from "node:path"; import { editSmartIgnore, @@ -8,7 +8,7 @@ import { parseSmartIgnoreLine, SmartIgnoreFullError, } from "../smart-ignore.ts"; -import { parseSnapRaidConfig } from "../config-parser.ts"; +import { parseSnapRaidConfig } from "../../config/config-parser.ts"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; diff --git a/apps/server/src/smart/smart-baseline.ts b/apps/server/src/smart/smart-baseline.ts index 1a7d429..84a379b 100644 --- a/apps/server/src/smart/smart-baseline.ts +++ b/apps/server/src/smart/smart-baseline.ts @@ -1,8 +1,8 @@ -import { existsSync } from "./fs.ts"; +import { existsSync } from "../system/fs.ts"; import type { SmartDiskInfo } from "@shared/types.ts"; import { CRC_ATTRIBUTE_ID } from "@shared/smart-health.ts"; -import { resolveFromBase } from "./config.ts"; -import { writeTextFileAtomic } from "./atomic-write.ts"; +import { resolveFromBase } from "../config/config.ts"; +import { writeTextFileAtomic } from "../system/atomic-write.ts"; import { readFile } from "node:fs/promises"; const STATE_FILE = "smart-baseline.json"; diff --git a/apps/server/src/smart/smart-history.ts b/apps/server/src/smart/smart-history.ts index fc60992..099822b 100644 --- a/apps/server/src/smart/smart-history.ts +++ b/apps/server/src/smart/smart-history.ts @@ -1,7 +1,7 @@ -import { existsSync } from "./fs.ts"; +import { existsSync } from "../system/fs.ts"; import type { SmartDiskInfo, SmartHistoryPoint } from "@shared/types.ts"; -import { resolveFromBase } from "./config.ts"; -import { writeTextFileAtomic } from "./atomic-write.ts"; +import { resolveFromBase } from "../config/config.ts"; +import { writeTextFileAtomic } from "../system/atomic-write.ts"; import { readFile } from "node:fs/promises"; const STATE_FILE = "smart-history.json"; diff --git a/apps/server/src/smart/smart-selftest.ts b/apps/server/src/smart/smart-selftest.ts index d85d2c6..9749508 100644 --- a/apps/server/src/smart/smart-selftest.ts +++ b/apps/server/src/smart/smart-selftest.ts @@ -3,10 +3,10 @@ // runs it on its own; its status and the log of the last tests come from `smartctl -c -l selftest`. // smartctl's JSON output (-j) covers ATA and NVMe disks alike. import type { DeviceInfo, DiskSelfTest, SelfTestLogEntry, SelfTestType } from "@shared/types.ts"; -import { executeSnapraidCommand } from "./executors/command-executor.ts"; -import { parseDevicesOutput } from "./parsers/devices-parser.ts"; -import { runCommand } from "./command.ts"; -import { DEMO_MODE, demoDevices, demoDisks } from "./demo.ts"; +import { executeSnapraidCommand } from "../executors/command-executor.ts"; +import { parseDevicesOutput } from "../parsers/devices-parser.ts"; +import { runCommand } from "../system/command.ts"; +import { DEMO_MODE, demoDevices, demoDisks } from "../system/demo.ts"; // The image installs smartmontools; tests point it to a stand-in const smartctlBin = () => process.env.SMARTCTL_BIN ?? "smartctl"; diff --git a/apps/server/src/snapraid/__tests__/log-manager.test.ts b/apps/server/src/snapraid/__tests__/log-manager.test.ts index 3b34c1d..94930b5 100644 --- a/apps/server/src/snapraid/__tests__/log-manager.test.ts +++ b/apps/server/src/snapraid/__tests__/log-manager.test.ts @@ -55,7 +55,7 @@ test("parseRunResult - diff and dup that found something succeeded", () => { const fixture = (name: string) => - readFileSync(new URL(`../parsers/__tests__/fixtures/${name}`, import.meta.url), "utf8"); + readFileSync(new URL(`../../parsers/__tests__/fixtures/${name}`, import.meta.url), "utf8"); // The touch log of issue #48 ends with the saved content file, the smart log with the array failure probability const TOUCH_LOG = fixture("touch.log"); diff --git a/apps/server/src/snapraid/__tests__/recovery.test.ts b/apps/server/src/snapraid/__tests__/recovery.test.ts index 5b60715..5e7bd0a 100644 --- a/apps/server/src/snapraid/__tests__/recovery.test.ts +++ b/apps/server/src/snapraid/__tests__/recovery.test.ts @@ -1,6 +1,6 @@ import { expect, test } from "bun:test"; import type { RunResult, SnapRaidCommand } from "@shared/types.ts"; -import type { JobOutcome } from "../engine/engine.ts"; +import type { JobOutcome } from "../../engine/engine.ts"; import { exactFilter, restoreFiles, restoreRuns } from "../recovery.ts"; test("exactFilter - anchored at the disk root, wildcards taken literally", () => { diff --git a/apps/server/src/snapraid/__tests__/restore-ownership.test.ts b/apps/server/src/snapraid/__tests__/restore-ownership.test.ts index 90f2e74..c12ab75 100644 --- a/apps/server/src/snapraid/__tests__/restore-ownership.test.ts +++ b/apps/server/src/snapraid/__tests__/restore-ownership.test.ts @@ -3,7 +3,7 @@ import { join } from "node:path"; import { adoptFolderOwnership, fileMode, folderMode, recreatedFiles } from "../restore-ownership.ts"; import { chmod, chown, mkdir, mkdtemp, rm, stat, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; -import { writeTextFile } from "../fs.ts"; +import { writeTextFile } from "../../system/fs.ts"; // From a real `snapraid fix`: a/two was changed (fixed in place), a/one and a/we:ird were deleted const FIX_LOG = [ diff --git a/apps/server/src/snapraid/__tests__/run-result.test.ts b/apps/server/src/snapraid/__tests__/run-result.test.ts index 464eb5c..d72944f 100644 --- a/apps/server/src/snapraid/__tests__/run-result.test.ts +++ b/apps/server/src/snapraid/__tests__/run-result.test.ts @@ -4,7 +4,7 @@ import type { RunResult } from "@shared/types.ts"; import { readFileSync } from "node:fs"; const fixture = (name: string) => - readFileSync(new URL(`../parsers/__tests__/fixtures/${name}`, import.meta.url), "utf8"); + readFileSync(new URL(`../../parsers/__tests__/fixtures/${name}`, import.meta.url), "utf8"); const lines = (...tags: string[]) => tags.join("\n"); diff --git a/apps/server/src/snapraid/duplicates.ts b/apps/server/src/snapraid/duplicates.ts index 5f16b58..baabfc7 100644 --- a/apps/server/src/snapraid/duplicates.ts +++ b/apps/server/src/snapraid/duplicates.ts @@ -5,7 +5,7 @@ import { join, normalize, sep } from "node:path"; import type { DiffReport, DuplicateDeletion, DuplicateSkip, DupReport } from "@shared/types.ts"; import { lstat } from "node:fs/promises"; -import { remove as removeFile } from "./fs.ts"; +import { remove as removeFile } from "../system/fs.ts"; // One request cleans up this many copies at most export const MAX_DUPLICATE_DELETIONS = 5000; diff --git a/apps/server/src/snapraid/log-manager.ts b/apps/server/src/snapraid/log-manager.ts index bc30d8d..c516b14 100644 --- a/apps/server/src/snapraid/log-manager.ts +++ b/apps/server/src/snapraid/log-manager.ts @@ -4,7 +4,7 @@ import { join, resolve } from "node:path"; import type { SnapRaidCommand, LogFile, LastRun, RunResult } from "@shared/types.ts"; import { detectForceOption } from "@shared/force-option.ts"; import { parseRunResult } from "@shared/run-result.ts"; -import { isAlreadyExists, isNotFound, remove } from "./fs.ts"; +import { isAlreadyExists, isNotFound, remove } from "../system/fs.ts"; export interface LogManager { ensureLogDirectory(): Promise; diff --git a/apps/server/src/snapraid/recovery.ts b/apps/server/src/snapraid/recovery.ts index 16f1504..b9f2b9f 100644 --- a/apps/server/src/snapraid/recovery.ts +++ b/apps/server/src/snapraid/recovery.ts @@ -3,7 +3,7 @@ // both, until the next sync records the new state. import type { RestoreFile, SnapRaidCommand } from "@shared/types.ts"; import { isSuccessful } from "./run-report.ts"; -import type { JobOutcome } from "./engine/engine.ts"; +import type { JobOutcome } from "../engine/engine.ts"; // More filters make the command line long and gain little, "all missing" (-m) covers the rest export const MAX_RESTORE_FILES = 1000; diff --git a/apps/server/src/snapraid/restore-ownership.ts b/apps/server/src/snapraid/restore-ownership.ts index 63ec73a..6e93cf5 100644 --- a/apps/server/src/snapraid/restore-ownership.ts +++ b/apps/server/src/snapraid/restore-ownership.ts @@ -7,9 +7,9 @@ import type { Stats } from "node:fs"; import { chmod, chown, lstat } from "node:fs/promises"; import { dirname, join } from "node:path"; import { msg } from "@shared/i18n.ts"; -import { parseSnapRaidConfig } from "./config-parser.ts"; -import { type SnapRaidEngine, wrapJobs } from "./engine/engine.ts"; -import { parseLogTags, unescapeTagValue } from "./parsers/structured-log.ts"; +import { parseSnapRaidConfig } from "../config/config-parser.ts"; +import { type SnapRaidEngine, wrapJobs } from "../engine/engine.ts"; +import { parseLogTags, unescapeTagValue } from "../parsers/structured-log.ts"; export interface RestoredFile { disk: string; diff --git a/apps/server/src/snapraid/run-report.ts b/apps/server/src/snapraid/run-report.ts index f39d6f2..5074065 100644 --- a/apps/server/src/snapraid/run-report.ts +++ b/apps/server/src/snapraid/run-report.ts @@ -1,7 +1,7 @@ import { basename } from "node:path"; import type { CommandOutput, RunResult, SnapRaidCommand } from "@shared/types.ts"; import { parseRunResult } from "./log-manager.ts"; -import { collectKeyValues, parseLogTags, toInt } from "./parsers/structured-log.ts"; +import { collectKeyValues, parseLogTags, toInt } from "../parsers/structured-log.ts"; import { readFile } from "node:fs/promises"; /** diff --git a/apps/server/src/snapraid/snapraid-runner.ts b/apps/server/src/snapraid/snapraid-runner.ts index d0a295a..884605d 100644 --- a/apps/server/src/snapraid/snapraid-runner.ts +++ b/apps/server/src/snapraid/snapraid-runner.ts @@ -1,11 +1,11 @@ // Reports only the SnapRAID CLI gives, whatever engine runs the jobs (engine/engine.ts): // they read the content file or the devices, snapraid-daemon has no API for them import type { DevicesReport, DupReport, ListReport } from "@shared/types.ts"; -import { executeSnapraidCommand } from "./executors/command-executor.ts"; -import { parseDevicesOutput } from "./parsers/devices-parser.ts"; -import { LIST_ARGS, parseListOutput } from "./parsers/list-parser.ts"; -import { parseDupOutput } from "./parsers/dup-parser.ts"; -import { STRUCTURED_LOG_ARGS, splitStructuredOutput } from "./parsers/structured-log.ts"; +import { executeSnapraidCommand } from "../executors/command-executor.ts"; +import { parseDevicesOutput } from "../parsers/devices-parser.ts"; +import { LIST_ARGS, parseListOutput } from "../parsers/list-parser.ts"; +import { parseDupOutput } from "../parsers/dup-parser.ts"; +import { STRUCTURED_LOG_ARGS, splitStructuredOutput } from "../parsers/structured-log.ts"; /** * Run a SnapRAID command with the structured log on stderr. diff --git a/apps/server/src/snapraid/usage-history.ts b/apps/server/src/snapraid/usage-history.ts index 7b6e743..ef6e6c8 100644 --- a/apps/server/src/snapraid/usage-history.ts +++ b/apps/server/src/snapraid/usage-history.ts @@ -1,7 +1,7 @@ -import { existsSync } from "./fs.ts"; +import { existsSync } from "../system/fs.ts"; import type { SnapRaidStatus, UsagePoint } from "@shared/types.ts"; -import { resolveFromBase } from "./config.ts"; -import { writeTextFileAtomic } from "./atomic-write.ts"; +import { resolveFromBase } from "../config/config.ts"; +import { writeTextFileAtomic } from "../system/atomic-write.ts"; import { readFile } from "node:fs/promises"; const STATE_FILE = "usage-history.json"; diff --git a/apps/server/src/system/__tests__/atomic-write.test.ts b/apps/server/src/system/__tests__/atomic-write.test.ts index 8935e84..9faada4 100644 --- a/apps/server/src/system/__tests__/atomic-write.test.ts +++ b/apps/server/src/system/__tests__/atomic-write.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { assertRejects } from "./assert-error.ts"; +import { assertRejects } from "../../__tests__/assert-error.ts"; import { readdirSync } from "node:fs"; import { join } from "node:path"; import { writeTextFileAtomic } from "../atomic-write.ts"; diff --git a/apps/server/src/system/__tests__/demo.test.ts b/apps/server/src/system/__tests__/demo.test.ts index 7644857..c561288 100644 --- a/apps/server/src/system/__tests__/demo.test.ts +++ b/apps/server/src/system/__tests__/demo.test.ts @@ -2,10 +2,10 @@ import { expect, test } from "bun:test"; import type { ParsedSnapRaidConfig } from "@shared/types.ts"; import { demoDataDiskUsage, demoDocker, demoParityUsage, demoStatusLog } from "../demo.ts"; import { inspectContainer, listContainers, pauseContainer, unpauseContainer } from "../docker.ts"; -import { parseStatusOutput } from "../parsers/status-parser.ts"; +import { parseStatusOutput } from "../../parsers/status-parser.ts"; import { readFile } from "node:fs/promises"; -const STATUS_LOG = await readFile(new URL("../parsers/__tests__/fixtures/status.log", import.meta.url), "utf8"); +const STATUS_LOG = await readFile(new URL("../../parsers/__tests__/fixtures/status.log", import.meta.url), "utf8"); // The fixture's array: two data disks and one parity const CONFIG = { diff --git a/apps/server/src/system/__tests__/docker.test.ts b/apps/server/src/system/__tests__/docker.test.ts index 72ee8ed..96c1db6 100644 --- a/apps/server/src/system/__tests__/docker.test.ts +++ b/apps/server/src/system/__tests__/docker.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { assertRejects } from "./assert-error.ts"; +import { assertRejects } from "../../__tests__/assert-error.ts"; import { containerIdFromMountinfo, type DockerRequest, listContainers, parseHttpResponse } from "../docker.ts"; import { pauseContainers, resumeContainers } from "../container-pause.ts"; diff --git a/apps/server/src/system/app-files.ts b/apps/server/src/system/app-files.ts index 0176d12..5e4d487 100644 --- a/apps/server/src/system/app-files.ts +++ b/apps/server/src/system/app-files.ts @@ -3,7 +3,7 @@ // (scripts/build-binary.sh) embeds them at the same paths in its own file system, /$bunfs/root/ const COMPILED = import.meta.url.startsWith("file:///$bunfs/"); -const ROOT = COMPILED ? new URL("file:///$bunfs/root/") : new URL("../../../", import.meta.url); +const ROOT = COMPILED ? new URL("file:///$bunfs/root/") : new URL("../../../../", import.meta.url); /** * A file of the app by its path from the repository's root, e.g. "apps/web/package.json" diff --git a/apps/server/src/system/container-pause.ts b/apps/server/src/system/container-pause.ts index 195171c..adbf820 100644 --- a/apps/server/src/system/container-pause.ts +++ b/apps/server/src/system/container-pause.ts @@ -4,7 +4,7 @@ import { existsSync, remove } from "./fs.ts"; import type { SnapRaidCommand } from "@shared/types.ts"; import { msg } from "@shared/i18n.ts"; -import { resolveFromBase } from "./config.ts"; +import { resolveFromBase } from "../config/config.ts"; import { dockerSocket, type DockerRequest, @@ -13,7 +13,7 @@ import { pauseContainer, unpauseContainer, } from "./docker.ts"; -import { loadMaintenanceSettings } from "./maintenance-settings.ts"; +import { loadMaintenanceSettings } from "../config/maintenance-settings.ts"; import { writeTextFileAtomic } from "./atomic-write.ts"; import { readFile } from "node:fs/promises"; diff --git a/apps/server/src/system/demo.ts b/apps/server/src/system/demo.ts index 4cfc11d..51477b9 100644 --- a/apps/server/src/system/demo.ts +++ b/apps/server/src/system/demo.ts @@ -4,7 +4,7 @@ // The sandbox disks also hold only a few KiB on the host's filesystem: `status` and the // disk usage get the sizes of the demo disks, so the dashboard looks like a real array. import type { DataDiskUsage, DeviceInfo, ParityLevelUsage, ParsedSnapRaidConfig } from "@shared/types.ts"; -import { parseSnapRaidConfig } from "./config-parser.ts"; +import { parseSnapRaidConfig } from "../config/config-parser.ts"; import type { DockerRequest } from "./docker.ts"; export const DEMO_MODE = process.env.SNAPRAID_DEMO === "1"; diff --git a/apps/web/src/components/AccountDialog.tsx b/apps/web/src/components/AccountDialog.tsx index 9ccf010..81dd4e5 100644 --- a/apps/web/src/components/AccountDialog.tsx +++ b/apps/web/src/components/AccountDialog.tsx @@ -31,7 +31,7 @@ import { import { Input } from './ui/input' import { Label } from './ui/label' -// The backend's minimum (MIN_PASSWORD_LENGTH in apps/server/src/auth.ts) +// The backend's minimum (MIN_PASSWORD_LENGTH in apps/server/src/auth/auth.ts) const MIN_PASSWORD_LENGTH = 12 const MAX_NAME_LENGTH = 64 diff --git a/apps/web/src/components/RemoveDataDiskWizard.tsx b/apps/web/src/components/RemoveDataDiskWizard.tsx index ce47ef3..5db9413 100644 --- a/apps/web/src/components/RemoveDataDiskWizard.tsx +++ b/apps/web/src/components/RemoveDataDiskWizard.tsx @@ -26,7 +26,7 @@ import { DialogTitle, } from './ui/dialog' -// Must match REMOVAL_DIR in apps/server/src/config-parser.ts +// Must match REMOVAL_DIR in apps/server/src/config/config-parser.ts const REMOVAL_DIR = '.snapraid-removal' const JOB_POLL_MS = 2000 diff --git a/docker/Dockerfile b/docker/Dockerfile index e9a6e9b..e474a64 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -58,9 +58,9 @@ COPY --from=backend-builder /app/tsconfig.base.json /app/tsconfig.base.json COPY --from=backend-builder /app/node_modules /app/node_modules COPY --from=backend-builder /app/apps/server /app/apps/server COPY packages/shared/ /app/packages/shared/ -# The backend loads the frontend's server from here (apps/server/src/frontend.ts) +# The backend loads the frontend's server from here (apps/server/src/http/frontend.ts) COPY --from=frontend-builder /app/apps/web/.output /app/apps/web/.output -# The version, which the backend reads from it (apps/server/src/version.ts) +# The version, which the backend reads from it (apps/server/src/system/version.ts) COPY apps/web/package.json /app/apps/web/package.json # The documentation, which the MCP endpoint offers to AI agents COPY docs/*.md /app/docs/ diff --git a/docs/architecture.md b/docs/architecture.md index e27a64c..c264bf9 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -45,7 +45,7 @@ The container runs a single process, the backend (in `/app/apps/server`), under bun src/main.ts ``` -It listens on port 80 (`PORT=80`) and answers `/api` and `/ws` itself. Every other path goes to the frontend: Nitro builds the server-side rendering and the static files (`apps/web/.output/`) with its `standard` preset into one fetch handler, which `apps/server/src/frontend.ts` loads from `apps/web/.output/server/index.mjs`. +It listens on port 80 (`PORT=80`) and answers `/api` and `/ws` itself. Every other path goes to the frontend: Nitro builds the server-side rendering and the static files (`apps/web/.output/`) with its `standard` preset into one fetch handler, which `apps/server/src/http/frontend.ts` loads from `apps/web/.output/server/index.mjs`. ```mermaid flowchart LR @@ -63,13 +63,13 @@ The image sets `SNAPRAID_BASE_PATH=/app/snapraid`, `SNAPRAID_BIN=/usr/local/bin/ ## Standalone binary -`scripts/build-binary.sh` compiles `apps/server/src/bin.ts` with `bun build --compile` into one executable per architecture (`snapraid-ui-linux-x86_64`, `snapraid-ui-linux-aarch64`), which the release attaches to the GitHub release. The binary embeds the Bun runtime, the backend bundled with its packages, and with `--asset` the built frontend (`apps/web/.output/`), `apps/web/package.json` and `docs/*.md`. Inside the binary these files lie under `/$bunfs/root/` at their paths in the repository (`apps/web/…`, `docs/…`); `apps/server/src/app-files.ts` finds them there, and at the same paths from the root in the repository and the image: +`scripts/build-binary.sh` compiles `apps/server/src/bin.ts` with `bun build --compile` into one executable per architecture (`snapraid-ui-linux-x86_64`, `snapraid-ui-linux-aarch64`), which the release attaches to the GitHub release. The binary embeds the Bun runtime, the backend bundled with its packages, and with `--asset` the built frontend (`apps/web/.output/`), `apps/web/package.json` and `docs/*.md`. Inside the binary these files lie under `/$bunfs/root/` at their paths in the repository (`apps/web/…`, `docs/…`); `apps/server/src/system/app-files.ts` finds them there, and at the same paths from the root in the repository and the image: | File | Read by | For | |---|---|---| -| `apps/web/.output/server/index.mjs` | `apps/server/src/frontend.ts` | The UI | -| `apps/web/package.json` | `apps/server/src/version.ts` | The version, for `--version`, Home Assistant and the MCP endpoint; `SNAPRAID_UI_VERSION` overrides it | -| `docs/*.md` | `apps/server/src/mcp.ts` | The documentation the MCP endpoint offers | +| `apps/web/.output/server/index.mjs` | `apps/server/src/http/frontend.ts` | The UI | +| `apps/web/package.json` | `apps/server/src/system/version.ts` | The version, for `--version`, Home Assistant and the MCP endpoint; `SNAPRAID_UI_VERSION` overrides it | +| `docs/*.md` | `apps/server/src/integrations/mcp.ts` | The documentation the MCP endpoint offers | `bin.ts` first imports `bin-cli.ts`, which answers `--version` and `--help` and sets the data directory to `./snapraid` when `SNAPRAID_BASE_PATH` is not set, and then `main.ts`. SnapRAID and `smartctl` are not inside: the binary runs the ones installed on the host. @@ -87,34 +87,34 @@ Module map: | Module | Responsibility | |---|---| | `main.ts` | App setup, middleware, route mounting, dependency injection, startup | -| `config.ts` | `SNAPRAID_BASE_PATH`, `SNAPRAID_BIN`, `SNAPRAID_EXTRA_ARGS`; `resolveFromBase()` (relative paths resolve against the data directory, absolute ones stay as they are); `snapraidCommand()` | -| `config-parser.ts` | Parses `snapraid.conf` (parity levels incl. split parity, content, data, exclude, pool, `autosave`, `blocksize`); loads and saves `config.json` | -| `auth.ts` | Login, logout, password change, passkey routes, session cookie, lockout, session secret | -| `credentials.ts` | `auth.json`: the password changed in the UI (PBKDF2) and the passkeys, dropped when the environment credentials change | -| `passkeys.ts` | WebAuthn registration and login (`@simplewebauthn/server`): relying party from the Origin, one-time challenges | -| `websocket.ts` | WebSocket client set, `broadcast()`, replay of the running job on connect | +| `config/config.ts` | `SNAPRAID_BASE_PATH`, `SNAPRAID_BIN`, `SNAPRAID_EXTRA_ARGS`; `resolveFromBase()` (relative paths resolve against the data directory, absolute ones stay as they are); `snapraidCommand()` | +| `config/config-parser.ts` | Parses `snapraid.conf` (parity levels incl. split parity, content, data, exclude, pool, `autosave`, `blocksize`); loads and saves `config.json` | +| `auth/auth.ts` | Login, logout, password change, passkey routes, session cookie, lockout, session secret | +| `auth/credentials.ts` | `auth.json`: the password changed in the UI (PBKDF2) and the passkeys, dropped when the environment credentials change | +| `auth/passkeys.ts` | WebAuthn registration and login (`@simplewebauthn/server`): relying party from the Origin, one-time challenges | +| `http/websocket.ts` | WebSocket client set, `broadcast()`, replay of the running job on connect | | `executors/command-executor.ts` | Spawns long-running SnapRAID jobs, streams output, tracks the current and last job, follows progress, handles abort | | `executors/log-tail.ts` | Reads new lines that a running process appends to its log file | | `engine/engine.ts`, `engine/cli-engine.ts` | The [SnapRAID engine](#snapraid-engine) interface and its CLI implementation: jobs, status, diff, SMART, power state | -| `snapraid-runner.ts` | Reports only the CLI gives (`devices`, `list`, `dup`) | +| `snapraid/snapraid-runner.ts` | Reports only the CLI gives (`devices`, `list`, `dup`) | | `parsers/*` | Parsers for SnapRAID's structured log: `status`, `diff`, `check`, `list`, `dup`, `smart`, `probe`, `devices`, progress (`run:pos`) | -| `run-report.ts` | Summarizes a finished run (result, duration, error counts, sync changes) from its log | -| `log-manager.ts` | Log file naming, listing, outcome detection, last run per config, rotation, deletion | -| `scheduler.ts` | Cron schedules (via [Croner](https://github.com/hexagon/croner)), multi-step sync routine, sync guard, skip logic | -| `notifications.ts` | Notification settings, secret masking, delivery via SMTP ([Nodemailer](https://nodemailer.com/)), ntfy and webhook | -| `notification-events.ts` | Builds notifications for runs, skipped schedules and SMART changes, in the configured language | -| `smart-baseline.ts`, `smart-history.ts` | CRC error baseline per disk; daily SMART history | -| `usage-history.ts`, `parity-usage.ts` | Daily array usage history; size and free space of data and parity disks | -| `disk-replacement.ts`, `disk-removal.ts` | Config rewriting and state for the replace-disk and remove-data-disk wizards | -| `backup.ts` | Export and restore of settings, histories and SnapRAID configs | -| `smart-selftest.ts` | SMART self-tests through `smartctl -j` (ATA and NVMe): start, stop, status and log per disk; keeps the spindown away from disks with a running test | -| `recovery.ts`, `restore-ownership.ts` | Restoring files from parity, one `fix` per disk; after every `fix`, files it recreated (from its log) and the folders made for them take the owner and permissions of their folder | -| `duplicates.ts` | Deleting duplicates safely: a copy goes only while it and the copy that stays are as at the last sync (not in the diff, the size `dup` reported); marks copies no longer on their disk | -| `disk-check.ts` | Disks that are missing, empty or on another filesystem than the content file recorded, from the `status` log | -| `maintenance-settings.ts` | Docker pause and spindown settings | -| `docker.ts`, `container-pause.ts` | Docker Engine API over the unix socket; pausing containers for the duration of jobs, resuming leftovers after a restart | -| `spindown.ts` | Watches `/proc/diskstats` and spins idle disks down with `snapraid down` | -| `demo.ts` | The demo sandbox (`SNAPRAID_DEMO=1`): fake `smart`, `probe` and device output, demo disk sizes in `status` and the disk usage, a Docker API with demo containers | +| `snapraid/run-report.ts` | Summarizes a finished run (result, duration, error counts, sync changes) from its log | +| `snapraid/log-manager.ts` | Log file naming, listing, outcome detection, last run per config, rotation, deletion | +| `schedule/scheduler.ts` | Cron schedules (via [Croner](https://github.com/hexagon/croner)), multi-step sync routine, sync guard, skip logic | +| `notifications/notifications.ts` | Notification settings, secret masking, delivery via SMTP ([Nodemailer](https://nodemailer.com/)), ntfy and webhook | +| `notifications/notification-events.ts` | Builds notifications for runs, skipped schedules and SMART changes, in the configured language | +| `smart/smart-baseline.ts`, `smart/smart-history.ts` | CRC error baseline per disk; daily SMART history | +| `snapraid/usage-history.ts`, `disks/parity-usage.ts` | Daily array usage history; size and free space of data and parity disks | +| `disks/disk-replacement.ts`, `disks/disk-removal.ts` | Config rewriting and state for the replace-disk and remove-data-disk wizards | +| `config/backup.ts` | Export and restore of settings, histories and SnapRAID configs | +| `smart/smart-selftest.ts` | SMART self-tests through `smartctl -j` (ATA and NVMe): start, stop, status and log per disk; keeps the spindown away from disks with a running test | +| `snapraid/recovery.ts`, `snapraid/restore-ownership.ts` | Restoring files from parity, one `fix` per disk; after every `fix`, files it recreated (from its log) and the folders made for them take the owner and permissions of their folder | +| `snapraid/duplicates.ts` | Deleting duplicates safely: a copy goes only while it and the copy that stays are as at the last sync (not in the diff, the size `dup` reported); marks copies no longer on their disk | +| `disks/disk-check.ts` | Disks that are missing, empty or on another filesystem than the content file recorded, from the `status` log | +| `config/maintenance-settings.ts` | Docker pause and spindown settings | +| `system/docker.ts`, `system/container-pause.ts` | Docker Engine API over the unix socket; pausing containers for the duration of jobs, resuming leftovers after a restart | +| `disks/spindown.ts` | Watches `/proc/diskstats` and spins idle disks down with `snapraid down` | +| `system/demo.ts` | The demo sandbox (`SNAPRAID_DEMO=1`): fake `smart`, `probe` and device output, demo disk sizes in `status` and the disk usage, a Docker API with demo containers | | `routes/*` | HTTP routes, see the [API reference](#api-reference) | ## Running SnapRAID @@ -174,7 +174,7 @@ The API was last compared with snapraid-daemon **`v2.1`**; the version is in `ap ### Tests -`apps/server/src/__tests__/engine-contract.ts` holds what every engine has to do: a sync succeeds, the status lists the disks, the diff finds a new file, `afterRun` runs while the job is still current, and so on. `engine-contract.test.ts` runs it against the fake engine (`__tests__/fake-engine.ts`) always, against the CLI engine with a real SnapRAID binary, and against the daemon engine with a running snapraid-daemon (see [Development](development.md#tests)). +`apps/server/src/engine/__tests__/engine-contract.ts` holds what every engine has to do: a sync succeeds, the status lists the disks, the diff finds a new file, `afterRun` runs while the job is still current, and so on. `engine-contract.test.ts` runs it against the fake engine (`__tests__/fake-engine.ts`) always, against the CLI engine with a real SnapRAID binary, and against the daemon engine with a running snapraid-daemon (see [Development](development.md#tests)). Without a daemon, `daemon-engine.test.ts` runs the daemon engine against a stand-in that answers like snapraid-daemon 2.0: queuing and following a task, progress, abort, the fallback, auth and errors, and the mapping of its JSON to the UI's types. `engine-settings.test.ts` covers the settings and which engine runs which config, `scheduler.test.ts` the scheduler against the fake engine. @@ -250,7 +250,7 @@ Everything lives in the data directory, `SNAPRAID_BASE_PATH`. In the image that | `auth.json` | Password changed in the UI (hash) and passkeys (only with login enabled) | | `logs/*.log` | One structured log per job (directory set in `config.json`) | -Writes go through `node:fs`: the JSON state files through `writeTextFileAtomic` (`apps/server/src/atomic-write.ts`, a temporary file renamed over the old one), other writes (the SnapRAID configs among them) are plain `writeFile` calls. Most files are read again whenever they are needed. Two things are read only at startup: the log directory and retention settings from `config.json`, and the cron timers, which are rebuilt from `schedules.json` on start, on changes through the API and after a restore. See [Configuration](configuration.md) for details on each file. +Writes go through `node:fs`: the JSON state files through `writeTextFileAtomic` (`apps/server/src/system/atomic-write.ts`, a temporary file renamed over the old one), other writes (the SnapRAID configs among them) are plain `writeFile` calls. Most files are read again whenever they are needed. Two things are read only at startup: the log directory and retention settings from `config.json`, and the cron timers, which are rebuilt from `schedules.json` on start, on changes through the API and after a restore. See [Configuration](configuration.md) for details on each file. ## Frontend @@ -275,7 +275,7 @@ UI texts live in `apps/web/messages/{en,de,it}.json` and are compiled by [Paragl The backend does not know the viewer's language. For errors a user can run into, it sends a marked key instead of text: `msg("server_error_job_running")` from `packages/shared/src/i18n.ts` produces `\u0002["server_error_job_running",{}]\u0003`. The frontend (`apps/web/src/lib/i18n.ts`, `localizeServer()`) replaces such markers with the translated message; plain text, such as SnapRAID's own output, passes through unchanged. Some validation errors for malformed requests are plain English strings. -Notifications are the exception: they leave the server by e-mail, ntfy or webhook in the language set in the notification settings (`apps/server/src/notification-events.ts`). +Notifications are the exception: they leave the server by e-mail, ntfy or webhook in the language set in the notification settings (`apps/server/src/notifications/notification-events.ts`). See [Development](development.md#translations) for how to add or change texts. diff --git a/docs/development.md b/docs/development.md index 63da7ee..a4f3dcf 100644 --- a/docs/development.md +++ b/docs/development.md @@ -59,7 +59,7 @@ pkill -f "vite dev" - runs sync, scrub, touch and sync again with their logs, then adds, changes, moves and deletes files and runs `diff` and `status`, so that every page has something to show; - gives the sandbox a past with `apps/server/scripts/seed-demo.ts` (run with Bun in `apps/server/`, whose packages it uses): the job logs move back in time (last sync 10 hours ago, scrub yesterday), `usage-history.json` gets two months of disk usage with one disk filling up, `schedules.json` a nightly sync with touch and scrub, a weekly scrub and a SMART check, and `maintenance.json` Docker pause and spindown. -All sandbox disks are directories on one filesystem, which SnapRAID only accepts with `--test-skip-device`. They hold a few KiB and have no SMART data, power state or Docker next to them, so `SNAPRAID_DEMO=1` makes the backend answer like a real home server (`apps/server/src/demo.ts`): +All sandbox disks are directories on one filesystem, which SnapRAID only accepts with `--test-skip-device`. They hold a few KiB and have no SMART data, power state or Docker next to them, so `SNAPRAID_DEMO=1` makes the backend answer like a real home server (`apps/server/src/system/demo.ts`): - `smart` and `probe`: generated results in the real structured log format: a healthy HDD, an SSD, an HDD with growing reallocated sectors, a sleeping 16 TB disk and an 18 TB parity disk; - `status`: the real log of the sandbox, with the sizes, file counts and fragmentation of those disks and a scrub that keeps up (64 % verified, the oldest block 17 days old) instead of the sandbox's figures; @@ -224,7 +224,7 @@ Biome (`apps/web/biome.json`) uses the recommended rules, 2-space indentation, s The UI is available in English, German and Italian. All texts are in `apps/web/messages/{en,de,it}.json` in the inlang message format (`@inlang/plugin-message-format`); `en` is the base locale (`apps/web/project.inlang/settings.json`). Placeholders are written as `{name}`. - **Frontend:** import the messages as `import * as m from '../paraglide/messages'` (relative path) and call `m.key({ … })`. -- **Backend:** for errors a user can see, return `msg("server_error_…", { … })` from `@shared/i18n.ts` (`packages/shared/src/i18n.ts`) instead of text. The frontend renders the key in the UI language with `localizeServer()`. Notification texts are not in the message files; they are in `apps/server/src/notification-events.ts` and `apps/server/src/routes/notifications.ts`, one block per language. +- **Backend:** for errors a user can see, return `msg("server_error_…", { … })` from `@shared/i18n.ts` (`packages/shared/src/i18n.ts`) instead of text. The frontend renders the key in the UI language with `localizeServer()`. Notification texts are not in the message files; they are in `apps/server/src/notifications/notification-events.ts` and `apps/server/src/routes/notifications.ts`, one block per language. To add, change or remove a key in all three files at once, use the helper script from `apps/web/`: diff --git a/packages/shared/src/i18n.ts b/packages/shared/src/i18n.ts index 5133f32..fddae23 100644 --- a/packages/shared/src/i18n.ts +++ b/packages/shared/src/i18n.ts @@ -5,7 +5,7 @@ // SnapRAID's own output, passes through unchanged. // // Notifications are the exception: they leave the server by mail, ntfy or -// webhook in the language set for them (apps/server/src/notification-events.ts). +// webhook in the language set for them (apps/server/src/notifications/notification-events.ts). export type MsgInputs = Record; diff --git a/scripts/build-binary.sh b/scripts/build-binary.sh index a05f62c..50c3b59 100755 --- a/scripts/build-binary.sh +++ b/scripts/build-binary.sh @@ -27,7 +27,7 @@ fi # The files the backend reads at runtime, laid out as in the repository: --asset embeds a # directory under its own name, so the binary has them at /$bunfs/root/apps/web/ and -# /$bunfs/root/docs/ (apps/server/src/app-files.ts). The documentation as the MCP endpoint offers +# /$bunfs/root/docs/ (apps/server/src/system/app-files.ts). The documentation as the MCP endpoint offers # it: the Markdown pages without the pictures. stage="$(mktemp -d)" trap 'rm -rf "$stage"' EXIT From f0ed1f0c421c5f71df67a70235f7a8123cbfa5b0 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 05:17:21 +0000 Subject: [PATCH 3/3] Describe the server's domain folders in the docs The source tree in development.md lists the folders instead of the loose modules, and the module map in architecture.md is ordered by folder. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Y3rdfbWb1xDUdPRng7TX9Z --- docs/architecture.md | 33 +++++++++++++++++---------------- docs/development.md | 24 +++++++++++++++++------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index c264bf9..32ca653 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -82,38 +82,39 @@ Middleware, in order: 1. **CORS**: credentials are only accepted from an origin with the same hostname as the request, e.g. the dev frontend on `localhost:3000` calling `localhost:8080`. 2. **Auth** (only when `SNAPRAID_UI_USERNAME` and `SNAPRAID_UI_PASSWORD` are both set): guards `/api/*` and `/ws`, except `/api/auth/*`, `/api/metrics` and `/api/mcp`, which check their own token. A missing or invalid session returns `401 {"error":"Unauthorized"}`. Sessions are HS256-signed JWTs in an HTTP-only `snapraid_session` cookie, see [Security](security.md). `auth.ts` has the routes, `credentials.ts` the password changed in the UI and the passkeys in `auth.json`, `passkeys.ts` WebAuthn with `@simplewebauthn/server`. -Module map: +Module map. `main.ts`, `bin.ts` and `bin-cli.ts` sit in `src/`, every other module in a folder of its domain (`auth/`, `config/`, `disks/`, `engine/`, `executors/`, `http/`, `integrations/`, `notifications/`, `parsers/`, `routes/`, `schedule/`, `smart/`, `snapraid/`, `system/`), with its unit tests in that folder's `__tests__/`: | Module | Responsibility | |---|---| | `main.ts` | App setup, middleware, route mounting, dependency injection, startup | -| `config/config.ts` | `SNAPRAID_BASE_PATH`, `SNAPRAID_BIN`, `SNAPRAID_EXTRA_ARGS`; `resolveFromBase()` (relative paths resolve against the data directory, absolute ones stay as they are); `snapraidCommand()` | -| `config/config-parser.ts` | Parses `snapraid.conf` (parity levels incl. split parity, content, data, exclude, pool, `autosave`, `blocksize`); loads and saves `config.json` | | `auth/auth.ts` | Login, logout, password change, passkey routes, session cookie, lockout, session secret | | `auth/credentials.ts` | `auth.json`: the password changed in the UI (PBKDF2) and the passkeys, dropped when the environment credentials change | | `auth/passkeys.ts` | WebAuthn registration and login (`@simplewebauthn/server`): relying party from the Origin, one-time challenges | -| `http/websocket.ts` | WebSocket client set, `broadcast()`, replay of the running job on connect | +| `config/config.ts` | `SNAPRAID_BASE_PATH`, `SNAPRAID_BIN`, `SNAPRAID_EXTRA_ARGS`; `resolveFromBase()` (relative paths resolve against the data directory, absolute ones stay as they are); `snapraidCommand()` | +| `config/config-parser.ts` | Parses `snapraid.conf` (parity levels incl. split parity, content, data, exclude, pool, `autosave`, `blocksize`); loads and saves `config.json` | +| `config/backup.ts` | Export and restore of settings, histories and SnapRAID configs | +| `config/maintenance-settings.ts` | Docker pause and spindown settings | +| `disks/parity-usage.ts` | Size and free space of data and parity disks | +| `disks/disk-replacement.ts`, `disks/disk-removal.ts` | Config rewriting and state for the replace-disk and remove-data-disk wizards | +| `disks/disk-check.ts` | Disks that are missing, empty or on another filesystem than the content file recorded, from the `status` log | +| `disks/spindown.ts` | Watches `/proc/diskstats` and spins idle disks down with `snapraid down` | +| `engine/engine.ts`, `engine/cli-engine.ts` | The [SnapRAID engine](#snapraid-engine) interface and its CLI implementation: jobs, status, diff, SMART, power state | | `executors/command-executor.ts` | Spawns long-running SnapRAID jobs, streams output, tracks the current and last job, follows progress, handles abort | | `executors/log-tail.ts` | Reads new lines that a running process appends to its log file | -| `engine/engine.ts`, `engine/cli-engine.ts` | The [SnapRAID engine](#snapraid-engine) interface and its CLI implementation: jobs, status, diff, SMART, power state | -| `snapraid/snapraid-runner.ts` | Reports only the CLI gives (`devices`, `list`, `dup`) | -| `parsers/*` | Parsers for SnapRAID's structured log: `status`, `diff`, `check`, `list`, `dup`, `smart`, `probe`, `devices`, progress (`run:pos`) | -| `snapraid/run-report.ts` | Summarizes a finished run (result, duration, error counts, sync changes) from its log | -| `snapraid/log-manager.ts` | Log file naming, listing, outcome detection, last run per config, rotation, deletion | -| `schedule/scheduler.ts` | Cron schedules (via [Croner](https://github.com/hexagon/croner)), multi-step sync routine, sync guard, skip logic | +| `http/websocket.ts` | WebSocket client set, `broadcast()`, replay of the running job on connect | | `notifications/notifications.ts` | Notification settings, secret masking, delivery via SMTP ([Nodemailer](https://nodemailer.com/)), ntfy and webhook | | `notifications/notification-events.ts` | Builds notifications for runs, skipped schedules and SMART changes, in the configured language | +| `parsers/*` | Parsers for SnapRAID's structured log: `status`, `diff`, `check`, `list`, `dup`, `smart`, `probe`, `devices`, progress (`run:pos`) | +| `schedule/scheduler.ts` | Cron schedules (via [Croner](https://github.com/hexagon/croner)), multi-step sync routine, sync guard, skip logic | | `smart/smart-baseline.ts`, `smart/smart-history.ts` | CRC error baseline per disk; daily SMART history | -| `snapraid/usage-history.ts`, `disks/parity-usage.ts` | Daily array usage history; size and free space of data and parity disks | -| `disks/disk-replacement.ts`, `disks/disk-removal.ts` | Config rewriting and state for the replace-disk and remove-data-disk wizards | -| `config/backup.ts` | Export and restore of settings, histories and SnapRAID configs | | `smart/smart-selftest.ts` | SMART self-tests through `smartctl -j` (ATA and NVMe): start, stop, status and log per disk; keeps the spindown away from disks with a running test | +| `snapraid/snapraid-runner.ts` | Reports only the CLI gives (`devices`, `list`, `dup`) | +| `snapraid/run-report.ts` | Summarizes a finished run (result, duration, error counts, sync changes) from its log | +| `snapraid/log-manager.ts` | Log file naming, listing, outcome detection, last run per config, rotation, deletion | +| `snapraid/usage-history.ts` | Daily array usage history | | `snapraid/recovery.ts`, `snapraid/restore-ownership.ts` | Restoring files from parity, one `fix` per disk; after every `fix`, files it recreated (from its log) and the folders made for them take the owner and permissions of their folder | | `snapraid/duplicates.ts` | Deleting duplicates safely: a copy goes only while it and the copy that stays are as at the last sync (not in the diff, the size `dup` reported); marks copies no longer on their disk | -| `disks/disk-check.ts` | Disks that are missing, empty or on another filesystem than the content file recorded, from the `status` log | -| `config/maintenance-settings.ts` | Docker pause and spindown settings | | `system/docker.ts`, `system/container-pause.ts` | Docker Engine API over the unix socket; pausing containers for the duration of jobs, resuming leftovers after a restart | -| `disks/spindown.ts` | Watches `/proc/diskstats` and spins idle disks down with `snapraid down` | | `system/demo.ts` | The demo sandbox (`SNAPRAID_DEMO=1`): fake `smart`, `probe` and device output, demo disk sizes in `status` and the disk usage, a Docker API with demo containers | | `routes/*` | HTTP routes, see the [API reference](#api-reference) | diff --git a/docs/development.md b/docs/development.md index a4f3dcf..5a463dc 100644 --- a/docs/development.md +++ b/docs/development.md @@ -88,14 +88,24 @@ apps/server/ the backend src/ main.ts app setup, middleware, startup bin.ts entry point of the standalone binary; bin-cli.ts: --version, --help - app-files.ts where the frontend, its package.json and docs/ are (repository, image, binary) - fs.ts, command.ts file and process helpers (node:fs, Bun.spawn) - frontend.ts loads the built frontend, which the backend serves - routes/ HTTP routes per area + auth/ login, passkeys, the credentials in auth.json + config/ data directory and SnapRAID binary, config.json and snapraid.conf, setup + wizard, backup/restore, maintenance settings + disks/ disk check, power, spindown, removal and replacement wizards, disk usage + engine/ the SnapRAID engine: CLI and snapraid-daemon executors/ job runner (spawn, stream, progress, abort) - parsers/ structured log parsers, tests and real SnapRAID fixtures - __tests__/ backend unit tests - *.ts auth, scheduler, notifications, log manager, histories, wizards, backup, demo + http/ the built frontend the backend serves, the WebSocket + integrations/ array snapshot, Prometheus metrics, MCP server, Home Assistant over MQTT + notifications/ channels (SMTP, ntfy, webhook) and the notifications for runs and SMART + parsers/ structured log parsers + routes/ HTTP routes per area + schedule/ cron schedules and the sync routine + smart/ SMART baseline, history, smartignore, self-tests + snapraid/ run reports, logs, status cache, usage history, recovery, duplicates, + reports only the CLI gives + system/ file and process helpers, atomic writes, app files, version, Docker and + the container pause, the demo stand-ins + */__tests__/ unit tests next to the folder they test; __tests__/ holds assert-error.ts apps/web/ the frontend messages/ UI texts: en.json, de.json, it.json project.inlang/ Paraglide/inlang project settings