Skip to content

PR2: auth + real upload + DB schema + RLS#6

Open
Regantih wants to merge 6 commits into
pr4/chat-and-rich-memofrom
pr7/auth-upload-db
Open

PR2: auth + real upload + DB schema + RLS#6
Regantih wants to merge 6 commits into
pr4/chat-and-rich-memofrom
pr7/auth-upload-db

Conversation

@Regantih

Copy link
Copy Markdown
Owner

PR2: Auth + Real Upload + DB Schema + RLS

Implements the full auth layer, Supabase client wiring, database schema with RLS, real file upload, and usage guardrails. No fake data. All error states surface real causes.

Base branch: pr4/chat-and-rich-memo
No files touched from PR#3 (pr6/path-b-and-swarm) or PR#4 (pr5/rendering-and-cost-transparency).
Shared file modified: docs/DECISIONS.md — D-025 through D-033 appended only.


What was created

1. Next.js scaffold

File Purpose
package.json Next.js 15 + @supabase/ssr + @supabase/supabase-js + React 19 deps
tsconfig.json Next.js App Router–compatible (bundler module resolution, JSX preserve)
tsconfig.lib.json Extends root; NodeNext module resolution for standalone lib/ + api/ compilation (PR#3 compatibility)
next.config.ts Minimal Next.js config; existing .html files unaffected
.env.example All env vars documented with correct NEXT_PUBLIC_ prefixes

2. Supabase client wiring

File What it does
lib/supabase/browser.ts Singleton createBrowserClient — uses NEXT_PUBLIC_SUPABASE_URL + NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY. Throws with clear message if vars absent.
lib/supabase/server.ts getSupabaseServerClient() — cookie-based session client (respects RLS). getSupabaseAdminClient() — service-role client (bypasses RLS; server-only). Guarded by import 'server-only'.
lib/supabase/types.ts Hand-authored DB types matching the migration schema. Replace with supabase gen types typescript after schema stabilises.

3. Real SupabaseStorageClient

File What it does
lib/storage.supabase.ts Full StorageClient implementation: putFile (uploads to deal-uploads bucket), getSignedUrl (time-limited signed URL), listDealFiles (newest-first). Standalone — inline types until PR#3 merges. Uses SUPABASE_SECRET_KEY.

4. SQL migrations

File Tables / policies created
supabase/migrations/20260510000001_init.sql pgcrypto extension; profiles, deals, deal_files, deal_runs, cost_ledger, usage_counters tables; set_updated_at trigger on deals
supabase/migrations/20260510000002_rls.sql RLS enabled on all 6 tables; 14 policies (select/insert/update/delete per table per ownership rule); handle_new_user auth trigger
supabase/migrations/20260510000003_storage.sql deal-uploads private bucket (50 MB limit, 13 allowed MIME types); Storage RLS: upload/select/delete gated on deals.owner_id = auth.uid()
supabase/config.toml Supabase CLI project config

5. Auth UI (Next.js App Router)

File What it does
app/layout.tsx Root layout — design tokens inline, no global CSS required
app/(auth)/layout.tsx Centred auth card + brand header (DealLens / Marketlogic Investors LLC)
app/(auth)/login/page.tsx Server Component — checks session, redirects if authenticated
app/(auth)/login/LoginForm.tsx Client Component — email magic link form + GitHub OAuth button; honest error banners; success state shows email address
app/(auth)/signup/page.tsx Server Component — redirects if authenticated
app/(auth)/signup/SignupForm.tsx Client Component — same flow as login (OTP creates user if absent); shows free-tier limits on the form
app/auth/callback/route.ts OAuth + magic-link callback handler; exchanges code → session; upserts profile row; redirects to /deals.html or ?next= param
middleware.ts Edge Middleware — refreshes session on every request; redirects unauthenticated users to /login?next=<path>; passes through public paths + static assets

6. API routes

File Endpoint What it does
app/api/upload/route.ts POST /api/upload Authenticates caller; verifies deal ownership; validates MIME + size (≤ 50 MB); uploads to deal-uploads via service-role; inserts deal_files row; returns file metadata
app/api/usage/route.ts GET /api/usage Returns plan, is_owner, monthly deals_processed + usd_spent, computed limits (null for owners = unlimited), within_limits boolean

7. Documentation

File Contents
ENV.md All env vars, NEXT_PUBLIC_ mapping, Vercel setup guide
SETUP.md Clone → install → configure → migrate → run → deploy → GitHub OAuth → set owner flag → type-check → gen types
docs/DECISIONS.md D-025 through D-033 appended (Next.js App Router, Supabase Auth strategy, NEXT_PUBLIC_ naming, is_owner bypass, free tier, migration timestamps, tsconfig.lib.json, auth trigger + upsert, private bucket)

Schema at a glance

profiles (id → auth.users, email, plan, is_owner, created_at)
  └─ deals (id, owner_id → profiles, name, stage, status, created_at, updated_at)
       ├─ deal_files (id, deal_id, storage_path, mime, size_bytes, source, created_at)
       └─ deal_runs  (id, deal_id, status, started_at, finished_at, total_usd, tokens)
            └─ cost_ledger (id, run_id, agent, model, tokens_in, tokens_out, usd_cost, duration_ms)
usage_counters (profile_id → profiles, month, deals_processed, usd_spent)

RLS: every table locked down. Service-role (SUPABASE_SECRET_KEY) bypasses RLS for server-only operations. Auth trigger auto-creates profiles row on first sign-in.


Acceptance checklist

  • lib/supabase/browser.tsgetSupabaseBrowserClient() returns singleton, throws if env vars absent
  • lib/supabase/server.tsgetSupabaseServerClient() (RLS) + getSupabaseAdminClient() (bypass); guarded by server-only
  • lib/supabase/types.ts — typed Database interface matching migration schema
  • lib/storage.supabase.tsSupabaseStorageClient implementing StorageClient interface; putFile, getSignedUrl, listDealFiles all functional; server-only
  • supabase/migrations/20260510000001_init.sql — all 6 tables, pgcrypto, set_updated_at trigger
  • supabase/migrations/20260510000002_rls.sql — RLS on all tables, 14 policies, auth trigger
  • supabase/migrations/20260510000003_storage.sql — private bucket, 3 storage policies
  • app/(auth)/login/page.tsx — server auth check + redirect
  • app/(auth)/login/LoginForm.tsx — magic link form + GitHub OAuth button + honest error/success states
  • app/(auth)/signup/page.tsx + SignupForm.tsx — same flow, shows free-tier limits
  • app/auth/callback/route.ts — code exchange, session cookie, profile upsert, redirect
  • middleware.ts — session refresh, unauthenticated redirect, static-asset passthrough
  • POST /api/upload — auth check, deal ownership, MIME/size validation, storage upload, deal_files insert, real errors
  • GET /api/usage — plan + is_owner, monthly counters, within_limits, null limits for owners
  • ENV.md — all vars documented with NEXT_PUBLIC_ mapping and Vercel instructions
  • SETUP.mdnpx supabase link + npx supabase db push migration guide
  • No real secret values in any file, comment, or commit message
  • No fake data shown to end users
  • SUPABASE_SECRET_KEY only referenced in lib/supabase/server.ts and lib/storage.supabase.ts (both server-only)
  • All RLS policies present from day one (not deferred)
  • docs/DECISIONS.md D-025–D-033 appended; no existing rows modified

Step-by-step deployment guide

Prerequisites

  • Node.js ≥ 20, npm ≥ 10
  • Supabase CLI: npm install -g supabase
  • Supabase project kipyuhjbtkyhfapinwgj (already provisioned)

Local setup

git checkout pr7/auth-upload-db
npm install
cp .env.example .env.local
# Fill in NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY,
# SUPABASE_URL, SUPABASE_SECRET_KEY from Supabase Dashboard → Settings → API

Apply migrations

npx supabase link --project-ref kipyuhjbtkyhfapinwgj
# Enter your Supabase DB password when prompted
npx supabase db push

This applies all three migration files in timestamp order.

Run locally

npm run dev
# Visit http://localhost:3000/login

Deploy to Vercel

  1. Import repo in Vercel → set Framework = Next.js, Root = .
  2. Add env vars in Vercel → Settings → Environment Variables:
    • NEXT_PUBLIC_SUPABASE_URL (all environments)
    • NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY (all environments)
    • SUPABASE_URL (all environments)
    • SUPABASE_SECRET_KEY (server only — Production + Preview, NO NEXT_PUBLIC_ prefix)
  3. In Supabase Dashboard → Auth → URL Configuration add:
    • Site URL: https://your-app.vercel.app
    • Redirect URL: https://your-app.vercel.app/auth/callback
  4. Deploy.

Set owner flag (one-time)

-- In Supabase Dashboard → SQL Editor
UPDATE public.profiles
SET is_owner = true
WHERE email = 'operator@marketlogicinvestors.com';

Enable GitHub OAuth (optional)

  1. Create a GitHub OAuth App → Callback URL: https://kipyuhjbtkyhfapinwgj.supabase.co/auth/v1/callback
  2. In Supabase Dashboard → Auth → Providers → GitHub: enable + paste credentials.

Merge order recommendation

This PR is independent of PR#3 and PR#4 and can be merged in any order. After merging all three, replace the inline type/interface declarations in lib/storage.supabase.ts with imports from lib/storage.ts (one-line change).
"

Copy link
Copy Markdown
Owner Author

⚠️ Superseded by PR #7

This PR (PR2: auth + real upload + DB schema + RLS, branch pr7/auth-upload-db) has been superseded by PR #7 (pr7/real-wire).

What happened

PR #7 was directly branched from this PR's head commit (pr7/auth-upload-db @ 9cd6e96). All of this PR's content is therefore already included in PR #7, plus the following additions:

This PR's content Status in PR #7
Next.js scaffold (package.json, tsconfig.json, next.config.ts) ✅ Included + @anthropic-ai/sdk@0.95.1 added
lib/supabase/browser.ts, server.ts, types.ts ✅ Included + types.ts updated with jobs table
lib/storage.supabase.ts (SupabaseStorageClient) ✅ Included + updated to import from lib/storage.ts
Auth UI pages (login, signup, callback) ✅ Included unchanged
middleware.ts ✅ Included unchanged
POST /api/upload, GET /api/usage ✅ Included unchanged
3 SQL migrations (init, RLS, storage) ✅ Included + 4th migration added (jobs table)
ENV.md, SETUP.md ✅ Included

PR #7 adds on top: real Anthropic LLM router, Supabase-backed jobs queue, ingest-link route, jobs worker, health endpoint.

Recommended action

This PR will be auto-closed when PR #7 merges to main. The merge order is: PR #8 (docs) first, then PR #7 (everything).

If you wish to close this PR now, use "Close pull request" below.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant