65 Commits
Author SHA1 Message Date
aneurinandClaude Sonnet 5 1964fda05e Add configurable Cache-Control headers for statics, files, and API
PR Checks / checks (pull_request) Successful in 1m40s
Build / build-and-push (push) Successful in 1m16s
Send our own Cache-Control headers instead of relying entirely on
front-proxy configuration, so browsers cache appropriately even when
Zampler is reached directly. --static-max-age, --file-max-age, and
--api-max-age (all cobra Duration flags, 0 disables caching) cover the
three response categories independently; --no-immutable turns off the
immutable directive on served files, which is on by default since a
file's content never changes for a given ID. The HTML shell always
gets no-cache regardless of --static-max-age, since it isn't
content-hashed like the rest of the Vite build and must keep
revalidating so a new deploy's asset references are picked up.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 11:48:53 +01:00
aneurinandClaude Sonnet 5 60ecec609d Batch scan writes into transactions instead of one per file
Build / build-and-push (push) Successful in 6m18s
Previously each scanned file was its own autocommit transaction, so a
large library meant tens of thousands of individually journaled (and,
outside WAL mode, fsynced) commits back to back. Grouping them into
batches cuts that by ~2-3 orders of magnitude and shrinks the window
during which the scanner holds the write lock, part of the fix for #17.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 09:42:13 +00:00
aneurinandClaude Sonnet 5 e9542dfda5 Harden DSN construction against an already-parameterized --sqlite path
PR Checks / checks (pull_request) Successful in 1m55s
Build / build-and-push (push) Successful in 6m52s
Address review feedback on !18: appending "?..." unconditionally broke
if the --sqlite value already carried its own query string, since the
driver treats the first "?" as the path/query boundary. Join with "&"
when one is already present.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 10:33:19 +01:00
aneurinandClaude Sonnet 5 520bf1c186 Tune SQLite connection: WAL mode, busy_timeout, single writer
Build / build-and-push (push) Skipped
PR Checks / checks (pull_request) Successful in 9m14s
Fixes SQLITE_BUSY errors seen with a large library: the connection
previously used SQLite's default journal mode (which blocks readers
during a write) and a zero busy_timeout (any lock contention failed
immediately instead of retrying), while an uncapped connection pool
let multiple connections contend for the single writer lock.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 10:27:43 +01:00
aneurin a02e96ceec QOL patch: only show filenames
PR Checks / checks (pull_request) Successful in 1m15s
Build / build-and-push (push) Successful in 1m43s
Full directories in the real world are a lot longer than I realised. I will come up with a better way to show them. For now, filenames only, just so the page doesn't break
2026-09-08 12:08:10 +01:00
aneurinandClaude Sonnet 5 7a6e1aa6a8 fix middleware races, ReadFile error paths, and lib.Coalesce
PR Checks / checks (pull_request) Successful in 1m36s
Build / build-and-push (push) Successful in 1m13s
Closes #8, #9, #10; partially addresses #11.

