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>
This commit is contained in:
1 parent
384c4a7217
commit
9972537b63
97 files changed
+11810
-336
No files matched your search
@@ -65,6 +65,7 @@ func newSession(ctx context.Context, queries db.Querier, userID uuid.UUID) (stri
|
||||
func registerAuthRoutes(api huma.API, queries db.Querier) {
|
||||
huma.Register(api, huma.Operation{
|
||||
OperationID: "register",
|
||||
Tags: []string{"Auth"},
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/auth/register",
|
||||
Summary: "Register a new account",
|
||||
@@ -100,6 +101,7 @@ func registerAuthRoutes(api huma.API, queries db.Querier) {
|
||||
|
||||
huma.Register(api, huma.Operation{
|
||||
OperationID: "login",
|
||||
Tags: []string{"Auth"},
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/auth/login",
|
||||
Summary: "Log in to an existing account",
|
||||
@@ -133,6 +135,7 @@ func registerAuthRoutes(api huma.API, queries db.Querier) {
|
||||
|
||||
logoutOp := huma.Operation{
|
||||
OperationID: "logout",
|
||||
Tags: []string{"Auth"},
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/auth/logout",
|
||||
Summary: "Log out of the current session",
|
||||
@@ -149,6 +152,7 @@ func registerAuthRoutes(api huma.API, queries db.Querier) {
|
||||
|
||||
meOp := huma.Operation{
|
||||
OperationID: "get-me",
|
||||
Tags: []string{"Auth"},
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/auth/me",
|
||||
Summary: "Get the current user",
|
||||
|
||||
@@ -17,6 +17,7 @@ func RegisterRoutes(api huma.API, queries db.Querier) {
|
||||
// Root/Hello Route
|
||||
huma.Register(api, huma.Operation{
|
||||
OperationID: "get-hello",
|
||||
Tags: []string{"Misc"},
|
||||
Method: http.MethodGet,
|
||||
Path: "/hello",
|
||||
Summary: "Say Hello",
|
||||
@@ -52,6 +53,7 @@ func RegisterRoutes(api huma.API, queries db.Querier) {
|
||||
func registerPlatformRoutes(api huma.API, queries db.Querier) {
|
||||
listOp := huma.Operation{
|
||||
OperationID: "list-platforms",
|
||||
Tags: []string{"Platforms"},
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/platforms",
|
||||
Summary: "List Platforms",
|
||||
@@ -71,6 +73,7 @@ func registerPlatformRoutes(api huma.API, queries db.Querier) {
|
||||
|
||||
getOp := huma.Operation{
|
||||
OperationID: "get-platform",
|
||||
Tags: []string{"Platforms"},
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/platforms/{id}",
|
||||
Summary: "Get Platform",
|
||||
@@ -88,6 +91,7 @@ func registerPlatformRoutes(api huma.API, queries db.Querier) {
|
||||
|
||||
createOp := huma.Operation{
|
||||
OperationID: "create-platform",
|
||||
Tags: []string{"Platforms"},
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/platforms",
|
||||
Summary: "Create Platform",
|
||||
@@ -103,6 +107,7 @@ func registerPlatformRoutes(api huma.API, queries db.Querier) {
|
||||
|
||||
updateOp := huma.Operation{
|
||||
OperationID: "update-platform",
|
||||
Tags: []string{"Platforms"},
|
||||
Method: http.MethodPut,
|
||||
Path: "/api/platforms/{id}",
|
||||
Summary: "Update Platform",
|
||||
@@ -124,6 +129,7 @@ func registerPlatformRoutes(api huma.API, queries db.Querier) {
|
||||
|
||||
deleteOp := huma.Operation{
|
||||
OperationID: "delete-platform",
|
||||
Tags: []string{"Platforms"},
|
||||
Method: http.MethodDelete,
|
||||
Path: "/api/platforms/{id}",
|
||||
Summary: "Delete Platform",
|
||||
@@ -185,6 +191,7 @@ func parseDate(s string) (pgtype.Date, error) {
|
||||
func registerGameRoutes(api huma.API, queries db.Querier) {
|
||||
listOp := huma.Operation{
|
||||
OperationID: "list-games",
|
||||
Tags: []string{"Games"},
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/games",
|
||||
Summary: "List Games",
|
||||
@@ -212,6 +219,7 @@ func registerGameRoutes(api huma.API, queries db.Querier) {
|
||||
|
||||
getOp := huma.Operation{
|
||||
OperationID: "get-game",
|
||||
Tags: []string{"Games"},
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/games/{id}",
|
||||
Summary: "Get Game",
|
||||
@@ -237,6 +245,7 @@ func registerGameRoutes(api huma.API, queries db.Querier) {
|
||||
|
||||
createOp := huma.Operation{
|
||||
OperationID: "create-game",
|
||||
Tags: []string{"Games"},
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/games",
|
||||
Summary: "Create Game",
|
||||
@@ -280,6 +289,7 @@ func registerGameRoutes(api huma.API, queries db.Querier) {
|
||||
|
||||
updateOp := huma.Operation{
|
||||
OperationID: "update-game",
|
||||
Tags: []string{"Games"},
|
||||
Method: http.MethodPut,
|
||||
Path: "/api/games/{id}",
|
||||
Summary: "Update Game",
|
||||
@@ -327,6 +337,7 @@ func registerGameRoutes(api huma.API, queries db.Querier) {
|
||||
|
||||
deleteOp := huma.Operation{
|
||||
OperationID: "delete-game",
|
||||
Tags: []string{"Games"},
|
||||
Method: http.MethodDelete,
|
||||
Path: "/api/games/{id}",
|
||||
Summary: "Delete Game",
|
||||
|
||||
Reference in new issue
Block a user