diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..80019c6 --- /dev/null +++ b/.env.example @@ -0,0 +1,82 @@ +# ============================================================================ +# Route Commerce — Environment variables +# ============================================================================ +# Copy to `.env.local` and fill in real values for local development. +# Production: set these in your hosting dashboard (Vercel / Netlify / etc.). +# ============================================================================ + +# ── App ───────────────────────────────────────────────────────────────────── +NEXT_PUBLIC_BASE_URL=http://localhost:4000 +NEXT_PUBLIC_SITE_URL=http://localhost:4000 + +# ── Database (Postgres, direct — Supabase is being removed) ──────────────── +# Single connection string used by `pg.Pool` in src/lib/auth.ts and the +# admin-permissions / data-service layer. Format: +# postgresql://USER:PASS@HOST:PORT/DBNAME?sslmode=require +DATABASE_URL=postgresql://postgres:postgres@localhost:5432/route_commerce + +# ── Auth.js (NextAuth v5) ─────────────────────────────────────────────────── +# Generate with: npx auth secret +# Or: openssl rand -base64 32 +AUTH_SECRET=replace-me-with-a-32-byte-base64-string + +# Base URL used to build OAuth callback URLs. In dev: +AUTH_URL=http://localhost:4000 +# In production, set to https://yourdomain.com + +# Google OAuth provider. +# 1. Go to https://console.cloud.google.com/apis/credentials +# 2. Create an OAuth 2.0 Client ID (type: Web application) +# 3. Add Authorized redirect URI: http://localhost:3000/api/auth/callback/google +# 4. Copy client id + client secret below +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= + +# Auth.js also reads AUTH_GOOGLE_ID / AUTH_GOOGLE_SECRET if you prefer the +# default NextAuth variable names. The code in src/auth.config.ts falls +# back to those names if GOOGLE_CLIENT_ID is unset. + +# Set to "false" to disable the in-app dev credentials provider even in +# development. Default: enabled in dev only. +ALLOW_DEV_LOGIN=true + +# ── Supabase (legacy, being removed) ──────────────────────────────────────── +# Still used by the existing admin pages, server actions, and the +# `getAdminUser` flow. Once the auth migration is complete and the +# @supabase/* packages are removed, these can go away. +NEXT_PUBLIC_SUPABASE_URL= +NEXT_PUBLIC_SUPABASE_ANON_KEY= +SUPABASE_SERVICE_ROLE_KEY= + +# ── Stripe ───────────────────────────────────────────────────────────────── +STRIPE_SECRET_KEY= +STRIPE_WEBHOOK_SECRET= +NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY= +STRIPE_PRICE_STARTER= +STRIPE_PRICE_FARM= +STRIPE_PRICE_ENTERPRISE= +STRIPE_PRICE_HARVEST_REACH= +STRIPE_PRICE_WHOLESALE_PORTAL= +STRIPE_PRICE_WATER_LOG= +STRIPE_PRICE_AI_TOOLS= +STRIPE_PRICE_SQUARE_SYNC= +STRIPE_PRICE_SMS_CAMPAIGNS= + +# ── Resend (transactional email) ──────────────────────────────────────────── +RESEND_API_KEY= +RESEND_WEBHOOK_SECRET= + +# ── AI providers ──────────────────────────────────────────────────────────── +OPENAI_API_KEY= +ANTHROPIC_API_KEY= +GOOGLE_API_KEY= +XAI_API_KEY= +MINIMAX_API_KEY= +MINIMAX_BASE_URL=https://api.minimax.io/v1 + +# ── Square (optional) ─────────────────────────────────────────────────────── +SQUARE_APP_SECRET= +SQUARE_ENVIRONMENT=sandbox + +# ── Cron / automation ─────────────────────────────────────────────────────── +CRON_SECRET=replace-me-with-a-random-string diff --git a/CLAUDE.md b/CLAUDE.md index f4e9f3e..69ba6b7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Route Commerce is a multi-tenant B2B e-commerce platform for fresh produce wholesale distribution. Brands sell to customers who pick up at scheduled stops or receive shipments. The platform includes admin dashboards for order management, stop/route scheduling, product catalogs, payment processing (Stripe + Square), and a communications module ("Harvest Reach") for email/SMS campaigns. -Tech stack: Next.js 16 (App Router) · Supabase (auth + Postgres + RLS) · Stripe · Square · Resend (email) · Tailwind CSS v4 +Tech stack: Next.js 16 (App Router) · **Postgres** (direct — Supabase is being removed) · Auth.js (NextAuth v5, in-progress migration from bespoke cookie auth) · Stripe · Square · Resend (email) · Tailwind CSS v4 + +> **Direction:** Supabase is being removed in favor of a direct Postgres connection. The `supabase/` directory is kept as a path for migrations tooling only (no Supabase platform/CLI/auth). Until the Auth.js migration ships, auth still flows through the `dev_session` / `rc_auth_uid` cookies — see the Authentication section. New DB code should connect to Postgres directly (via `pg` or the chosen driver — see Database section) and **must not** import from `@supabase/*` or call Supabase REST. --- @@ -21,14 +23,12 @@ npx tsc --noEmit # TypeScript check (no emit) npx playwright test # Run E2E tests (Playwright) ``` -> The migrate script auto-detects Supabase CLI first, then falls back to direct PostgreSQL. -> For CLI mode: `brew install supabase/tap/supabase` then `supabase link --project-ref wnzkhezyhnfzhkhiflrp` -> For direct PG mode: `pg` and `dotenv` are already in devDependencies. -> If `get_brand_settings` migration fails with "cannot change return type", the function signature changed — drop and recreate it first. +> The migrate script (`supabase/push-migrations.js`) now only uses the direct `pg` path — the Supabase CLI branch is legacy. It reads `DATABASE_URL` from `.env.local` via `dotenv`. `pg` is already in devDependencies. +> If a migration fails with "cannot change return type", the function signature changed — drop and recreate it first. **Recent migration work is documented in `MEMORY.md`** (Supabase login + link process, updates to `push-migrations.js` for modern CLI, specific SQL patches made to 091/145/148/200/201 so they would apply cleanly, and which migrations were pushed in the session). Cat `MEMORY.md` for details. -No test suite currently exists. E2E tests use Playwright (`tests/` or `test-e2e.ts`). +E2E tests live in `tests/` and run via Playwright. Specs include `tests/smoke.spec.ts` and `tests/login/login-flow.spec.ts`. **Note: `playwright.config.ts` defaults `baseURL` to production** (`https://route-commerce-platform.vercel.app`); override with `PLAYWRIGHT_URL=http://localhost:3000` for local runs, or pass `--config` with a local config. --- @@ -41,10 +41,24 @@ No test suite currently exists. E2E tests use Playwright (`tests/` or `test-e2e. - `dev_session=brand_admin` — full access to assigned brand only - `dev_session=store_employee` — limited access (orders, pickup, wholesale only) -`src/lib/admin-permissions.ts` is the single source of truth for the current admin user. It uses a `dev_session` cookie in development and Supabase Auth in production. **Never import this file directly into Client Components** — use the `getCurrentAdminUser` server action from `@/actions/admin-user` instead. +`src/lib/admin-permissions.ts` is the single source of truth for the current admin user. It uses a `dev_session` cookie in development and the legacy `rc_auth_uid` cookie in production (set by the pre-Auth.js `/api/login`) — never import this file directly into Client Components. Use the `getCurrentAdminUser` server action from `@/actions/admin-user` instead. + +The middleware (`src/middleware.ts`) guards `/admin/:path*` and `/login`. It auto-issues a `dev_session=platform_admin` cookie for the demo flow when no auth is present. A `clerk-auth.ts` helper exists in `src/lib/` but is currently a stub — do not depend on it. The `AdminUser` type lives in `src/lib/admin-permissions-types.ts` and is shared across server/client boundary. +#### Auth.js (NextAuth v5) migration — in progress + +The platform is migrating from the bespoke `dev_session` + `rc_auth_uid` cookie flow to **Auth.js (NextAuth v5)**, with Supabase as the database adapter and email/OAuth providers. While the migration is in flight: + +- Do **not** add new code that depends on the `dev_session` or `rc_auth_uid` cookies — write against the Auth.js API (`auth()`, `signIn`, `signOut`, `getSession`) instead. +- New env vars: `AUTH_SECRET`, `AUTH_URL`, and provider-specific keys (`AUTH_GITHUB_ID`/`SECRET`, `AUTH_GOOGLE_ID`/`SECRET`, etc.). See `.env.example` for the full list. +- A new route handler at `src/app/api/auth/[...nextauth]/route.ts` will replace the ad-hoc `/api/login`, `/api/auth/uid`, and `/api/logout` endpoints. +- The middleware (`src/middleware.ts`) will eventually use `auth()` from NextAuth to populate the session; the existing `dev_session` auto-login branch is a temporary fallback for demos. +- `src/lib/admin-permissions.ts` will keep its public surface (`getAdminUser`, `getCurrentAdminUser`) but read the session from NextAuth internally — the `AdminUser` type does not need to change. +- `clerk-auth.ts` is being removed in favor of Auth.js; do not extend it. +- Until the migration ships, the `dev_session` and `rc_auth_uid` paths remain the source of truth — see the section above for current behavior. + ### Server Actions Pattern All database writes go through server actions in `src/actions/`. These: @@ -55,9 +69,19 @@ All database writes go through server actions in `src/actions/`. These: Server actions are "use server" files that export async functions. Client components import and call them directly. -### SECURITY DEFINER RPCs + Brand Scoping +### Database (Postgres, direct) -The app uses **PostgreSQL SECURITY DEFINER functions** for all data access. These run with the function owner's privileges and bypass RLS entirely. This means: +The app connects to **Postgres directly** — no Supabase platform, JS client, or REST gateway. Server actions use the `pg` driver (or whatever the chosen connection layer is) to call `SECURITY DEFINER` PL/pgSQL functions. Storage of files (product images, etc.) is moving to an S3-compatible object store; until that's wired up, image references can stay as URLs. + +#### Connection + +- `DATABASE_URL` in `.env.local` (and hosting dashboard) is the only required DB env var. +- A single shared `pg` `Pool` is exported from `src/lib/db.ts` (TBD — to be created/confirmed during the migration). Server actions and API routes import it and call `pool.query(...)` against RPC names. +- No `NEXT_PUBLIC_SUPABASE_URL` / `SUPABASE_SERVICE_ROLE_KEY` / `@supabase/*` imports — these are being purged from the codebase. + +#### SECURITY DEFINER RPCs + Brand Scoping + +The app uses **PostgreSQL SECURITY DEFINER functions** for all data access. These run with the function owner's privileges and bypass any future RLS. This means: - Brand isolation must be enforced at the **application layer** (in server actions), not in database policies - Every RPC that touches brand-scoped data accepts a `p_brand_id UUID` parameter and filters by it @@ -182,10 +206,19 @@ For annual pricing, create separate annual prices in Stripe (e.g., $441/yr for S ### Communications Module ("Harvest Reach") -The communications system (`/admin/communications`) uses a separate set of tables that are **NOT protected by RLS** — they rely entirely on the SECURITY DEFINER RPCs + application-layer brand scoping. Key tables: `communication_campaigns`, `communication_templates`, `communication_contacts`, `communication_message_logs`. +The communications system (`/admin/communications`) uses a separate set of tables that are **NOT protected by RLS** — they rely entirely on the SECURITY DEFINER RPCs + application-layer brand scoping. Key tables: `communication_campaigns`, `communication_templates`, `communication_contacts`, `communication_message_logs`. (The "no RLS" framing carries over from the Supabase era; on raw Postgres this just means no row-level policies — scoping is still enforced by RPC + app layer.) `send_campaign` / `send_stop_blast` RPCs insert into `communication_message_logs` but do NOT populate `event_id`. The Resend webhook (`src/app/api/resend/webhook/route.ts`) must therefore look up logs by `customer_email + subject + created_at` (7-day window), not by `event_id`. +**Scheduled automations** (declared in `vercel.json`): +- `POST /api/email-automation/abandoned-cart` — every 6h, fires abandoned-cart sequence emails +- `POST /api/email-automation/welcome-sequence` — every 6h, fires welcome onboarding sequence +- `POST /api/cron/send-scheduled` — daily 09:00, sends scheduled campaigns +- `POST /api/wholesale/notifications/{send,dispatch,pickup-reminder}` — wholesale lifecycle +- `POST /api/square/process-queue` — every 2 min, drains Square sync queue + +These endpoints are also reachable via curl for manual triggering; the email-automation routes accept `Authorization: Bearer $CRON_SECRET`. + ### Payments - **Stripe** — primary payment processor; `src/actions/payments.ts` and `src/app/api/stripe/` handle checkout, webhooks, refunds @@ -200,7 +233,7 @@ Separate from orders/stops — tracks irrigation/water usage per brand. `src/act ## Key Conventions -- All DB mutations use Supabase REST API (`fetch` to `${supabaseUrl}/rest/v1/rpc/...`) from server actions, NOT the Supabase JS client (avoids SSR cookie issues) +- All DB access goes through a shared `pg` `Pool` (see Database section). Server actions call SECURITY DEFINER RPCs via `pool.query('SELECT * FROM fn_name($1, $2)', [...])`. Do not introduce `@supabase/*` imports or REST fetch to `*/rest/v1/`. - `gen_random_uuid()` used in migrations for primary keys - Migrations use `CREATE OR REPLACE FUNCTION` for idempotency — never `DROP` then `CREATE` - Status enums stored as TEXT — no PostgreSQL ENUM type @@ -217,11 +250,11 @@ Separate from orders/stops — tracks irrigation/water usage per brand. `src/act |---|---| | Admin auth + permissions | `src/lib/admin-permissions.ts`, `src/lib/admin-permissions-types.ts` | | Middleware (route protection) | `src/middleware.ts` | -| Server actions | `src/actions/*.ts` (one file per domain) | +| Server actions | `src/actions/*.ts` (one file per domain; also grouped into `src/actions/{admin,ai,billing,communications,harvest-reach,integrations,orders,products,settings,shipping,stops,water-log,platform,route-trace,time-tracking,email-automation}/`) | | Admin pages | `src/app/admin/[module]/page.tsx` | | Admin client components | `src/components/admin/*.tsx` | -| Migrations | `supabase/migrations/` | -| Supabase client | `src/lib/supabase.ts` | +| Migrations | `supabase/migrations/` (kept for now; will likely move to `db/migrations/` in a later pass) | +| Postgres pool / driver | `src/lib/db.ts` (TBD — create during the Supabase removal pass) | | Email templates | `src/lib/email-templates.ts` | | Date formatting | `src/lib/format-date.ts` | | Feature flags | `src/lib/feature-flags.ts` | @@ -238,7 +271,8 @@ Separate from orders/stops — tracks irrigation/water usage per brand. `src/act ## Gotchas - **Dev mode `brand_id: null`**: `getAdminUser()` returns `brand_id: null` for platform_admin dev sessions. Always pass explicit `brandId` to server action functions that accept it — don't rely on `adminUser.brand_id` alone. -- **Communications = no RLS**: The communications tables (campaigns, templates, contacts, message_logs) have RLS disabled. All brand scoping must be enforced in server actions. +- **Communications = no RLS**: The communications tables (campaigns, templates, contacts, message_logs) have no row-level policies. All brand scoping must be enforced in server actions. +- **Supabase residue in the wild**: `grep -r "@supabase" src/` will still find imports during the transition. Do not add new ones; if you're touching a file that imports from Supabase, replace the call with the equivalent `pg`-pool call before merging. - **Webhook event_id**: `log_communication_messages` never populates `event_id`, so the Resend webhook uses `customer_email + subject` lookup instead. - **Mixed fulfillment orders**: An order can have both pickup and ship items. `get_shipping_orders` RPC returns orders with at least one `fulfillment = 'ship'` item. - **SMS opt-in defaults**: `communication_contacts.sms_opt_in` defaults to `FALSE` (opt-out by default). `email_opt_in` defaults to `TRUE`. Always check `sms_opt_in` specifically for SMS sends, not `email_opt_in`. \ No newline at end of file diff --git a/MEMORY.md b/MEMORY.md index 96d35ff..917ecd2 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -2,11 +2,40 @@ This file captures key context, decisions, fixes, and state from recent work so it survives across conversations. -**Last updated:** 2026-06-03 (during Supabase migration apply session) +**Last updated:** 2026-06-06 (Supabase → Postgres pivot) --- -## Supabase CLI + Migrations Tooling +## 🚨 Direction Pivot (2026-06-06) — Supabase → Postgres + +The platform is moving off Supabase entirely. We are connecting to **Postgres directly** (via `pg`), with **Auth.js (NextAuth v5)** handling authentication. See `CLAUDE.md` for the full updated architecture. + +### What changes immediately +- **DB connection**: `DATABASE_URL` is the only required DB env var. No more `NEXT_PUBLIC_SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, or `SUPABASE_ANON_KEY`. +- **Migrations**: only the `pg` direct path in `supabase/push-migrations.js` is supported going forward. The Supabase CLI branch is dead code. The script reads `DATABASE_URL` from `.env.local` via `dotenv`. +- **Code**: no new `@supabase/*` imports, no `rest/v1/` REST fetch, no Supabase JS client usage. Use a shared `pg` `Pool` (target location: `src/lib/db.ts`, **TBD — create during the cutover**). +- **Auth**: legacy `rc_auth_uid` cookie + bespoke `/api/login` is being replaced by Auth.js. Until the Auth.js migration ships, the `dev_session` cookie remains the source of truth. +- **Storage**: Supabase Storage (e.g. the `product-images` bucket created in migration 145) is going away. Need an S3-compatible alternative — **TBD**. + +### What's TBD / needs follow-up +- [ ] `DATABASE_URL` for local dev (Neon? local Postgres? — user hasn't specified the Postgres host yet) +- [ ] New connection layer: raw `pg` Pool vs Drizzle vs Prisma vs Neon serverless driver — **not decided** +- [ ] Auth.js migration actually landing (currently "in progress" per CLAUDE.md) +- [ ] Where do product images, brand logos, etc. live now? S3? Cloudflare R2? Re-encode as URL strings? +- [ ] Whether the Supabase project (`wnzkhezyhnfzhkhiflrp`) gets shut down or kept read-only for the transition +- [ ] Cutover sequencing: do we delete `@supabase/*` from `package.json` in one PR or incrementally? + +### Migration content that's now obsolete +- **145 (product-images bucket)**: Supabase Storage bucket + RLS policies. Replaced by object store of choice. +- **Any RLS policy on tables** (200 added several): the "no RLS, app-layer scoping" model still holds but the policies are inert in the new world. +- The `supabase link --project-ref wnzkhezyhnfzhkhiflrp` setup is no longer needed for ongoing work. + +### Historical sections below +The "Supabase CLI + Migrations Tooling" section that used to live at the top of this file describes the *previous* tooling. It is kept below as **historical record** of work that was already applied to the Supabase project. Do **not** follow its CLI instructions — use the `pg` direct path instead. Migration-file patch notes (091, 145, 148, 200, 201) are also kept as historical record of what got applied. + +--- + +## Supabase CLI + Migrations Tooling *(SUPERSEDED — see Direction Pivot above)* ### Login + Link (done in this session) - User ran `supabase login` @@ -35,15 +64,23 @@ Key changes: - Falls back to direct `pg` only if CLI path fails. - Header comments updated with current recommended workflow. -**Recommended commands now:** +**Recommended commands now (Supabase CLI path — being phased out, use `pg` direct path going forward):** ```bash +# Supabase CLI path (legacy — do not use going forward) supabase login supabase link --project-ref wnzkhezyhnfzhkhiflrp -node supabase/push-migrations.js 148 # or any prefix +node supabase/push-migrations.js 148 # CLI path # or npm run migrate:one 148 ``` +```bash +# Direct pg path (this is the future — only the pg branch is kept alive in the script) +DATABASE_URL=postgres://... node supabase/push-migrations.js 148 +# or +DATABASE_URL=postgres://... npm run migrate:one 148 +``` + `npm run migrate` (no arg) will push every `*.sql` in order (use with caution). --- @@ -133,19 +170,23 @@ Verification queries (post-apply) confirmed: --- -## Current State / Gotchas +## Current State / Gotchas (2026-06-06) -- `supabase migration list` will show a lot of "pending" because the project's custom numeric migrations (00x_*.sql) were historically applied via the raw-SQL push script, not registered in Supabase's `schema_migrations` tracking table. Only early ones (001/002) + many timestamped migrations from other activity are tracked. -- Many migrations are intentionally written to be re-runnable. Re-pushing a prefix is the supported workflow for this project. -- When adding **new** migrations in the future, prefer the standard `supabase migration new` flow if possible, but the custom `push-migrations.js` + numeric prefix style is still the established pattern here. -- Storage policies, RLS, SECURITY DEFINER functions, and brand-scoped data are all over the place — test carefully after big applies. +- The Supabase CLI is no longer the recommended path. Use `DATABASE_URL` + `pg` directly. The `supabase/` directory is kept as a path for migrations tooling only. +- The Postgres host/URL for local dev is **TBD** (not yet decided by the user). Until it's set, `npm run migrate` will fail at the `pg` connect step. (The Supabase project at `wnzkhezyhnfzhkhiflrp` may still exist as a fallback read-only target — unconfirmed.) +- `supabase migration list` will show a lot of "pending" because the project's custom numeric migrations (00x_*.sql) were historically applied via the raw-SQL push script, not registered in Supabase's `schema_migrations` tracking table. This is mostly irrelevant now that we're moving off Supabase. +- Many migrations are intentionally written to be re-runnable. Re-pushing a prefix is the supported workflow for this project — this still holds under direct `pg`. +- When adding **new** migrations, use the established `supabase/push-migrations.js` + numeric prefix style (`NNN_descriptive_name.sql`). Do not introduce `supabase migration new` — that flow is going away with the CLI branch. +- Storage policies (145), RLS policies (200), SECURITY DEFINER functions, and brand-scoped data are still in Postgres — test carefully after big applies. Brand scoping still relies on `p_brand_id` parameters in RPCs. - CLAUDE.md already documents the overall migrate story and the `get_brand_settings` gotcha. +- **Open question for next session:** confirm Postgres host + connection layer (raw `pg` vs Drizzle/Prisma) and start the actual cutover (drop `@supabase/*` deps, create `src/lib/db.ts`, replace cookie auth with Auth.js). --- ## How to Use This Memory - Cat this file at the start of future sessions if context is needed: `cat MEMORY.md` +- **Read the Direction Pivot section first** — it supersedes the older Supabase-flavored instructions. - Update this file with new key facts, applied migrations, or new gotchas. - Feel free to add dated sections. diff --git a/middleware.ts b/middleware.ts deleted file mode 100644 index 9b2287e..0000000 --- a/middleware.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { NextResponse, type NextRequest } from "next/server"; - -const DEV_UID = "dev-user-00000000-0000-0000-0000-000000000000"; - -export async function middleware(request: NextRequest) { - const response = NextResponse.next({ request }); - - // ── Dev session bypass (enabled in all envs for demo) ────────────── - // Allow dev cookies via: document.cookie = "dev_session=platform_admin; path=/" - const devSession = request.cookies.get("dev_session")?.value; - const isDevMode = devSession === "platform_admin" || devSession === "brand_admin" || devSession === "store_employee"; - const rcAuthUid = request.cookies.get("rc_auth_uid")?.value; - - let authUid: string | null = null; - - if (isDevMode) { - // Dev session only valid in development - authUid = DEV_UID; - } else if (rcAuthUid) { - // rc_auth_uid is set by /api/login — treat as authenticated - authUid = rcAuthUid; - } - // No rc_auth_uid in production → authUid stays null → redirect to /login - - const isAdmin = request.nextUrl.pathname.startsWith("/admin"); - const isLogin = request.nextUrl.pathname === "/login"; - - if (isAdmin && !authUid) { - const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; - // Auto-login for demo: no Supabase configured, no auth cookie present - if (!supabaseUrl || !supabaseUrl.includes("supabase.co")) { - const url = request.nextUrl.clone(); - url.pathname = "/admin"; - url.searchParams.set("demo", "1"); - const response = NextResponse.redirect(url); - response.cookies.set("dev_session", "platform_admin", { - path: "/", - maxAge: 60 * 60 * 24, - httpOnly: true, - sameSite: "strict", - }); - return response; - } - const url = request.nextUrl.clone(); - url.pathname = "/login"; - return NextResponse.redirect(url); - } - - if (isLogin && authUid) { - const url = request.nextUrl.clone(); - url.pathname = "/admin"; - return NextResponse.redirect(url); - } - - return response; -} - -export const config = { - matcher: [ - "/admin/:path*", - "/admin", - "/login", - ], -}; \ No newline at end of file diff --git a/package.json b/package.json index f91f6b6..99c5f69 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "scripts": { - "dev": "node fix-agents.js && next dev --webpack -H 0.0.0.0", + "dev": "node fix-agents.js && next dev --webpack -H 0.0.0.0 -p 4000", "build": "next build --webpack", "start": "next start", "lint": "eslint", @@ -15,6 +15,7 @@ }, "dependencies": { "@anthropic-ai/sdk": "^0.96.0", + "@auth/pg-adapter": "^1.11.2", "@clerk/nextjs": "^7.4.2", "@google/generative-ai": "^0.24.1", "@gsap/react": "^2.1.2", @@ -30,6 +31,7 @@ "gsap": "^3.15.0", "lucide-react": "^1.17.0", "next": "^16.2.6", + "next-auth": "^5.0.0-beta.31", "next-themes": "^0.4.6", "openai": "^6.37.0", "papaparse": "^5.5.3", @@ -50,6 +52,7 @@ "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/papaparse": "^5.5.2", + "@types/pg": "^8.20.0", "@types/qrcode": "^1.5.6", "@types/react": "^19", "@types/react-dom": "^19", diff --git a/src/actions/auth-signin.ts b/src/actions/auth-signin.ts new file mode 100644 index 0000000..88ed86b --- /dev/null +++ b/src/actions/auth-signin.ts @@ -0,0 +1,56 @@ +"use server"; + +import { signIn, signOut } from "@/lib/auth"; +import { AuthError } from "next-auth"; + +/** + * Server actions that wrap the Auth.js v5 `signIn` / `signOut` API for + * use from client components. + * + * Why server actions? + * • The Auth.js v5 `signIn` function has to run on the server (it + * needs to set the session cookie, talk to the database adapter, + * and redirect the user to the OAuth provider). + * • Calling it from a client component via a server action keeps the + * client bundle small and avoids exposing the OAuth client secret. + * + * Usage from a client component: + *
+ * + * Usage for the dev credentials provider (dev only): + * + */ + +export async function signInWithGoogle(): Promise