middlewares (#8):
- Log: log once from the Middleware closure after the handler returns
  instead of spawning a goroutine per Write (data race on the shared
  LogWriter, multiple log lines per request). Bodiless responses
  (204/304/redirects) are now logged too, and the status-class switch has
  a default branch so a stray sub-100 status can't nil-deref the event.
- logWriter forwards http.Flusher / http.Hijacker / io.ReaderFrom so
  streaming handlers and the file server keep their fast paths.
- Both middlewares use the closure idiom rather than storing next on a
  shared instance, so they're safe to register on more than one chain.
- CorsOrigin now honours its argument: a comma-separated allow-list,
  with "*" meaning allow-any. Wired to a new --allow-origin flag that
  defaults to "*" (unchanged behaviour out of the box).

ReadFile (#9):
- Add the missing returns after GetFile / open failures (the old code
  fell through to os.ReadFile(nil path) and wrote a second body).
- Hand off to http.ServeContent for Content-Type, Range and
  If-Modified-Since, and set Content-Disposition: inline so the embedded
  audio player streams instead of downloading (the download button
  already uses the HTML download attribute).

lib.Coalesce (#10):
- Constrain to comparable and compare against a fresh zero value. The
  old IsZero type switch compared an any against untyped constants, so
  every narrow numeric type (int8..int64, uint*, float32) reported
  non-zero and never fell back. IsZero is removed (was unused elsewhere).

tests (#11, high-value/easy set):
- lib.Coalesce table across every numeric width, string, bool, pointer.
- database.Migrate against a temp sqlite: schema created, recorded once,
  second call is a no-op.
- types.NewFile: id = sha256(path), hash = sha256(contents), scan fields
  copied, and the missing-file path still yields an id.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 17:22:00 +01:00
aneurin 7f57e25dcf Build a multi-arch (amd64 + arm64) image (!4)
Build / build-and-push (push) Successful in 6m7s
Co-authored-by: Aneurin Barker Snook <aneurin@aneur.in>
2026-09-07 00:42:58 +00:00
aneurinandClaude Sonnet 5 2c288ca1e2 add PR checks workflow
PR Checks / checks (pull_request) Successful in 2m14s
Build / build-and-push (push) Successful in 1m26s
Runs on pull requests to main: frontend type-check and build, then
gofmt, go mod tidy verification, go vet, go build, and go test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 20:27:22 +01:00
aneurinandClaude Sonnet 5 d7a8a7a21e revert to single-job workflow
Build / build-and-push (push) Successful in 46s
This Gitea instance reports as GHES and rejects the @actions/artifact v2
protocol, so upload-artifact@v4 / download-artifact@v4 fail. Roll the
workflow, Dockerfile and .dockerignore back to the pre-split state
(dd6bb1a): one build-and-push job, no artifact handoff.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 17:10:20 +01:00
aneurinandClaude Sonnet 5 ad4fc7dd7d name the build artifact with its architecture
Build / build-binary (push) Failing after 59s
Build / build-image (push) Skipped
Rename the CI binary and its artifact to zampler-amd64, and make the
Dockerfile arch-agnostic via ARG TARGETARCH / COPY zampler-$TARGETARCH.
Keeps the pipeline single-arch for now but makes adding an arm64 leg a
smaller change later.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 17:07:20 +01:00
aneurinandClaude Sonnet 5 298d2f248e split build workflow into binary and image jobs
build-binary compiles the frontend and Go binary (with the module and
build caches) and uploads it as an artifact; build-image downloads the
artifact and builds/pushes the Docker image from it.

The executable bit is restored after download since upload-artifact
does not preserve it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 16:25:09 +01:00
aneurinandClaude Sonnet 5 dd6bb1a547 build zampler binary in CI instead of the Dockerfile
Build / build-and-push (push) Successful in 5m9s
The Go compile is dominated by modernc.org/libc (~400k lines of
generated Go from modernc.org/sqlite). Inside `docker build` on an
ephemeral runner it recompiled cold every push; on a 1 CPU / 1 GB
runner that took ~10 minutes.

Move the frontend and Go builds into the workflow, where actions/cache
persists ~/.cache/go-build across runs so libc is compiled once. The
Dockerfile is now just an alpine wrapper around the prebuilt
CGO_ENABLED=0 static binary.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 15:26:36 +01:00
aneurinandClaude Sonnet 5 974edb28c2 optimise Dockerfile layer caching
Build / build-and-push (push) Successful in 11s
Copy dependency manifests before source so npm ci / go mod download
layers survive source-only changes, and add BuildKit cache mounts for
the npm, Go module, and Go build caches. Build a static binary with
CGO_ENABLED=0 -ldflags="-s -w".

Tighten .dockerignore so the 177M frontend/node_modules, .git, docs and
editor dirs stay out of the build context.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 14:46:42 +01:00
aneurin a9a7f7df0a add gitea build workflow
Build / build-and-push (push) Canceled after 12m56s
2026-09-06 14:37:50 +01:00
aneurin 257765983f improve autoplay 2026-09-03 12:36:51 +01:00
aneurin e39d068ebe cleanup errant log lines 2026-09-01 10:52:20 +01:00
aneurin d3e505c64f improve docker bits 2026-09-01 10:52:12 +01:00
aneurin 3d8d909663 frontend, download, search fixes 2026-09-01 10:45:18 +01:00
aneurin 2f279e9ea0 api fixes, improvements 2026-09-01 10:22:48 +01:00
aneurin 0e762ac675 add --no-scan 2026-09-01 09:24:59 +01:00
aneurin da0885db92 adopt sqimple from scaffold 2026-09-01 01:30:49 +01:00
aneurin 17ce3314f5 working db scanner 2026-09-01 01:26:54 +01:00
aneurin 06ae7f16ec replacing memory db with sqlite pt1 2026-08-31 22:58:18 +01:00
aneurin 84fc57f43f simplify file scanning, skip unwanted extensions 2026-08-31 19:05:46 +01:00
aneurin ddbf13e05f npm update 2026-08-31 18:38:00 +01:00
aneurin 0f5184c93c clean up unwanted deps 2026-08-31 18:37:18 +01:00
aneurin 731f6e198e file access fixes 2026-08-31 18:31:37 +01:00
aneurin 667a7b173d working pages and paginated api support 2026-08-31 18:23:18 +01:00
aneurin d844bd8561 breaking up frontend into views 2026-08-31 17:02:10 +01:00
aneurin fe45546e00 fix json response structure bug 2026-08-31 17:02:01 +01:00
aneurin f621b01a45 improve output formatting 2026-08-31 13:10:17 +01:00
aneurin ee9a9ccebf npm i again 2026-08-31 13:10:17 +01:00
aneurin d3a86108d6 npm i 2026-08-31 13:10:17 +01:00
aneurin c17fe75346 npm audit fix 2026-08-31 13:10:17 +01:00
aneurin 61ec887e4e go mod tidy 2026-08-31 13:10:17 +01:00
aneurin ca1a4deaea add api querying and pagination 2026-08-31 12:03:11 +01:00
aneurin 01d5653974 improve startup with deferred scan 2026-08-30 20:02:08 +01:00
aneurin f7009a411c cleanup index, unused assets 2026-07-22 10:11:55 +01:00
aneurin 1513b5314f add missing colors from light scheme 2026-07-22 10:08:47 +01:00
aneurin 054fc88293 tweak readme 2026-07-22 10:04:36 +01:00
aneurin bdf379cb89 update docs 2026-07-22 09:53:58 +01:00
aneurin ac0d3b2ecf unstick footer 2026-07-22 09:17:06 +01:00
aneurin 404e79fe26 fix audio player bugs, cleanup buttons 2026-07-22 09:15:23 +01:00
aneurin ba08ec5081 fix download button refresh bug 2026-07-22 08:33:21 +01:00
aneurin 49180a462d add text files 2026-07-21 23:14:07 +01:00
aneurin e25737b097 minor fixes 2026-07-21 22:59:38 +01:00
aneurin d33987aef9 add finishing touches, dockerfile 2026-07-21 22:55:38 +01:00
aneurin 1119bf1a3d moved audio player to header, fixed layout problems 2026-07-21 21:51:05 +01:00
aneurin 4abb9e6826 frontend coming together 2026-07-21 17:50:02 +01:00
aneurin e399b10286 add more audio player controls 2026-07-21 14:33:27 +01:00
aneurin 7771432b6e add search, bookmarks, lots of cleanup 2026-07-21 14:23:46 +01:00
aneurin d71c49284b improve download icon 2026-07-21 01:05:36 +01:00
aneurin 085d185593 improve downloading 2026-07-21 01:00:53 +01:00
aneurin cae7bfaa50 add audio player 2026-07-21 00:18:08 +01:00
aneurin 6a4f56b0bf sample grid improvements 2026-07-20 23:14:16 +01:00
aneurin 8980df82bc start adding samples grid to frontend 2026-07-20 09:37:05 +01:00
aneurin bf3820033a init frontend project
npm create vite@latest
2026-07-20 05:51:58 +01:00
aneurin 8b37a74e0d add file streaming endpoints 2026-07-17 15:42:52 +01:00
aneurin 66413c1036 reorganise code into func areas pt2 2026-07-17 15:03:35 +01:00
aneurin 3ed4bc949f reorganise code into func areas 2026-07-17 14:59:55 +01:00
aneurin 5b625b56f5 add file by path api 2026-07-17 14:30:28 +01:00
aneurin 8b85456754 progress on api, logging 2026-07-16 09:18:02 +01:00
aneurin 5e3c028140 clean up go.mod, app 2026-07-16 08:47:56 +01:00
aneurin 1328098668 add files api 2026-07-16 00:45:42 +01:00
aneurin 9e9fc32efd initial commit 2026-07-16 00:33:50 +01:00