Commit Graph
342 Commits
Author SHA1 Message Date
Coderbot fe0f52ce17 fix: services/status probe fails with self-signed certs when CA is missing in container
The /api/v1/services/status endpoint (dashboard card ON/OFF) uses an
HTTPS agent to probe each service. When /app/pki/root.crt is missing
inside the container, it fell back to new https.Agent() which rejects
self-signed certificates. This caused all .sami domain probes to fail
with UNABLE_TO_GET_ISSUER_CERT_LOCALLY, making dashboard cards randomly
flip between ON and OFF depending on whether the Pylon relay responded
before the 10s deadline.

Fix: use rejectUnauthorized: false as fallback when CA cert is absent.
2026-05-23 14:35:39 -07:00
Coderbot 8df5214a45 fix: fetchT() now handles self-signed HTTPS certs
- Node.js native fetch() (undici) cannot use rejectUnauthorized:false
- Added _httpsFetch() using raw https module for internal .sami endpoints
- Fixes quality profile fetch for Sonarr/Radarr (was returning "fetch failed")
- Also fixed corrupted proces..._KEY -> process.env.PYLON_KEY in pylon

Coderbot fix #1
2026-05-23 14:16:18 -07:00
SamiandClaude Opus 4.7 2457d30ed3 fix(installer): point dashcaddy_auth snippet at /api/v1/auth/gate
The 1.5.0 API cutover removed the legacy `/api` mount, but the
`dashcaddy_auth` Caddy snippet generated by the installer still called
`uri /api/auth/gate/{args[0]}`. forward_auth then hit the notFoundHandler
and propagated a DC-404 to the browser instead of a 401-redirect to the
login flow (seen on requests.sami).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 01:33:50 -07:00
Sami a3ec1ffbb3 chore(release): bump to 1.5.0 2026-05-17 11:39:13 -07:00
SamiandClaude Opus 4.7 d36705bd90 feat: 1.5.0 prep — API v1 cutover, LICENSE, CHANGELOG, CI
- Remove legacy /api/ mount; all routes now under /api/v1/ only
- Update path matchers (CSRF excludes, public routes, audit log, rate limits)
- Move standalone routes (/api/network/ips, /api/docs, /api/docs/spec) to v1
- Update openapi.yaml (110 paths), CA pages, and 4 lingering frontend files
- Add LICENSE (proprietary EULA), CHANGELOG.md (Keep a Changelog format)
- Add .gitea/workflows/ci.yml (test+lint and security audit jobs)
- Fix 9 pre-existing no-empty lint errors so CI starts green
- Drop ad-hoc scratch reports and *.bak files from repo root

