SansanandClaude Sonnet 5 786fdbb0d5 Dockerize the API with an embedded web UI, Fyne-for-Windows, and real releases
cmd/api/main.go: registerWebUI serves a built web/ as a single-page app -
Echo's static middleware in HTML5 mode, with a Skipper that exempts
/api/*, /hello, /docs, /schemas/* and openapi.json/.yaml so it can never
shadow a real API route. It reads WEB_DIST_DIR (default /frontend, where
the Dockerfile puts it) and no-ops if that directory doesn't exist, so
`go run ./cmd/api` and CI stay Node-free and API-only; only the Docker
image actually turns the web UI on.

Dockerfile: new node:22-alpine stage builds web/, copied into the final
image at /frontend alongside the existing Go-built binary. Added
.dockerignore (web/node_modules, web/dist, web/.angular, .git) now that a
second language's tooling is in the build context.

Looked at how your other Gitea projects (MusicServer et al.) structure
this, since I can't exercise Gitea Actions itself from here: MusicServer's
own Dockerfile already does "copy a prebuilt frontend into the Go image,
serve it from Go" (there, from a separately-published frontend image) -
registerWebUI does the same thing in spirit, just within one image rather
than two, since this UI isn't meant to be published separately.

release.yml:
- build-gui now also cross-compiles the Fyne GUI for windows/amd64 via
  mingw-w64, alongside the existing linux/amd64. Verified for real, not
  just written: installed mingw-w64 locally and cross-built cmd/gui,
  first plain (got a working PE32+ console exe), then with
  -ldflags "-H=windowsgui" (got the GUI-subsystem exe actually wanted, no
  console window). macOS isn't attempted - cgo has no equivalent
  cross-toolchain for Darwin and there's no registered macOS runner here.
- new build-docker job pushes the API image (now carrying the web build)
  to gitea.sanplex.xyz/sansan/completed, tagged with the version and
  latest.
- replaced the hand-rolled curl+jq Gitea-release creation with
  akkuman/gitea-release-action, and artifact upload with the Gitea generic
  package registry - both are what your other projects (HanjaKanaMun,
  MusicPlayer) actually use, found by reading their workflows rather than
  guessing. Same for the registry host, owner, and secrets.TOKEN name
  (previously secrets.GITHUB_TOKEN, which nothing else here uses).

ci.yml: new docker job builds the same image on every push to main/develop
(push: false) as a health check, matching MusicServer's build-vs-release
split.

Verified for real wherever I could: a full `docker build` of the new
Dockerfile, then running that image against a real Postgres container and
hitting it with curl - / serves the actual built Angular index.html,
/games (a client-side route) correctly falls back to it, /api/games and
/hello are untouched (still return real API responses, not shadowed by
the static handler), and register+list-games works end to end through the
container. Also ran it the documented way, `docker compose up --build`,
with the same results. Separately confirmed the Windows Fyne cross-build
produces a real, correctly-subsystemed .exe. What I could not run from
here is Gitea Actions itself - the workflow YAML is written to match your
other projects' established, working conventions, but its first real
execution on your Gitea instance is still the first real test of it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-03 12:49:36 +02:00
2026-10-03 11:39:21 +02:00

Completed

A tracker for a video game collection: what you own, which platform it's on, your score for it, and whether (and when) you finished it.

Status

Each account has its own private game list, but all accounts share one platform list (only an admin can add/rename/delete a platform - the first account ever registered becomes an admin automatically). The API is usable, and serves its own web UI (see below). There are three small-base clients: a terminal UI, a desktop GUI (Fyne), and a web app (Angular) — each can log in and show your games, with more to come.

Running it

just docker-up    # starts Postgres and the API on :8080
just docker-down  # stops them

Or against a Postgres you already have running locally:

export DATABASE_URL="postgres://postgres:password@localhost:5432/gamedb?sslmode=disable"
just run-api

The API creates its database and runs migrations on startup if needed. Browse http://localhost:8080/docs for the generated API docs, or http://localhost:8080/ for the web UI - the Docker image bakes in a production build of web/ and the API serves it itself (see registerWebUI in cmd/api/main.go); unknown paths fall back to its index.html so Angular's router handles deep links like /games. Running the API outside Docker (just run-api) skips this and serves the API only, unless WEB_DIST_DIR points at a build of web/ (just web-build outputs one to web/dist/web/browser).

Using the API

# Register (also logs you in; the first account ever registered becomes
# an admin, which is required to create platforms - see Status above)
curl -X POST localhost:8080/api/auth/register -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com","password":"a long password","name":"You"}'
# -> {"token": "...", "user": {...}}

TOKEN=... # the token from above

curl -X POST localhost:8080/api/platforms -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' -d '{"name":"PC"}'

curl localhost:8080/api/games -H "Authorization: Bearer $TOKEN"

A browser-based client can skip the Authorization header: register/login also set an httpOnly session cookie.

Clients

  • just run-tui — a terminal UI (Bubble Tea). Log in, see your games in a scrollable table, r to refresh.
  • just run-gui — a desktop GUI (Fyne). Log in, see your games in a table, refresh.
  • just web-dev — a web app (Angular + Material) at http://localhost:4200, proxying nothing itself; it talks directly to the API at localhost:8080 (CORS is permissive enough for local dev as-is). Log in, see your games. This is for frontend development with hot reload; the API's own / (see above) serves a production build of the same app with nothing extra to run.

All three point at http://localhost:8080 by default. The TUI and GUI read COMPLETED_API_URL to use a different API; the web app's URL is a build-time constant in web/src/app/app.config.ts.

Each client is a small base - listing is there, but adding/editing a game or managing platforms isn't yet.

Development

just test       # go test ./...
just sqlc       # regenerate internal/db after changing db/query.sql or a migration
just openapi    # regenerate api/openapi.json after changing a route
just web-test   # web app unit tests (Vitest)
just web-build  # production build of the web app

Schema changes go in db/migrations as a new <timestamp>_<name>.up.sql / .down.sql pair; sqlc.yaml reads the schema from that directory directly, so there's no separate schema file to keep in sync.

The TUI and GUI share internal/client, a typed Go HTTP client for the API. The web app's API client (web/src/app/api/) is generated from api/openapi.json by ng-openapi-gen (cd web && npx ng-openapi-gen) and shouldn't be hand-edited.

CI/CD

.gitea/workflows/ci.yml builds and tests everything on every push to main or develop: Go (gofmt/vet/build/test/tidy), sqlc, the web app, and a Docker build-only check (the same image release.yml later pushes).

.gitea/workflows/release.yml runs on a vX.Y.Z tag:

  • cross-compiles completed-api/completed-tui for linux, darwin and windows (amd64 + arm64 where it makes sense - both are pure Go, no cgo)
  • cross-compiles the Fyne GUI for linux/amd64 (native) and windows/amd64 (via mingw-w64; verified locally - a real GUI-subsystem .exe, no console window). macOS isn't built, since cgo has no equivalent cross-toolchain and this repo has no registered macOS runner
  • builds the web app
  • builds and pushes the API's Docker image (which embeds that same web build) to gitea.sanplex.xyz/sansan/completed, tagged with the version and latest
  • uploads every build artifact to the Gitea generic package registry and attaches them to a Gitea release

Both workflows were run for real against this repo - the Go test suite, sqlc diff, go mod tidy -diff, the Angular build and test suite, the Fyne-for-Windows cross-compile, and the actual multi-stage Docker build (built, run against a real Postgres container, and hit with curl) all passed. The workflow YAML itself (trigger/job wiring, the Gitea API calls for uploading packages and creating the release) is written to match this Gitea instance's other projects, but wasn't run through Gitea Actions itself from this environment - worth watching the first real run of each.

S
Description
No description provided
Readme
229 KiB
0 Stars 1 Watchers 0 Forks
v0.0.3
Latest
2026-10-03 20:24:31 +02:00
Languages
TypeScript 47.1%
Go 46.3%
HTML 2%
PLpgSQL 1.9%
Dockerfile 0.9%
Other 1.8%