Commit Graph

7 Commits

Author SHA1 Message Date
H1K0 16e68236a0 build(project): ship the dedup CLI in the runtime image
The backend build stage compiled only ./cmd/server, so the dedup maintenance
tool was never available on deploy. Build it alongside the server and copy
/out/dedup to /app/dedup in the runtime image (which already has ffmpeg/ffprobe
for video frames and the /data volume). Run it with `docker exec`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 13:13:53 +03:00
H1K0 58cea88f52 fix(project): drop external dockerfile frontend directive
deploy / deploy (push) Successful in 3s
The `# syntax=docker/dockerfile:1` line made BuildKit fetch its frontend
image from Docker Hub on every build, even when all base images and layers
were already cached. On a host that briefly can't resolve registry-1.docker.io
this is the first and only mandatory network round-trip, so the build fails at
"resolve image config for docker-image://docker.io/docker/dockerfile:1" before
any stage runs.

This Dockerfile uses no frontend-specific syntax (no heredocs, no RUN --mount,
no COPY --link) — only multi-stage, COPY --from/--chown, RUN, ENV, etc., all
handled by the engine's built-in frontend. Dropping the directive removes the
Docker Hub dependency and lets a fully cached build complete offline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 12:15:23 +03:00
H1K0 88e07f0723 build(project): install vips-tools for image thumbnailing
deploy / deploy (push) Successful in 57s
Add vips-tools (vipsthumbnail) to the runtime image alongside ffmpeg and
exiftool, and document THUMB_MAX_PIXELS as the pure-Go fallback guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 01:25:55 +03:00
H1K0 5571dfa46d chore(project): install exiftool in the runtime image
The backend now shells out to exiftool for metadata extraction, so it must
be present alongside ffmpeg in the Alpine runtime stage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 20:55:33 +03:00
H1K0 fce71bb946 feat(project): add Docker Compose with flexible storage and DB modes
Bundle the app + Postgres into a compose stack on top of the existing image.

- app: builds the image, publishes ${APP_PORT:-42776}, reads .env, pins
  STATIC_DIR so SPA serving can't be disabled by an empty value
- db: postgres:14-alpine under the "with-db" profile; toggle it off via
  COMPOSE_PROFILES to point the app at a Postgres on the host instead
  (host.docker.internal), with depends_on required:false so it stays optional

Storage and the DB data dir each default to a named volume but can be bind
mounted to a host folder via FILES_DIR / THUMBS_DIR / IMPORT_DIR / DB_DIR.
Add PUID/PGID (via user:) so bind-mounted folders are writable by the
non-root container.

Run the container as a dedicated non-root user "tanabata" with uid/gid 42776,
reusing the project's signature number (also the default port). Document every
variable in .env.example.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 11:26:25 +03:00
H1K0 69650b6464 chore(project): set the default port to 42776
Make 42776 the project's default listen port everywhere :8080 was the default:
.env.example, the Go config fallback, the Dockerfile (ENV/EXPOSE/healthcheck),
and the docs example. 42776 is the sum of the Unicode code points of 七夕
(七 U+4E03 = 19971, 夕 U+5915 = 22805).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 11:03:26 +03:00
H1K0 f5f7db6c2a feat(project): containerize as a single image serving SPA + API
Add a multi-stage Dockerfile that builds the SvelteKit SPA (adapter-static,
no Node runtime in the final image) and the Go server, then ships an Alpine
runtime that serves both the static frontend and the API on one port.

- Stage 1 (node): npm ci + build → static SPA (index.html, _app, fonts, sw)
- Stage 2 (golang): CGO_ENABLED=0 static binary (image processing is pure Go)
- Stage 3 (alpine): + ffmpeg for video thumbnails, non-root user, /data volume,
  healthcheck on /health; secrets passed at runtime, not baked in

To serve the SPA on the API port, the Go server now optionally hosts static
files behind a new STATIC_DIR env var: a request maps to a real file when one
exists, otherwise falls back to index.html for client-side routes; unknown
/api/ paths still return JSON 404. Empty STATIC_DIR (local dev) keeps the API
standalone while Vite serves the UI. Cache-Control is tuned to adapter-static
output (immutable hashed assets, no-cache service worker) and .webmanifest is
registered so nosniff doesn't reject the PWA manifest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 10:52:27 +03:00