fix: reliable timesheet saves in Docker (surface errors, 0.5h steps, node middleware) - #8
Merged
Merged
Conversation
…abase The auth middleware ran in Next's edge runtime, which does not expose runtime environment variables such as SUPABASE_URL. The session-refresh Supabase client therefore fell back to NEXT_PUBLIC_SUPABASE_URL (e.g. http://localhost:8001), which is unreachable from inside the container where Supabase is at kong:8000. Token refresh failed with repeated "AuthRetryableFetchError: fetch failed", and because refreshed cookies are only persisted by the middleware, sessions went stale after the 1h JWT expiry. With RLS enabled, all server-side reads then returned empty — dashboard totals stopped updating and timesheet entries appeared to vanish, even though writes (via the browser client) persisted fine. Running the middleware in the Node.js runtime lets it read SUPABASE_URL at request time, exactly like the server components already do, so the fix holds regardless of how the Supabase URL is configured.
handleSave awaited each Supabase insert/update/delete without inspecting the returned error, then unconditionally showed "Changes saved!". When a write was rejected — most commonly by the hours CHECK constraint (hours must be between 0 and 24) returning HTTP 400 — the failure was swallowed: the UI reported success, but nothing persisted. On reload the old value reappeared and dashboard totals never changed, making it look like saved hours had vanished. Now every write checks its error and throws on failure, so the catch block reports the real message. A pre-save validation also flags any out-of-range hours with a clear per-day hint before the request is sent, turning a silent 400 into actionable feedback.
The hours input used a 0.25 step, which is finer than needed. Change the step to 0.5 and enforce half-hour increments in the pre-save validation so a manually typed value like 0.25 is rejected with a clear hint rather than accepted (the step attribute alone is only a soft browser hint).
Replaces the earlier `runtime: "nodejs"` workaround with the idiomatic Next 16 convention. Context: on this codebase (Next 16.0.10 + Turbopack), the legacy middleware.ts convention still defaults to the *edge* runtime — verified via middleware-manifest.json referencing server/edge/chunks/*. In the edge runtime the internal SUPABASE_URL (http://kong:8000) is not readable, so session refresh in Docker fell back to the public URL and failed with repeated "AuthRetryableFetchError: fetch failed". The `runtime: "nodejs"` option fixed that but is a landmine: setting `runtime` in a proxy file throws, so the recommended middleware -> proxy codemod would break the build. Migrating to proxy.ts resolves it correctly: Proxy defaults to the Node.js runtime (verified: empty middleware-manifest, logic compiled into the node server bundle, no edge chunk references updateSession), so the internal SUPABASE_URL is read server-side as intended. It also removes the `runtime` landmine and silences the "middleware convention is deprecated" build warning.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes timesheet hour edits appearing to save but silently not persisting on the Docker/Supabase deployment, plus two related robustness/UX improvements.
1. Surface save errors instead of swallowing them (the reported bug)
handleSaveawaited each Supabase insert/update/delete without checking the returned error, then always showed "Changes saved!". When a write was rejected — most commonly thehoursCHECK constraint (0 <= hours <= 24) returning HTTP 400 — the failure was swallowed. The UI reported success, nothing persisted, and on reload the old value reappeared while dashboard totals never changed, so it looked like saved hours had vanished.Now every write checks its error and throws on failure so the real message is shown, and a pre-save validation flags out-of-range hours per day before the request is sent.
2. Restrict hours to 0.5 increments
The input used a
0.25step (too granular). Changed to0.5and enforced half-hour increments in the pre-save validation, since thestepattribute alone is only a soft browser hint.3. Migrate
middleware.ts→proxy.ts(Node.js runtime)Separate latent bug found while investigating: on this codebase (Next 16.0.10 + Turbopack) the legacy
middleware.tsconvention still defaults to the edge runtime — verified viamiddleware-manifest.jsonreferencingserver/edge/chunks/*. In the edge runtime the internalSUPABASE_URL(http://kong:8000) is not readable server-side, so session refresh in Docker fell back to the public URL and failed with repeatedAuthRetryableFetchError: fetch failed, breaking refresh after the 1h JWT expiry.Fixed idiomatically by migrating to the Next 16
proxy.tsconvention, which defaults to the Node.js runtime (noruntimeoption needed — setting one in a proxy file throws). This keeps session refresh on Node so the internal URL is read as intended, removes theruntimelandmine for the recommended middleware→proxy codemod, and silences themiddleware convention is deprecatedbuild warning.Testing
PATCHwithhours > 24→400 23514 violates check constraint time_entries_hours_check(matches the browser request seen in Kong access logs); valid values persist.appcontainer for each change; app responds 200.middleware-manifest.json, no edge chunk referencesupdateSession,/dashboard→307 /auth/login, login200, zerofetch failedafter restart.Manual check to confirm: enter an hours value over 24 and verify the hint appears instead of a false "saved".