5492dd8cf1a041d0c0ab307897dc72cdf4929227
17
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5492dd8cf1 |
Stop building a standalone api release artifact
CI / go (push) Successful in 43s
CI / sqlc (push) Successful in 7s
CI / web (push) Successful in 18s
Release / build-tui (amd64, linux) (push) Successful in 25s
Release / build-tui (amd64, darwin) (push) Successful in 26s
CI / docker (push) Successful in 1m0s
Release / build-tui (arm64, darwin) (push) Successful in 21s
Release / build-tui (amd64, windows) (push) Successful in 21s
Release / build-tui (arm64, linux) (push) Successful in 43s
Release / build-web (push) Successful in 38s
Release / build-gui (amd64, windows, gcc-mingw-w64-x86-64) (push) Successful in 57s
Release / build-gui (amd64, linux, libgl1-mesa-dev xorg-dev libwayland-dev libxkbcommon-dev) (push) Successful in 1m4s
Release / build-docker (push) Successful in 47s
Release / release (push) Successful in 24s
The Docker image (build-docker) is the real distribution path for the server; a raw completed-api binary alongside it was a redundant artifact nobody was going to run directly. Renamed build-go -> build-tui since it now only builds the tui, which is a real CLI people do run on their own machine. Updated the release job's `needs:` and stale comments that still said build-go. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v0.0.3 |
||
|
|
d1d5ac8f98 |
Upload raw executables in release.yml instead of zip/tar.gz
CI / web (push) Successful in 22s
CI / go (push) Successful in 50s
CI / sqlc (push) Successful in 6s
CI / docker (push) Successful in 37s
Release / build-go (amd64, windows) (push) Successful in 23s
Release / build-go (amd64, linux) (push) Successful in 27s
Release / build-go (arm64, darwin) (push) Successful in 39s
Release / build-go (amd64, darwin) (push) Successful in 44s
Release / build-go (arm64, linux) (push) Successful in 35s
Release / build-web (push) Successful in 20s
Release / build-gui (amd64, windows, gcc-mingw-w64-x86-64) (push) Successful in 44s
Release / build-gui (amd64, linux, libgl1-mesa-dev xorg-dev libwayland-dev libxkbcommon-dev) (push) Successful in 56s
Release / build-docker (push) Successful in 52s
Release / release (push) Successful in 33s
build-go and build-gui now build straight to their final release filename (e.g. dist/Completed-api_linux_amd64, dist/Completed-gui_windows_amd64.exe) and upload-artifact uploads that directly - no separate Archive step, no zip/tar.gz wrapping a single or paired executable. build-web stays archived: it's not an executable, it's the built Angular site (a directory of HTML/JS/CSS/asset files), so bundling it into one tar.gz is still the right call there. Trade-off worth knowing: a raw upload doesn't preserve the Unix executable bit the way tar.gz did, so linux/darwin downloads need `chmod +x` before running; .exe downloads are unaffected. Documented in release.yml's comments. The release job's download/upload loop needed no changes - it already walks whatever's under artifacts/ with `find -type f`, regardless of how many raw files land in a given artifact's subdirectory. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v0.0.2 |
||
|
|
f1337e824e |
Downgrade release.yml's artifact actions to v3
CI / go (push) Successful in 46s
Release / build-go (amd64, linux) (push) Successful in 47s
CI / docker (push) Successful in 51s
CI / sqlc (push) Successful in 9s
CI / web (push) Successful in 25s
Release / build-go (amd64, darwin) (push) Successful in 53s
Release / build-go (amd64, windows) (push) Successful in 52s
Release / build-go (arm64, darwin) (push) Successful in 31s
Release / build-go (arm64, linux) (push) Successful in 30s
Release / build-web (push) Successful in 17s
Release / build-gui (amd64, linux, libgl1-mesa-dev xorg-dev libwayland-dev libxkbcommon-dev) (push) Successful in 49s
Release / build-docker (push) Successful in 52s
Release / build-gui (amd64, windows, gcc-mingw-w64-x86-64) (push) Successful in 4m16s
Release / release (push) Successful in 18s
Found by actually running release.yml against a real v0.0.1 tag: every build-* job failed at actions/upload-artifact@v4 with "GHESNotSupportedError: @actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+ are not currently supported on GHES." - exactly the risk flagged (but not yet hit) when this workflow was first written. This Gitea Actions instance doesn't support the v4 artifact protocol; v3 does. v3 has no merge-multiple option: downloading all artifacts with no `name` given lands each one in its own artifacts/<artifact-name>/ subdirectory instead of flattened into artifacts/. Changed the generic-package-registry upload loop from a flat `artifacts/*` glob to `find artifacts -type f` so it finds files regardless of that nesting. Not yet confirmed: whether build-docker (needs secrets.TOKEN, still unset) and the rest of the release job succeed once the artifact step itself works - that's the next real run to check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v0.0.1 |
||
|
|
51d6e24122 |
Trigger CI to check runner capacity/concurrency
Release / build-go (amd64, linux) (push) Failing after 36s
Release / build-go (amd64, darwin) (push) Failing after 36s
Release / build-go (arm64, darwin) (push) Failing after 37s
Release / build-go (amd64, windows) (push) Failing after 38s
Release / build-web (push) Failing after 20s
Release / build-go (arm64, linux) (push) Failing after 22s
Release / build-gui (amd64, linux, libgl1-mesa-dev xorg-dev libwayland-dev libxkbcommon-dev) (push) Failing after 31s
CI / sqlc (push) Successful in 10s
CI / go (push) Successful in 33s
CI / web (push) Successful in 18s
CI / docker (push) Successful in 50s
Release / build-docker (push) Successful in 1m41s
Release / build-gui (amd64, windows, gcc-mingw-w64-x86-64) (push) Failing after 4m36s
Release / release (push) Skipped
|
||
|
|
066f316dc6 |
Install Wayland dev headers for the Fyne/glfw build in CI
Found by actually running ci.yml on the real Gitea instance: the go job failed at go vet with "wayland-client-core.h: No such file or directory" building go-gl/glfw (a Fyne dependency), which compiles in both its X11 and Wayland backends by default. libgl1-mesa-dev + xorg-dev alone aren't enough; added libwayland-dev and libxkbcommon-dev to both ci.yml and release.yml's linux packages list. I'd only verified cmd/gui's build on macOS and via the Windows mingw-w64 cross-compile - never on an actual Linux machine, so this gap wasn't visible until the real CI run hit it. sqlc, web and docker all passed on that same run (confirmed from the job logs, not just the status column). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
959b6793da |
Remove the placeholder test.yml workflow
It set container: gitea/gitea:latest, which has no node - actions/checkout fails immediately with "node: executable file not found" (confirmed via tea actions runs logs 102 on the live instance: conclusion failure, 15s). ci.yml replaces it - same go test coverage plus sqlc/web/docker, and no container override, so it uses the runner's normal image instead of the Gitea server's own image. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
7f0cb01954 | Merge backend-foundation: multi-user API, three clients, Gitea CI/CD, Docker | ||
|
|
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> |
||
|
|
9972537b63 |
Add a small, tested base for all three clients, and Gitea CI/release workflows
internal/client (new): a typed Go HTTP client for the API - login, register, logout, me, and full CRUD for games/platforms - shared by the TUI and the Fyne GUI so request-building, auth and error handling (an APIError with the HTTP status) live in one tested place instead of being duplicated. 8 tests against an httptest server covering success, 401, 403 and that the token is stored/cleared at the right times. cmd/tui (rewrite): drops the old unauthenticated read-only viewer. Now a login screen (bubbles/textinput) -> a scrollable games table (bubbles/table) with 'r' to refresh, built as an explicit state machine (screenLogin/screenLoading/screenGames) so Update's transitions are testable without a real terminal. 9 tests driving Update() directly. cmd/gui (rewrite): drops raylib for Fyne. Same shape as the TUI - a login form, then a games table, with networking on a goroutine and fyne.Do for the UI updates. The table-cell and login-validation logic are plain functions with no Fyne/display dependency, so they're tested directly (7 tests) rather than needing Fyne's headless test driver. internal/api: tagged every operation (Auth/Platforms/Games/Misc) so the generated OpenAPI spec groups into named services instead of one bucket; api/openapi.json regenerated. Added CORS middleware (wildcard origin, no credentials needed since clients use a bearer token, not the cookie) - needed for the new web client's browser requests to the API's a different origin. web/ (new): an Angular 22 + Material app, API client generated from api/openapi.json via ng-openapi-gen (services: true for per-resource classes - AuthService/GamesService/PlatformsService - instead of one generic Api facade). A Session service holds the token/user as signals, persists the token to localStorage, and is the only thing that calls the generated AuthService; an HttpInterceptorFn attaches the bearer token to every request; a CanActivateFn guard protects /games. Login and Games are standalone, lazy-loaded route components. 21 tests (Vitest, Angular's new default - no browser/Karma needed) covering the service, interceptor, guard and both components, run via `npm test` exactly as CI will call it. Justfile: fixed run-tui/run-gui, which used `go run .../main.go` - broken now that both packages span multiple files, since that only compiles the one named file. Added web-install/web-dev/web-build/web-test. .gitea/workflows/ci.yml (moved from .github, which Gitea doesn't read): on push to main/develop - gofmt, go vet, go build, go test, go mod tidy diff (with the Fyne build's system deps installed), sqlc diff, and the web app's build+test. .gitea/workflows/release.yml (new): on a vX.Y.Z tag, cross-compiles api+tui for linux/darwin/windows (amd64+arm64, pure Go, no cgo), builds the Fyne GUI for linux/amd64 only (cgo + its OpenGL/X11 link means it can't cross-compile from one Linux runner the way api/tui do, and this repo has no registered macOS/Windows runner to build it on natively - documented in the workflow's comments), builds the web app, and creates a Gitea release with all of it attached via the Gitea API. Written to Gitea's documented GitHub-Actions-compatible syntax but not executable from here - there's no Gitea server in this environment to run it against, unlike everything else in this commit, which was. Verified: every Go package builds/vets/tests clean; sqlc diff and go mod tidy -diff are clean; the Angular app builds for production and its test suite passes via `npm test`/`npm run build` (the exact commands CI runs), including after a clean `npm ci` from the committed lockfile; the new CORS config was checked with real preflight and GET requests against a running server, not just read. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
384c4a7217 |
Restrict platform-list edits to admins
Anyone authenticated can still read the shared platform list, but only an
admin may add, rename or delete from it.
- users.is_admin boolean, defaulting to false. The very first account ever
registered becomes an admin automatically (CreateUser's INSERT uses
NOT EXISTS (SELECT 1 FROM users) for is_admin), so there's always at
least one without manual setup; promoting anyone beyond that is a manual
UPDATE for now (documented in the migration's comment on the column).
- GetSessionAndUser, GetUserByEmail and GetUserByID now also select
is_admin, and it flows through to model.User (and so to
/api/auth/{register,login,me}'s response) and to the auth middleware's
authedUser.
- New requireAdminMiddleware + addAdminAuth, layered on top of the existing
auth middleware. Wired onto create/update/delete-platform; list/get stay
on plain addAuth, since reading the list only requires being logged in.
Verified end to end: the first registered user comes back is_admin:true,
the second false; the non-admin gets 403 on create/update/delete-platform
but 200 on list; the admin can do all of it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
7fbd403d30 |
Make the platform list shared across users; keep games private
Platforms no longer have a user_id: there's one global list, unique by name, that every authenticated user reads from and writes to. Games are unchanged - still private per user, still referencing platform_id. - Migration: dropped platform.user_id and its FK, UNIQUE(user_id, name) -> UNIQUE(name), index on change_seq alone instead of (user_id, change_seq). Amended the one init migration in place rather than stacking a second one, since nothing is deployed yet. - db/query.sql: platform queries no longer take a user_id parameter. DeletePlatform's NOT EXISTS guard already checked games from any user (it never filtered the subquery by user_id), so deleting a shared platform is correctly blocked by any user's games on it, not just the deleter's. - routes.go: platform handlers still require auth (the list isn't public to anonymous requests) but no longer scope by user. - Tests: updated the mocks for the new signatures, added TestListPlatformsIsSharedAcrossUsers. Verified against a real Postgres: user A creates "PC", user B sees it immediately, both can add games against it, each only sees their own games, deleting the platform is blocked while either user has a game on it, and the name stays globally unique. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
5072919b61 |
Rebuild the schema and API on a working, multi-user foundation
Steps 1-4 of the review plan (docs/plans not checked in; see conversation):
Step 1 - make it boot from a clean checkout:
- Replace the old schema.sql + single ALTER-TABLE migration (which never
created the tables) with one init migration: users, sessions, platform,
game, all with UUIDv7-ready uuid ids, soft deletes (deleted_at), and a
shared change_seq sequence + trigger for a future sync feature.
- sqlc now reads the schema from db/migrations directly.
- Migrations are embedded in the binary (db/migrations/embed.go) instead of
being copied around and found via path fallbacks.
- ensureDatabase/runMigrations failures are now fatal, and main pings the
pool after connecting, instead of starting an API with no schema.
- Switched both the app and golang-migrate to pgx/v5 (dropped lib/pq).
- Fixed the Dockerfile's Go version (1.23 -> 1.25) and build target.
- Added a postgres healthcheck to docker-compose so the app waits for a
ready database instead of racing it.
- go.mod tidied, bin/ added to .gitignore, Justfile's docker-compose calls
switched to the v2 `docker compose` plugin.
Step 2 - auth and per-user data:
- internal/auth: argon2id password hashing, random session tokens stored
only as a hash.
- POST /api/auth/{register,login,logout}, GET /api/auth/me. Sessions work
via either an Authorization: Bearer header or an httpOnly cookie.
- Every platform/game query is scoped to the authenticated user.
Step 3 - API correctness:
- internal/model holds the DTOs, with no server-side dependencies, so
clients can import it later without pulling in Huma/Echo.
- dbError() maps ErrNoRows -> 404, unique violation -> 409, FK violation ->
409, instead of leaking Postgres error text with a 500.
- Soft-delete meant the FK on game.platform_id no longer blocks deleting a
referenced platform on its own; added a NOT EXISTS guard plus an
application-level check so that case now correctly returns 409 instead of
silently succeeding (caught by testing the live server, not by a mock).
- Added GET /games/{id} and GET /platforms/{id}. All JSON routes moved
under /api, freeing / for a future web client.
- ListGames/GetGame join platform_name in.
- cmd/openapi writes the generated spec to api/openapi.json.
Step 4 - tests and CI:
- Rewrote the mocks and handler tests for the new Querier shape; added
coverage for 401 (no session), 404 (not found / another user's row),
409 (duplicate email, platform referenced by games), 422 (score out of
range), and that queries are actually scoped by the authenticated user's
id, not just that the right status code comes back.
- .github/workflows/ci.yml: go vet, go test, go mod tidy -diff, sqlc diff.
Everything above was exercised against a real local Postgres (migrations
up/down, the trigger, constraints) and through the running server via curl
(register, login, scoping, every status-code case), not just unit tests.
Known regression, intentionally deferred to the next step: cmd/tui and
cmd/gui still call the old unauthenticated GET /games, which no longer
exists. They need internal/client and a login flow before they work again.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
fe30ab4514 |
Remove GitHub workflow file
test / test (push) Failing after 2s
|
||
|
|
1d335f1c0c |
Add Gitea CI/CD workflow for testing on main branch
test / test (push) Failing after 15s
|
||
|
|
6864129c74 | Add CI/CD workflow for testing on main branch | ||
|
|
47a9719eeb | Created a base to build on | ||
|
|
a1e33bd403 | Initial commit |