feat: Supabase auth, legal pages, and backend env fix - #2
Merged
afrinxnahar merged 5 commits intoAug 23, 2026
Merged
Conversation
Adds sign up, login, forgot password and reset password pages backed by Supabase, plus a route guard over the dashboard. Sessions are cookie-based via @supabase/ssr. The middleware refreshes them on every request and uses getClaims(), which verifies the JWT signature — getSession() is not safe for authorization on the server. The dashboard layout re-checks server-side so the guard sits next to the data it protects rather than relying on routing alone. When Supabase credentials are absent the guard fails closed: protected routes redirect, the marketing site keeps working. Credentials are validated with zod in the server actions, so bypassing the browser form still hits the same checks. The password reset and sign up flows return identical responses whether or not an account exists, to avoid leaking which emails are registered. Redirect targets are restricted to same-origin paths so ?next= cannot be used as an open redirect. One callback route handles all three link styles Supabase can send — OAuth and email confirmation via ?code=, and recovery via ?token_hash= — instead of separate confirm and callback routes. Pages follow the existing editorial styling: zero radius, accent orange, Bebas headings and mono uppercase labels.
Both documents describe how Anode actually works — Supabase, Gemini, Hugging Face embeddings, Vercel analytics, the setup-fee pricing model and Google sign-in — rather than generic boilerplate. Copy lives as data in lib/legal-content.ts, matching the existing anode-data.ts pattern, and both pages share one renderer. Adding a clause is a data edit and the two documents cannot drift apart in styling. Layout reuses the site's numbered-section idiom: accent eyebrow, Bebas heading, zero-padded numbering in the left gutter and mono body copy. The text is a starting template and has not been reviewed by a lawyer; there is a note to that effect at the top of the content file.
Turns the hero's secondary CTA from "Try The Demo" into "Get Started" pointing at /signup, keeping the scramble-on-hover treatment. Adds an Account column to the footer for sign in and create account, and puts the privacy policy and terms links in the bottom bar beside the copyright. The footer grid goes to five columns on desktop and still pairs cleanly at two columns on mobile.
The backend crashed on boot with "Drizzle database connection instance is not initialized yet!" because @anode/supabase reads DATABASE_URL when it is first required, and the env was never loaded. main.ts resolved .env from process.cwd(), which is apps/backend under pnpm --filter backend dev. Everything else in the repo expects it at the monorepo root: drizzle.config.ts uses '../../.env', ConfigModule uses __dirname/../../../.env, and both READMEs say project root. Resolving from __dirname lines main.ts up with the rest and works from src and dist alike. Also adds the root .env.example the README already tells you to copy, and reworks the DI error so it names DATABASE_URL instead of implying a lifecycle race.
Running the e2e suite drops test-results/ and playwright-report/ into the repo root. Neither belongs in version control.
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.
What
Three pieces of work, each in its own commit.
Supabase auth — sign up, login, forgot password and reset password, with a guard over the dashboard. Email/password and Google OAuth.
Privacy Policy and Terms of Service at
/privacyand/terms, linked from the footer.Backend boot fix — the API crashed on startup because it looked for
.envin the wrong directory.Auth notes
Sessions are cookie-based via
@supabase/ssr. The middleware refreshes them on every request and authorizes withgetClaims(), which verifies the JWT signature —getSession()is not safe for this on the server. The dashboard layout re-checks server-side so the guard sits next to the data it protects rather than relying on routing alone.Security choices worth reviewing:
required/typeoff the form and submitting garbage — the server still rejected it.?next=is restricted to same-origin paths, so it cannot be turned into an open redirect. Covered bye2e/auth-guard.spec.ts.A single callback route handles all three link styles Supabase can send (OAuth and email confirmation via
?code=, recovery via?token_hash=) rather than separate confirm and callback routes.Backend fix
The API died on boot with
Drizzle database connection instance is not initialized yet!.@anode/supabasereadsDATABASE_URLwhen it is first required, and the env was never loaded:main.tsresolved.envfromprocess.cwd(), which isapps/backendunderpnpm --filter backend dev. Everything else in the repo expects it at the monorepo root —drizzle.config.tsuses'../../.env',ConfigModuleuses__dirname/../../../.env, and both READMEs say project root.Fixed on the cheaper side by resolving from
__dirnameinmain.ts, which lines it up with the rest and works fromsrcanddistalike. Also adds the root.env.examplethe README already tells you to copy, and reworks the DI error so it namesDATABASE_URLinstead of implying a lifecycle race.Verified
/dashboardand/reset-passwordredirect to/loginwhen signed out; static assets and the public widget API still bypass the guarde2e/auth-guard.spec.tstests pass.env, and gives the new actionable error without oneoklch(0.7 0.2 45),0pxradius, Bebas headings, mono uppercase labelsReviewer notes
lib/legal-content.ts.NEXT_PUBLIC_SUPABASE_URLandNEXT_PUBLIC_SUPABASE_ANON_KEYare set, and Google sign-in additionally needs the provider enabled in Supabase with/auth/callbackallow-listed there and in the Google Cloud OAuth client.apps/frontend/lib/supabase.tsis dead code — zero importers, and it does not typecheck (@anode/supabaseis not a frontend dependency). Left alone deliberately; happy to delete it in a follow-up.#modes), so they do nothing from/privacyand/terms. That is why the legal layout carries its own back-to-site link instead of reusingAnodeFooter. Changing them to/#modeswould fix it globally.