Commit Graph

73 Commits

Author SHA1 Message Date
520fb98d96 Merge pull request 'feat: redesign history timeline — single-line, timestamp right-aligned' (#26) from feat/timezone-settings into dev
All checks were successful
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 19s
Reviewed-on: #26
2026-03-28 15:07:01 -04:00
800184d2be Merge branch 'dev' into feat/timezone-settings
All checks were successful
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
2026-03-28 15:06:34 -04:00
82c314f85c feat: redesign history timeline — single-line, timestamp right-aligned
All checks were successful
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Each event is now one row: label · old → new on the left, timestamp
right-aligned. Nothing is far from anything else. State changes use the
existing badge component for immediate visual recognition. The created
event reads 'instance created' in accent. Middle-dot separator keeps
field label and change value clearly associated without forced spacing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:06:09 -04:00
2fba532ec7 Merge pull request 'feat: rework history timeline for clarity' (#25) from feat/timezone-settings into dev
All checks were successful
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 20s
Reviewed-on: #25
2026-03-28 15:01:39 -04:00
9177578aaf Merge branch 'dev' into feat/timezone-settings
All checks were successful
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
2026-03-28 15:01:12 -04:00
94c4a0af51 feat: rework history timeline for clarity
All checks were successful
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
Timestamp now sits on its own line above each event so it's visually
separate from the change description. Field names use a friendly label
map (hardware_acceleration → hw acceleration, tailscale_ip → tailscale ip,
etc.). The created event reads "instance created" in accent colour instead
of a raw "created / —". Padding between rows increased.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:00:22 -04:00
ec60d53767 Merge pull request 'feat: timezone setting — display timestamps in selected local timezone' (#24) from feat/timezone-settings into dev
All checks were successful
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 19s
Reviewed-on: #24
2026-03-28 14:56:01 -04:00
ad81d7ace7 Merge branch 'dev' into feat/timezone-settings
All checks were successful
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
2026-03-28 14:55:35 -04:00
badd542bd7 feat: timezone setting — display timestamps in selected local timezone
All checks were successful
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Add a Display section to the settings modal with a timezone dropdown.
Selection is persisted to localStorage and applied to all timestamps via
fmtDate (date-only) and fmtDateFull (date + time + TZ abbreviation, e.g.
"Mar 28, 2026, 2:48 PM EDT"). Changing the timezone live-re-renders the
current page. Defaults to UTC.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:53:20 -04:00
7c31ee3327 Merge pull request 'chore: maintenance — test coverage, route cleanup, README rewrite' (#23) from chore/maintenance into dev
All checks were successful
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 23s
Reviewed-on: #23
2026-03-28 14:47:27 -04:00
0ecfa7dbc9 chore: maintenance — test coverage, route cleanup, README rewrite
All checks were successful
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
- Add fmtHistVal and stateClass helper tests (7 new, 106 total)
- Add import regression test: missing name field returns 400 not 500
- Fix normalise() crash on missing name: body.name.trim() → (body.name ?? '').trim()
- Extract duplicate DB error handler into handleDbError() helper
- Rewrite README from scratch with audit log, export/import, full API docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:46:48 -04:00
f16fb3e088 Merge pull request 'feat: audit log / history timeline on instance detail page' (#22) from feat/history-timeline into dev
All checks were successful
CI / test (push) Successful in 17s
CI / build-dev (push) Successful in 22s
Reviewed-on: #22
2026-03-28 14:36:22 -04:00
cb01573cdf feat: audit log / history timeline on instance detail page
All checks were successful
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Adds an instance_history table that records every field change:
- createInstance logs a 'created' event
- updateInstance diffs old vs new and logs one row per changed field
  (name, state, stack, vmid, tailscale_ip, all service flags)
- History is stored under the new vmid when vmid changes

New endpoint: GET /api/instances/:vmid/history

The 'timestamps' section on the detail page is replaced with a
grid timeline showing timestamp | field | old → new for each event.
State changes are colour-coded (deployed=green, testing=amber,
degraded=red). Boolean service flags display as on/off.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:35:35 -04:00
b48d5fb836 Merge pull request 'fix: remove stacks count from stats bar' (#21) from fix/remove-stacks-stat into dev
All checks were successful
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 20s
Reviewed-on: #21
2026-03-28 14:28:19 -04:00
6e124576cb fix: remove stacks count from stats bar
All checks were successful
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
Stacks are always just production/development — counting them adds
no useful information to the dashboard summary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:27:43 -04:00
1f328e026d Merge pull request 'fix: uniform 16px spacing above all settings sections' (#20) from fix/settings-section-spacing into dev
All checks were successful
CI / test (push) Successful in 14s
CI / build-dev (push) Successful in 22s
Reviewed-on: #20
2026-03-28 14:24:09 -04:00
71c2c68fbc fix: uniform 16px spacing above all settings sections
All checks were successful
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
Removing the :first-child { padding-top: 0 } override lets every
section use the same padding: 16px 0, so the gap above Export matches
the gap above Import (and any future sections).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:23:14 -04:00
8bcf8229db Merge pull request 'fix: remove top padding from settings modal body' (#19) from fix/settings-modal-body-padding into dev
All checks were successful
CI / test (push) Successful in 17s
CI / build-dev (push) Successful in 21s
Reviewed-on: #19
2026-03-28 14:20:20 -04:00
6e1e9f7153 fix: remove top padding from settings modal body
All checks were successful
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
The modal-body's 22px padding-top created a visible gap between the
header divider and the Export section title.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:19:39 -04:00
1fbb74d1ef Merge pull request 'fix: remove top gap above first settings section' (#18) from feat/settings-modal into dev
All checks were successful
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 23s
Reviewed-on: #18
2026-03-28 14:16:22 -04:00
617a5b5800 Merge branch 'dev' into feat/settings-modal
All checks were successful
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
2026-03-28 14:15:57 -04:00
0985d9d481 fix: remove top gap above first settings section
All checks were successful
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
padding-top on the first .settings-section created a visible gap
above the Export title. Fixed with :first-child { padding-top: 0 }.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:15:25 -04:00
2af6c56558 Merge pull request 'feat: settings modal with database export and import' (#17) from feat/settings-modal into dev
All checks were successful
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 21s
Reviewed-on: #17
2026-03-28 14:12:08 -04:00
af207339a4 feat: settings modal with database export and import
All checks were successful
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
Adds a gear button to the nav that opens a settings modal with:
- Export: GET /api/export returns all instances as a JSON backup file
  with a Content-Disposition attachment header
- Import: POST /api/import validates and bulk-replaces all instances;
  client uses FileReader to POST the parsed JSON, with a confirm dialog
  before destructive replace

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:10:59 -04:00
20d8a13375 Merge pull request 'chore: bump version to 1.2.2' (#15) from chore/bump-v1.2.2 into dev
All checks were successful
CI / test (push) Successful in 14s
CI / build-dev (push) Successful in 24s
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
Reviewed-on: #15
2026-03-28 13:59:53 -04:00
f72aaa52f8 chore: bump version to 1.2.2
All checks were successful
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:59:23 -04:00
dd47d5006e Merge pull request 'fix: collapse python3 one-liner to fix YAML indentation error' (#14) from fix/release-yaml-indent into dev
All checks were successful
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 21s
Reviewed-on: #14
2026-03-28 13:58:26 -04:00
10e25e1803 fix: collapse python3 one-liner to fix YAML indentation error
All checks were successful
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Multi-line python3 -c "..." had unindented code outside the run: | block,
causing 'yaml: line 83: could not find expected :'. Collapsed to a single
indented line so the YAML parser sees it correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:57:48 -04:00
1a62e2fdd9 Merge pull request 'chore: bump version to 1.2.1' (#12) from chore/bump-v1.2.1 into dev
All checks were successful
CI / test (push) Successful in 15s
CI / build-dev (push) Successful in 28s
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
Reviewed-on: #12
2026-03-28 13:54:12 -04:00
1271c061fd chore: bump version to 1.2.1
All checks were successful
CI / test (pull_request) Successful in 16s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:52:40 -04:00
7b2a996c21 Merge pull request 'fix: remove npm cache and fix release notes shell injection' (#11) from fix/release-workflow into dev
All checks were successful
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 20s
Reviewed-on: #11
2026-03-28 13:51:49 -04:00
3233d65db0 fix: remove npm cache and fix release notes shell injection
All checks were successful
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
cache: npm caused ~4min ETIMEDOUT on every run (cache service unreachable).

Commit messages containing backticks were shell-expanded inside the
curl -d "..." string, causing 'sha: No such file or directory'. Fixed by
writing release notes to a temp file and using python3 to build the JSON
payload, then passing it to curl with --data @file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:49:38 -04:00
3037381084 Merge pull request 'chore: bump version to 1.2.0' (#9) from chore/bump-v1.2.0 into dev
All checks were successful
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 26s
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Reviewed-on: #9
2026-03-28 13:22:15 -04:00
e54c1d4848 chore: bump version to 1.2.0
All checks were successful
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:21:18 -04:00
3ae3f98df5 Merge pull request 'fix: use git rev-parse for short SHA in build-dev' (#8) from fix/ci-short-sha into dev
All checks were successful
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 20s
Reviewed-on: #8
2026-03-28 13:19:23 -04:00
65d6514603 fix: use git rev-parse for short SHA in build-dev
All checks were successful
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
$GITEA_SHA is unset on Gitea runners — the nav showed "dev-" with an
empty SHA. git rev-parse --short HEAD works regardless of runner env vars.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:18:47 -04:00
bc44bcbde9 Merge pull request 'fix: remove npm cache from setup-node' (#7) from fix/ci-remove-npm-cache into dev
All checks were successful
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 13s
Reviewed-on: #7
2026-03-28 13:16:33 -04:00
cae0f2222a fix: remove npm cache from setup-node
All checks were successful
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
The Gitea runner's cache service is unreachable, causing a ~4 minute
ETIMEDOUT on every run before falling back to a cold install anyway.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:11:05 -04:00
28833a7ec6 Merge pull request 'feat: show dev-<sha> version string in nav for dev builds' (#6) from feat/dev-version-string into dev
All checks were successful
CI / test (push) Successful in 9m33s
CI / build-dev (push) Successful in 22s
Reviewed-on: #6
2026-03-28 13:03:23 -04:00
6ba02bf17d feat: show dev-<sha> version string in nav for dev builds
All checks were successful
CI / test (pull_request) Successful in 9m31s
CI / build-dev (pull_request) Has been skipped
Production images continue to display the semver (v1.x.x). Dev images
built by CI now receive BUILD_VERSION=dev-<7-char-sha> via a Docker ARG,
and app.js skips the v prefix for non-semver strings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 12:52:15 -04:00
bfe71b2511 Merge pull request 'fix: centre badge text on instance cards' (#5) from fix/badge-alignment into dev
All checks were successful
CI / test (push) Successful in 9m32s
CI / build-dev (push) Successful in 20s
Reviewed-on: #5
2026-03-28 12:38:53 -04:00
0f2a37cb39 fix: centre badge text on instance cards
All checks were successful
CI / test (pull_request) Successful in 9m31s
CI / build-dev (pull_request) Has been skipped
.badge lacked text-align: center. Inside the card's flex-end right
column, badge text was left-justified within each pill, making state
labels (deployed / testing / degraded) appear skewed to the left.

TDD: CSS regression test added to tests/helpers.test.js — reads
css/app.css directly and asserts the rule is present, so this
cannot regress silently in future.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 12:28:44 -04:00
73f4eabbc7 Merge pull request 'fix: db volume ownership and explicit error handling for write failures' (#3) from fix/db-permissions-and-error-handling into dev
All checks were successful
CI / test (push) Successful in 9m33s
CI / build-dev (push) Successful in 21s
Reviewed-on: #3
2026-03-28 12:10:32 -04:00
515ff8ddb3 Merge branch 'dev' into fix/db-permissions-and-error-handling
All checks were successful
CI / test (pull_request) Successful in 9m28s
CI / build-dev (pull_request) Has been skipped
2026-03-28 11:48:36 -04:00
08c12c9394 fix: skip db boot init in test env to prevent parallel worker lock
All checks were successful
CI / test (pull_request) Successful in 9m33s
CI / build-dev (pull_request) Has been skipped
Vitest runs test files in parallel workers. Each worker imports server/db.js,
which triggered module-level init(DEFAULT_PATH) unconditionally. Two workers
racing to open the same SQLite file caused "database is locked", followed
by process.exit(1) killing the worker — surfacing as:

  Error: process.exit unexpectedly called with "1"

Fix: guard the boot init block behind NODE_ENV !== 'test'. Vitest sets
NODE_ENV=test automatically. Each worker's beforeEach(() => _resetForTest())
initialises its own :memory: database, so no file coordination is needed.

process.exit(1) is also guarded by the same condition — it must never
fire inside a test runner process.

TDD: two regression tests added to tests/db.test.js documenting the
expected boot behaviour and proving the module loads cleanly in parallel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 11:48:07 -04:00
4ce7df4649 Merge pull request 'fix: skip db boot init in test env to prevent parallel worker lock' (#4) from fix/db-boot-test-isolation into dev
Some checks failed
CI / test (push) Successful in 9m29s
CI / build-dev (push) Has been cancelled
Reviewed-on: #4
2026-03-28 11:41:55 -04:00
6c04a30c3a fix: skip db boot init in test env to prevent parallel worker lock
All checks were successful
CI / test (pull_request) Successful in 9m29s
CI / build-dev (pull_request) Has been skipped
Vitest runs test files in parallel workers. Each worker imports server/db.js,
which triggered module-level init(DEFAULT_PATH) unconditionally. Two workers
racing to open the same SQLite file caused "database is locked", followed
by process.exit(1) killing the worker — surfacing as:

  Error: process.exit unexpectedly called with "1"

Fix: guard the boot init block behind NODE_ENV !== 'test'. Vitest sets
NODE_ENV=test automatically. Each worker's beforeEach(() => _resetForTest())
initialises its own :memory: database, so no file coordination is needed.

process.exit(1) is also guarded by the same condition — it must never
fire inside a test runner process.

TDD: two regression tests added to tests/db.test.js documenting the
expected boot behaviour and proving the module loads cleanly in parallel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 11:31:55 -04:00
c6cd8098fd Merge branch 'dev' into fix/db-permissions-and-error-handling
Some checks failed
CI / test (pull_request) Failing after 4m52s
CI / build-dev (pull_request) Has been skipped
2026-03-28 11:16:31 -04:00
15ed329743 fix: db volume ownership and explicit error handling for write failures
All checks were successful
CI / test (pull_request) Successful in 9m32s
Root cause of the 500 on create/update/delete: the non-root app user in
the Docker container lacked write permission to the volume mount point.
Docker volume mounts are owned by root by default; the app user (added
in a previous commit) could read the database but not write to it.

Fixes:

1. Dockerfile — RUN mkdir -p /app/data before chown so the directory
   exists in the image with correct ownership. Docker uses this as a
   seed when initialising a new named volume, ensuring the app user
   owns the mount point from the start.

   NOTE: existing volumes from before the non-root user was introduced
   will still be root-owned. Fix with:
     docker run --rm -v catalyst-data:/data alpine chown -R 1000:1000 /data

2. server/routes.js — replace bare `throw e` in POST/PUT catch blocks
   with console.error (route context + error) + explicit 500 response.
   Add try-catch to DELETE handler which previously had none. Unexpected
   DB errors now log the route they came from and return a clean JSON
   body instead of relying on the generic Express error handler.

3. server/db.js — wrap the boot init() call in try-catch. Fatal startup
   errors (e.g. data directory not writable) now print a clear message
   pointing to the cause before exiting, instead of a raw stack trace.

TDD: tests written first (RED), then fixed (GREEN). Six new tests in
tests/api.test.js verify that unexpected DB errors on POST, PUT, and
DELETE return 500 with { error: 'internal server error' } and call
console.error with the route context string.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 11:11:00 -04:00
1412b2e0b7 Merge pull request 'feat: build :dev Docker image on push to dev' (#1) from chore/dev-staging-build into dev
All checks were successful
CI / test (push) Successful in 9m29s
CI / build-dev (push) Successful in 13s
Reviewed-on: #1
2026-03-28 10:38:48 -04:00