Commit Graph

33 Commits

Author SHA1 Message Date
Ryan Hamamura
72d31fd143 fix: convert auth flows from SSE to standard HTTP to fix session cookies
Some checks failed
CI / Deploy / test (pull_request) Successful in 33s
CI / Deploy / lint (pull_request) Failing after 38s
CI / Deploy / deploy (pull_request) Has been skipped
Datastar's NewSSE() flushes HTTP headers before SCS's session middleware
can attach the Set-Cookie header, so the session cookie never reaches the
browser after login/register/logout.

Convert login, register, and logout to standard HTML forms with HTTP
redirects, which lets SCS write cookies normally. Also fix return_url
capture on the login page (was never being stored in the session).

Add handler tests covering login, register, and logout flows.
2026-03-11 10:10:28 -10:00
67a768ea22 Fix SSE architecture for reliable connections (#13)
All checks were successful
CI / Deploy / test (push) Successful in 14s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Successful in 1m32s
2026-03-03 23:33:13 +00:00
Ryan Hamamura
d6e64763cc fix: use templ.NewOnceHandle to prevent script duplication on SSE patches
All checks were successful
CI / Deploy / test (pull_request) Successful in 15s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Replace ConnectionIndicatorWithScript wrapper with a single ConnectionIndicator
component that uses templ.NewOnceHandle() to ensure the watcher script is only
rendered once per page, even when the indicator is patched via SSE.
2026-03-03 10:43:23 -10:00
Ryan Hamamura
06b3839c3a refactor: patch connection indicator with timestamp
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 26s
CI / Deploy / deploy (pull_request) Has been skipped
Server patches the ConnectionIndicator element with a timestamp on
each heartbeat. Client-side JS checks every second if the timestamp
is stale (>20s) and toggles red/green accordingly.

This properly detects connection loss since the indicator will turn
red if no patches are received.
2026-03-03 10:30:55 -10:00
Ryan Hamamura
da82f31d46 feat: add connection status indicator with SSE heartbeat
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
- Add ConnectionIndicator component showing green/red dot
- Send lastPing signal every 15 seconds via SSE
- Indicator turns red if no ping received in 20 seconds
- Gives users confidence the live connection is active
2026-03-03 10:05:03 -10:00
Ryan Hamamura
bcb1fa3872 refactor: simplify chat subscription API
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Room.Subscribe() now returns a channel of parsed Message structs
instead of raw NATS messages. The room handles NATS subscription
and message parsing internally, so callers no longer need to call
Receive() separately.
2026-03-03 09:45:56 -10:00
bf9a8755f0 Merge pull request 'Add version display in UI footer' (#8) from feat/version-display into main
All checks were successful
CI / Deploy / test (push) Successful in 14s
CI / Deploy / lint (push) Successful in 26s
CI / Deploy / deploy (push) Successful in 1m27s
2026-03-03 19:41:59 +00:00
Ryan Hamamura
eb75654403 feat: display app version in UI footer
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
- Add version package with build-time variables
- Inject version via ldflags in Dockerfile using git describe
- Show version in footer on every page
- Log version and commit on server startup
2026-03-03 09:40:23 -10:00
Ryan Hamamura
c52c389f0c Reapply "fix: append chat messages instead of re-rendering entire game"
Some checks failed
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Failing after 2s
CI / Deploy / deploy (pull_request) Has been skipped
This reverts commit 513467470c.
2026-03-03 09:15:46 -10:00
Ryan Hamamura
513467470c Revert "fix: append chat messages instead of re-rendering entire game"
All checks were successful
CI / Deploy / test (push) Successful in 14s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Successful in 3s
This reverts commit 6976b773bd.
2026-03-03 09:15:42 -10:00
Ryan Hamamura
6976b773bd fix: append chat messages instead of re-rendering entire game
All checks were successful
CI / Deploy / test (push) Successful in 15s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Successful in 33s
Previously patchAll() re-rendered the full GameContent on every chat
message, which was inefficient and could cause UI glitches. Now we
append just the new ChatMessage to the chat history element.
2026-03-03 09:09:51 -10:00
Ryan Hamamura
1db6b2596e fix: disable SSE request cancellation for live game updates
All checks were successful
CI / Deploy / test (pull_request) Successful in 25s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
The default Datastar requestCancellation:'auto' was causing SSE
connections to be cancelled whenever users interacted with the page
(making moves, sending chat messages, etc.), breaking live updates.
2026-03-03 08:49:35 -10:00
Ryan Hamamura
64b5d384ed fix: use correct Datastar keydown event syntax
All checks were successful
CI / Deploy / test (push) Successful in 15s
CI / Deploy / lint (push) Successful in 25s
CI / Deploy / deploy (push) Successful in 34s
Replace invalid .key_enter and .enter modifiers with evt.key === 'Enter'
guard in the expression, per Datastar docs. Also fix __stop and __throttle
modifier syntax to use double underscores.
2026-03-02 23:05:11 -10:00
Ryan Hamamura
dcf76bb773 refactor: replace session key strings with consts
Some checks failed
CI / Deploy / test (pull_request) Successful in 13s
CI / Deploy / lint (pull_request) Failing after 20s
CI / Deploy / deploy (pull_request) Has been skipped
Define KeyPlayerID, KeyUserID, and KeyNickname in the sessions package
and use them across all handlers to avoid duplicated magic strings.
2026-03-02 22:40:10 -10:00
Ryan Hamamura
4faf4f73b0 refactor: patch entire game content for snake SSE handler
Some checks failed
CI / Deploy / test (pull_request) Successful in 16s
CI / Deploy / lint (pull_request) Failing after 20s
CI / Deploy / deploy (pull_request) Has been skipped
Same approach as connect4 — extract GameContent component and patch it
as a single element, letting DOM morphing handle the diff.
2026-03-02 22:34:20 -10:00
Ryan Hamamura
0808c4d972 refactor: patch entire game content instead of individual components
Some checks failed
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Failing after 21s
CI / Deploy / deploy (pull_request) Has been skipped
Extract GameContent from GamePage so the SSE handler can patch a single
element and let DOM morphing diff the changes, replacing the per-component
sendGameComponents helper.
2026-03-02 21:43:25 -10:00
Ryan Hamamura
42211439c9 refactor: drop redundant WithSelectorID from SSE patches
Some checks failed
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Failing after 22s
CI / Deploy / deploy (pull_request) Has been skipped
All templ components already have id attributes on their root elements,
which PatchElementTempl uses automatically.
2026-03-02 21:34:46 -10:00
Ryan Hamamura
fb6c0e3d90 refactor: replace hardcoded NATS subjects with typed helpers
Some checks failed
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Failing after 21s
CI / Deploy / deploy (pull_request) Has been skipped
Add GameSubject/ChatSubject helpers to connect4 and snake packages,
eliminating magic string concatenation from handlers and main.go.
2026-03-02 21:30:47 -10:00
Ryan Hamamura
2cfd42b606 refactor: integrate chat persistence into Room
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Move SaveMessage/LoadMessages logic into Room as private methods.
NewPersistentRoom auto-loads history and auto-saves on Send, removing
the need for handlers to coordinate persistence separately.
2026-03-02 21:25:03 -10:00
Ryan Hamamura
c6885a069b refactor: rename Go module from c4 to games
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Rename module path github.com/ryanhamamura/c4 to
github.com/ryanhamamura/games across go.mod, all source files,
and golangci config.
2026-03-02 20:41:20 -10:00
Ryan Hamamura
38eb9ee398 refactor: rename game package to connect4, drop Game prefix from types
All checks were successful
CI / Deploy / test (pull_request) Successful in 16s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Rename game/ -> connect4/ to avoid c4/game stutter. Drop redundant
Game prefix from exported types (GameStore -> Store, GameInstance ->
Instance, GameStatus -> Status). Rename NATS subjects from game.{id}
to connect4.{id}. URL routes unchanged.
2026-03-02 20:31:00 -10:00
Ryan Hamamura
10de5d21ad refactor: extract standalone chat package from game-specific handlers
Some checks failed
CI / Deploy / test (pull_request) Failing after 11s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Create chat/ package with Message type, Room (NATS pub/sub + buffer),
DB persistence helpers, and a unified templ component parameterized by
Config (CSS prefix, post URL, color function, key propagation).

Both c4game and snakegame now use chat.Room for message management and
chatcomponents.Chat for rendering, eliminating the duplicated
ChatMessage types, chat templ components, chatAutoScroll scripts,
color functions, and inline buffer management.
2026-03-02 19:20:21 -10:00
Ryan Hamamura
7eadfbbb0c refactor: extract session helpers for player identity resolution
Add GetPlayerID, GetUserID, GetNickname to the sessions package.
Remove the inline player-ID-from-session pattern duplicated across
every handler in c4game and snakegame, and the local getPlayerID
helper in snakegame.
2026-03-02 19:16:09 -10:00
Ryan Hamamura
063b03ce25 refactor: extract shared player.ID type and GenerateID to player package
Both game and snake packages had identical PlayerID types and the snake
package imported game.GenerateID. Now both use player.ID and
player.GenerateID from the shared player package.
2026-03-02 19:09:01 -10:00
Ryan Hamamura
bc6488f063 refactor: deduplicate persistence, add upsert queries, throttle snake saves
Some checks failed
CI / Deploy / test (pull_request) Failing after 15s
CI / Deploy / lint (pull_request) Failing after 23s
CI / Deploy / deploy (pull_request) Has been skipped
- Replace Create+Get+Update with UpsertGame/UpsertSnakeGame queries
- Extract free functions (saveGame, loadGame, etc.) from duplicated
  receiver methods on Store and Instance types
- Remove duplicate generateID from snake package, reuse game.GenerateID
- Throttle snake game DB writes to every 2s instead of every tick
- Fix double-lock in c4game chat handler
- Update all code for sqlc pointer types (*string instead of sql.NullString)
2026-03-02 16:56:29 -10:00
Ryan Hamamura
2bea5bb489 chore: gitignore generated _templ.go files, track .templ sources
Some checks failed
CI / Deploy / test (pull_request) Failing after 13s
CI / Deploy / lint (pull_request) Failing after 24s
CI / Deploy / deploy (pull_request) Has been skipped
Generated _templ.go files are deterministic output from .templ sources,
same as output.css from input.css. Remove them from version control to
reduce diff noise and merge conflicts. Add build:templ and live:templ
tasks to the Taskfile so generation happens as part of the build.
2026-03-02 15:27:38 -10:00
Ryan Hamamura
4f1ee11fa3 fix: add Enter key handlers to all auth and nickname inputs
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Pressing Enter on the username field in login/register or the nickname
field in the join-game prompt now submits the form, matching user
expectations. Also add *.templ to the gitignore allowlist.
2026-03-02 15:06:01 -10:00
Ryan Hamamura
587f392b8b fix: serve datastar locally and clean up session/route config
Replace CDN-hosted datastar beta.11 with local v1.0.0-RC.7 to fix
client-side expression incompatibilities with the Go SDK. Also fix
quoted CSS class keys in data-class expressions, harden session cookie
settings (named cookie, Secure flag), simplify SetupRoutes to not
return an error, and regenerate templ output.
2026-03-02 14:34:39 -10:00
Ryan Hamamura
5120eef776 refactor: streamline routes to RESTful naming conventions
All checks were successful
CI / Deploy / test (pull_request) Successful in 13s
CI / Deploy / lint (pull_request) Successful in 23s
CI / Deploy / deploy (pull_request) Has been skipped
Remove /api/ prefix and consolidate route groups:
- /api/lobby/* -> /games, /snake, /logout (top-level)
- /game/{game_id} + /api/game/{game_id}/* -> /games/{id}/*
- /snake/{game_id} + /api/snake/{game_id}/* -> /snake/{id}/*
- /api/auth/* -> /auth/*
- Standardize snake join page to use return_url= (was return=)
2026-03-02 13:19:03 -10:00
Ryan Hamamura
67d4dba37f fix: suppress gosec G117 on auth form signal structs
All checks were successful
CI / Deploy / test (pull_request) Successful in 7s
CI / Deploy / lint (pull_request) Successful in 47s
CI / Deploy / deploy (pull_request) Has been skipped
2026-03-02 12:40:02 -10:00
Ryan Hamamura
afd8a3e9d0 fix: resolve all linting errors and add SSE compression
Some checks failed
CI / Deploy / test (pull_request) Successful in 8s
CI / Deploy / lint (pull_request) Failing after 44s
CI / Deploy / deploy (pull_request) Has been skipped
- Add brotli compression (level 5) to long-lived SSE event streams
  (HandleGameEvents, HandleSnakeEvents) to reduce wire payload
- Fix all errcheck violations with nolint annotations for best-effort calls
- Fix goimports: separate stdlib, third-party, and local import groups
- Fix staticcheck: add package comments, use tagged switch
- Zero lint issues remaining
2026-03-02 12:38:21 -10:00
Ryan Hamamura
2aa026b1d5 refactor: remove persister abstraction layer
Some checks failed
CI / Deploy / test (pull_request) Successful in 8s
CI / Deploy / lint (pull_request) Failing after 46s
CI / Deploy / deploy (pull_request) Has been skipped
Inline persistence logic directly into game stores and handlers:
- game/persist.go: DB mapping methods on GameStore and GameInstance
- snake/persist.go: DB mapping methods on SnakeStore and SnakeGameInstance
- Chat persistence inlined into c4game handlers
- Delete db/persister.go (GamePersister, SnakePersister, ChatPersister)
- Stores now take *repository.Queries directly instead of Persister interface
2026-03-02 12:30:33 -10:00
Ryan Hamamura
8c3b3fc6ea refactor: replace via framework with chi + templ + datastar
Some checks failed
CI / Deploy / test (pull_request) Successful in 28s
CI / Deploy / lint (pull_request) Failing after 42s
CI / Deploy / deploy (pull_request) Has been skipped
Migrate from the via meta-framework to direct dependencies:
- chi for routing, templ for HTML templates, datastar for SSE/reactivity
- Feature-sliced architecture (features/{auth,lobby,c4game,snakegame}/)
- Shared layouts and components (features/common/)
- Handler factory pattern (HandleX(deps) http.HandlerFunc)
- Embedded NATS server (nats/), SCS sessions (sessions/), chi router wiring (router/)
- Move ChatMessage domain type from ui package to game package
- Remove old ui/ package (gomponents-based via/h views)
- Remove via dependency from go.mod entirely
2026-03-02 12:16:25 -10:00