All 739 jest tests pass. Lint is clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 11:38:45 -07:00
Sami cd8ccaba2b chore(release): bump to 1.4.10 2026-05-17 02:44:40 -07:00
Sami fd2e906963 fix(release): stage build-rewritten files (sw.js, index.html) in release.sh
build.js rewrites three things during `node build.js`:
  - status/dist/*.js (bundle output)
  - status/index.html (CSP hash for inline bootstrap)
  - status/sw.js     (cache name derived from bundle content)

release.sh was only staging status/dist/. Result: when a release didn't
touch index.html or sw.js source, the post-build modifications to those
two files were left unstaged, the commit included only dist/, and the
tarball shipped the stale sw.js. Clients then kept the previous SW
cache name -> activate handler never wiped the cache -> precached old
bundles served forever even after they were "updated" on disk.

Now stage index.html and sw.js too. They're tracked (not gitignored)
so a plain `git add` is enough; the commit is a no-op when nothing
actually changed.
2026-05-17 02:44:35 -07:00
Sami 68bcc0cb5f chore(release): bump to 1.4.9 2026-05-17 02:34:56 -07:00
Sami 7e4c3a0963 fix(frontend): container-logs misuse of wireModal blocked rest of features.js
container-logs.js called `wireModal(modal, null, closeModal)` — passing
the local `closeModal()` function as a third arg where wireModal expects
button elements. wireModal then did `closeModal.addEventListener('click',...)`,
threw TypeError, and because each module's IIFE is a top-level statement
in the concatenated features.js bundle, every IIFE *after* container-logs
silently skipped: snapshot, smart-arr-connect, notification-settings,
panel-tabs, backup-restore, resource-monitor, health-check, update-
management, docker-resources, compose-import, container-exec, audit-log,
weather, clock, card-badges, theme-builder, and license. Symptoms:
"Customize Theme" did nothing on click, license badge stuck at "FREE TIER"
(because license.js never ran), no weather, etc.

- container-logs.js: drop the wireModal call, wire backdrop click directly
  to the local closeModal so the SSE log stream actually stops on close.
- globals.js: harden wireModal — skip any closeBtn that isn't a real
  EventTarget. One typo upstream shouldn't take down the rest of features.js
  init silently.
2026-05-17 02:34:51 -07:00
Sami ac723630f2 chore(release): bump to 1.4.8 2026-05-17 02:20:41 -07:00
Sami 49c8ecfb73 fix(build): compute CSP hash on LF-normalized index.html
When the build runs on a Windows checkout, fs.readFileSync returns the
file with CRLF intact, and the hash of the inline bootstrap script's
body reflects those CRLFs. The release tarball / git transport / Linux
file system strip CRLF on the publishing host, so the browser sees the
LF-only version and computes a different sha256. CSP then blocks the
script — disabling the version widget, theme switcher, and any other
DOM bindings set up in that inline block.

Normalize CRLF -> LF before computing the hash (the on-disk file keeps
its native line endings; only the hash input is normalized). The CSP
allowlist now matches whatever Caddy actually serves.
2026-05-17 02:20:36 -07:00
Sami 939f95d147 chore(release): bump to 1.4.7 2026-05-17 02:03:36 -07:00
Sami bab5105e48 fix(frontend): unbreak dashboard — bundle order, IIFE close, dup const
Three merge-fallout bugs that combined to leave the services grid empty
and most UI inert:

1. error-handler.js was bundled into onboarding.js (loaded 3rd), but
   globals.js in core.js (loaded 1st) does `const errorHandler = new
   ErrorHandler()` at top level. ErrorHandler was undefined when core.js
   ran -> ReferenceError -> globals.js stopped, so window.APPS,
   _showTotpOverlay, loadServices, etc. were never set, and init.js
   blew up on every call into core's exports.

   Moved error-handler.js to the start of the core.js bundle so the
   class is on window before any other script touches it.

2. setup-wizard.js also declared `const errorHandler = new ErrorHandler()`
   at top level. Classic scripts share the document's top-level lexical
   environment, so this collided with globals.js's declaration ->
   redeclaration SyntaxError in features.js. Removed setup-wizard.js's
   copy; it picks up the global one.

3. tooltip-definitions.js closed its `(function(window){...})(window);`
   IIFE at line ~171 ("Validation module loaded"), then the TOOLTIP_
   DEFINITIONS array, getter helpers, window.TooltipDefinitions export,
   and final `debug(...)` log all sat at top level — outside the IIFE,
   where `debug` was no longer in scope. Removed the early close and
   added one at EOF so the whole file is in one IIFE.
2026-05-17 02:03:31 -07:00
Sami f9dad0abb4 chore(release): bump to 1.4.6 2026-05-17 01:41:52 -07:00
Sami ebfc3be9d1 fix(build): derive sw.js cache tag from bundle content hash
The service worker uses staleWhileRevalidate on /dist/*, so after a
release it would serve old bundles from cache indefinitely (cache is
only wiped when the cache *name* changes, which was hardcoded to
'dashcaddy-shell-v10'). Result: dashboard appears unchanged after a
self-update until the user manually unregisters the SW.

build.js now hashes the concatenated dist bundles and writes
`dashcaddy-shell-<10-hex-chars>` into sw.js. Any change in dist/
produces a fresh cache name; on the next page load the SW's activate
handler deletes all older caches and the new bundles are fetched.
2026-05-17 01:41:47 -07:00
Sami 3878509fc5 chore(release): bump to 1.4.5 2026-05-17 01:29:19 -07:00
Sami d7804d6b68 fix(updates): route frontend deploy through host-side updater
When DashCaddy is installed without `${DASHBOARD_DIR}:/app/dashboard` bind
mounted into the container (e.g. legacy DNS2 setup where Caddy serves from
/var/www/dashcaddy-status/), the self-updater's in-container copy to
/app/dashboard was a silent no-op — leaving the dashboard stale across
self-updates, which led to CSP-hash mismatches and a broken UI.

- self-updater: new hostFrontendDir option (default `/var/www/dashcaddy-status`
  on Linux, overridable via DASHCADDY_HOST_FRONTEND_DIR). When set, defer the
  frontend copy to the host-side updater by passing frontendStagingDir +
  frontendTargetDir in trigger.json. Now also includes `js/` in the copy list.
- dashcaddy-update.sh: read those new trigger fields and sync the dashboard
  files on the host. Auto-detect fallback for older self-updaters (no fields
  in trigger.json) so a single release upgrade self-heals.
2026-05-17 01:29:07 -07:00
Sami 5b12c5c9c0 chore(release): bump to 1.4.4 2026-05-16 23:55:17 -07:00
Sami 7e23cb5b06 fix(updates): allow notify endpoint past CSRF + portable JSON parse in release.sh
- csrf-protection: skip CSRF validation on /api/system/update-notify. The
  endpoint has its own X-DashCaddy-Notify-Secret auth and is only ever called
  machine-to-machine; browsers never reach it. Without this, the CSRF cookie
  check rejects the notify POST before the secret comparison runs.
- release.sh: the verify step piped curl into `node -p ".../dev/stdin"` which
  works on Linux but blows up on Windows/git-bash. Replaced with portable
  grep+sed extraction so the same script works on both publisher OSes.
2026-05-16 23:55:08 -07:00
Sami 850db40479 chore(release): bump to 1.4.3 2026-05-16 23:47:34 -07:00
Sami c66fe498b6 feat(updates): seamless release flow — push-notify, VERSION copy, robust mirror
- self-updater: per-instance notify secret (auto-generated), notifyAndApply()
  triggers an immediate check+apply for the publishing host
- routes: POST /api/system/update-notify (X-DashCaddy-Notify-Secret gated,
  added to public-routes allowlist so TOTP doesn't block machine-to-machine)
- dashcaddy-update.sh: include VERSION in backup/deploy/rollback copy lists;
  belt-and-suspenders write trigger.json commit to VERSION post-deploy.
  Fixes drift where /app/VERSION stayed at the old commit after self-update.
- release.sh: mirror failures are non-fatal+loud; HTTP-verify get2 after
  rsync; auto-notify co-located instance via /opt/dashcaddy/updates/notify-secret
  (or honour DASHCADDY_NOTIFY_TARGETS for multi-instance setups).
2026-05-16 23:46:53 -07:00
Sami edac587c5c Merge branch 'main' of http://100.98.123.59:3000/sami7777/dashcaddy
# Conflicts:
#	status/dist/features.js
2026-05-16 22:52:07 -07:00
Sami 824313c411 chore(release): bump to 1.4.2 2026-05-16 22:36:30 -07:00
Sami d3cef9ed86 chore(release): bump to 1.4.1 2026-05-16 00:34:17 -07:00
Krystie 77688daec7 Add service filter, batch operations, and snapshot features
- Service Filter Bar: search by name, filter by status (online/offline)
- Batch Operations: multi-select containers for start/stop/restart
- Container Snapshots: create and manage Docker checkpoints
- Added filter bar and batch action bar to index.html
- Added snapshot button to Admin tools section
- New JS modules: service-filter.js, batch-operations.js, snapshot.js
- Updated build.js to include new modules in bundle
2026-05-15 02:17:24 -07:00
Krystie 0cec447bf1 Expose openContainerLogsModal function for service card buttons
- Added window.openContainerLogsModal(containerId, containerName) function
- Service cards (grid.js) already call this when clicking the 📋 logs button
- Modal now pre-selects the correct container when called from a card
- Rebuilt dist files
2026-05-15 01:51:51 -07:00
Krystie c50b106faf Add Container Log Viewer UI with streaming, search, and download
- New container-logs.js module for viewing Docker container logs
- Integrated with existing API endpoints (/logs/containers, /logs/container/:id, /logs/stream/:id)
- Features:
  - Select container from dropdown
  - View logs with stdout/stderr color coding
  - Real-time log streaming via SSE
  - Search/filter within logs
  - Download logs as text file
  - Line count and filter indicators
- Added '📜 Container Logs' button to Tools section in index.html
- Added to features.js bundle via build.js
- Rebuilt dist files
2026-05-15 01:27:56 -07:00
Krystie 22c48076c0 Merge remote changes - resolve conflicts: keep remote package.json v1.4.0, keep local self-updater.js logic, features.js regenerated by build 2026-05-14 21:08:20 -07:00
Krystie 947007438f Update: version 1.2.0, new version UI features, CSP hash auto-update, bug fixes 2026-05-14 21:04:35 -07:00
Krystie f60a3370db fix: route all console.error through ErrorHandler for consistent tracking
Converted 35+ raw console.error calls to use ErrorHandler.logError() across
12 files. ErrorHandler provides structured logging, local error storage,
and error tracking integration.

Files:
- app-selector.js: 4 errors (fetch templates, port check, suggested port, deploy)
- globals.js: 2 errors (CSRF token get/add)
- service-credentials.js: 2 errors (save/clear credentials)
- totp-settings.js: 4 errors (TOTP setup, session duration, disable, AuthCard)
- notification-settings.js: 2 errors (load config, load history)
- setup-wizard.js: 2 errors (save config to server)
- progress-tracker.js: 4 errors (storage read/write/session/fallback)
- tooltip-definitions.js: 2 errors (validation, condition eval)
- tour-manager.js: 2 errors (Driver.js not loaded, tooltip not found)
- theme-adapter.js: 1 error (theme change callback)
- weather.js: 1 error (weather update)
- onboarding.js: removed duplicate console.error, fixed 2 remaining calls

All console.error calls in the DashCaddy frontend now go through ErrorHandler.
2026-05-14 01:31:55 -07:00
Krystie 5d404f5733 cleanup: wrap console.log calls behind window.DASHCADDY_DEBUG flag
Wrapped 22 console.log calls across 6 files with a debug() helper
that only logs when window.DASHCADDY_DEBUG is true in the browser console.

Files:
- tour-manager.js: 10 calls
- theme-adapter.js: 4 calls
- keyboard-shortcuts.js: 4 calls
- tooltip-definitions.js: 2 calls
- progress-tracker.js: 1 call
- live-events.js: 1 call

console.error and console.warn calls preserved — those indicate
real issues worth seeing in production.
2026-05-14 00:48:51 -07:00
Krystie 8ecae81703 frontend: gate routine console.log behind window.DASHCADDY_DEBUG
Suppress noisy module-load and initialization logs in production.
Logs still appear when window.DASHCADDY_DEBUG is set (e.g. during dev).
2026-05-14 00:13:50 -07:00
SamiandClaude Opus 4.7 f65af5d7fd fix(updater): stop false-positive "update available" loop when commit is unknown
Dockerfile never received DASHCADDY_COMMIT at build, so /app/VERSION held
'unknown'. _isNewer then treated same-version-different-commit as newer,
making the auto-updater rebuild the container indefinitely (each rebuild
still produced commit='unknown').

- self-updater._isNewer: normalize commits; treat unknown/null/empty as no
  commit info and fall back to pure version comparison
- self-updater._autoCheckAndApply + routes/updates: refuse to apply when
  local version >= remote version (belt-and-suspenders)
- update-management.js: hide '(unknown)' from version label
- Dockerfile: COPY VERSION instead of writing from build arg
- VERSION: committed placeholder ('dev'); scripts/release.sh now writes
  the real short SHA into the tarball's VERSION before tar-ing, so every
  published release ships with an accurate commit

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:20:59 -07:00
Sami 88206ff215 chore(release): bump to 1.4.0 2026-05-06 20:29:04 -07:00
SamiandClaude Opus 4.7 54196a2d4f chore: add scripts/release.sh for cutting + publishing releases
Automates what was previously a six-step manual process that, twice
in this codebase's history, has produced version skew between git and
the released tarball (v1.2.0 was published with package.json 1.2.0 in
the tarball but the bump was never committed back to gitea — making
"what code is in v1.2.0?" answerable only by extracting the tarball).

The script:
- Refuses to run with a dirty tree, off main, or already at the
  target version.
- Bumps dashcaddy-api/package.json, rebuilds status/dist/, commits
  + pushes to gitea — so the released artifact and gitea HEAD are
  always in lockstep.
- Clones gitea HEAD on the release host, verifies the cloned commit
  matches what we just pushed (catches a stale clone or a missed
  push), tars it, computes sha256, writes version.json.
- Refreshes install.sh on the release host alongside the tarball
  (fresh installs use the install.sh from the latest release).
- Mirrors the release dir to the get2 backup via rsync.
- Verifies live by curling version.json and re-hashing the served
  tarball.

Hosts overridable via DASHCADDY_RELEASE_HOST / DASHCADDY_MIRROR_HOST
/ DASHCADDY_GITEA_URL env vars.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 20:28:45 -07:00
Sami d81d1183db Merge feature/cloud-backups-and-history: cloud backups + resource history 2026-05-06 19:15:01 -07:00
SamiandClaude Opus 4.7 f537a0dd25 feat: cloud backup destinations + long-term resource history
Cloud backups (Dropbox / WebDAV / SFTP):
- backup-manager.js: save + load handlers per provider, credential
  resolution via credentialManager, destination probe.
- routes/backups.js: /credentials/{provider} (masked GET, POST, DELETE),
  /test-destination, scheduling endpoints.
- status/js/backup-restore.js: destination picker, provider-specific
  credential forms, test button wired to backend probe.
- npm deps already present (dropbox 10.34.0, webdav 5.7.1,
  ssh2-sftp-client 11.0.0).

Resource history:
- resource-monitor.js: three-tier rollup storage — raw 10s samples
  (7-day retention), hourly rollups (30-day), daily rollups
  (365-day). getHistoryByRange() auto-selects the appropriate tier.
- routes/monitoring.js: /monitoring/history/:containerId now supports
  startTime/endTime range mode (legacy ?hours=N still works).
- status/js/resource-monitor.js + dashboard.css: "History" tab with
  range buttons (1h/24h/7d/30d/1y), SVG sparklines for
  CPU / memory / network. Renderer handles raw and rolled-up shapes.

status/dist/features.js rebuilt from source via build.js.

Lifted out of wip/cloud-backups-and-history; the half-finished
app-deps feature from that branch (frontend calls /api/v1/apps/
check-dependencies but the endpoint doesn't exist) is preserved
separately on wip/app-deps for later.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 19:14:38 -07:00
SamiandClaude Opus 4.7 6e47df0d3c fix(installer): add src/ deploy + drop MakeDirectory=yes; bump 1.3.1
- install.sh now deploys the src/ directory alongside routes/.
  Without this, fresh installs of v1.3.0+ produce containers whose
  Dockerfile references src/ but the directory is missing on the host
  filesystem, so docker build fails with "/src: not found".

- The fallback heredoc that writes /etc/systemd/system/dashcaddy-
  updater.path drops MakeDirectory=yes for the same reason it was
  removed from the on-disk unit (e994ad1): systemd creates the watched
  trigger.json path as an empty directory on unit start, blocking
  every subsequent update with EISDIR.

Bumped to 1.3.1 so the existing v1.3.0 instance auto-updates and
picks up these and the host-script fix from 0cf6323.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 18:42:23 -07:00
SamiandClaude Opus 4.7 0cf63231d3 fix(updater): copy src/, replace routes/ in place instead of nesting
Two bugs in the host-side updater script:

1. The Dockerfile (since f5fe32b) does \`COPY src/ ./src/\`, but the
   host script never copies src/ from staging into the api source
   directory. Result: every update fails with
   "failed to compute cache key: ... '/src': not found".

2. \`cp -rf staging/routes api_source/routes/\` does NOT replace the
   destination directory — it copies the source dir INTO the
   destination, producing api_source/routes/routes/. Means new route
   files end up nested one level deep and never get loaded by
   server.js, so updates silently regress route handlers even when
   the build succeeds.

Switch to "rm -rf dest && cp -rf src dest" semantics for both routes
and src, in all four touch points (deploy + 3 rollback paths).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 18:31:28 -07:00
SamiandClaude Opus 4.7 e994ad157e fix(systemd): drop MakeDirectory=yes from updater path unit
`MakeDirectory=yes` on a `PathChanged=` directive whose target is a
file (not a directory) causes systemd to create the watched path as
an empty directory on unit start. The container's self-updater then
crashes with EISDIR every time it tries to writeFile() the trigger,
and the host script never runs.

The parent `/opt/dashcaddy/updates/` is already created by the
installer/Docker volume, so the flag is redundant and only here as
a footgun. Drop it.

Reproducer: enable the unit on a fresh system, watch
`/opt/dashcaddy/updates/trigger.json` get materialized as a directory
within milliseconds of `systemctl start dashcaddy-updater.path`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 17:50:10 -07:00
SamiandClaude Opus 4.7 fa40dcff7a chore(release): bump to 1.3.0
v1.2.0 was published as a tarball but its package.json bump was never
committed back to git. This release picks up where that gap left off
and includes two fixes that v1.2.0 (commit a216dd8) was missing:

  - 6abba43: clear ALL pending self-update history entries (not just
    the first), so stuck installs unwind cleanly.
  - 0460129: allow apiSourceDir to be overridden via the
    DASHCADDY_API_SOURCE_DIR env var, so installs that don't follow
    the default /etc/dashcaddy/sites/dashcaddy-api/ layout (e.g. older
    deployments under /opt/dashcaddy/) can point the auto-updater at
    the right path without patching the constructor.

Without these, instances on the older /opt/dashcaddy/ layout get
stuck in a 30-min retry loop where every update attempt fails with
'cp: cannot create directory /etc/dashcaddy/sites/dashcaddy-api/'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 17:43:29 -07:00
SamiandClaude Opus 4.7 0460129b32 feat(self-updater): allow apiSourceDir override via DASHCADDY_API_SOURCE_DIR env
Mirrors the env-var pattern f5fe32b introduced for channel and
instanceIdFile. Lets installs that don't follow the default
/etc/dashcaddy/sites/dashcaddy-api/ layout (e.g. older deployments
under /opt/dashcaddy/) point the auto-updater at the right path
without having to patch the constructor call site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 16:27:32 -07:00
SamiandClaude Opus 4.7 6abba43a80 fix(self-updater): clear all pending history entries, not just one
checkPostUpdateResult() used history.find() which only ever updated a
single pending entry. When multiple update attempts stacked up, the
extra pending entries stayed stuck in 'pending' forever even though
the actual update completed. Switch to filter() + loop to clear all
matching entries.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 02:04:23 -07:00
Krystie a216dd882d Add dashboard version button and self-update UI wiring 2026-05-05 17:52:05 -07:00
Krystie 95b137bf17 Fix DNS2 self-updater path and sync live dashboard version UI 2026-05-05 17:26:42 -07:00
Krystie f5fe32b999 feat(update): add release policy checks and dashboard version verification 2026-05-04 18:05:00 -07:00
Krystie 0c658a26a8 fix(routes): complete post-refactor dependency wiring cleanup 2026-05-04 16:44:18 -07:00
Krystie 4eebb3ce7a fix(test): remove stale jest setupFilesAfterEnv entry 2026-05-04 16:36:09 -07:00
Krystie 55c405082a fix: use TIMEOUTS constants instead of magic numbers in health and services routes
- health.js: replace magic number 5000 with TIMEOUTS.HTTP_DEFAULT (twice)
- services.js: replace magic number 5000 with TIMEOUTS.HTTP_DEFAULT

Both files already import TIMEOUTS from constants but weren't using it.
2026-05-01 02:36:31 -07:00
Krystie 2f1e2107bc fix: replace console.log/console.error with proper logging in monitoring and themes routes
- monitoring.js: Added log dependency, replaced console.log with log.warn
- themes.js: Added log dependency, replaced console.error with log.error
- src/app.js: Pass log to monitoringRoutes and themesRoutes

This fixes error messages being lost to stdout instead of proper log files.
2026-05-01 02:24:59 -07:00