feat(db+auth): add pg pool, admin_users email/provider migration, refactor auth lookup

- Create src/lib/db.ts (shared pg.Pool, query/withTx helpers, server-only)
- Add @types/pg devDep
- Migration 204: add email, auth_provider, auth_subject columns to
  admin_users; backfill from auth.users; new upsert_admin_user accepts
  multi-provider args; new get_admin_user_for_session RPC resolves
  Auth.js session id (UUID or Google sub) to an admin row
- Refactor getAdminUser() to use pg + new RPC; auto-provisions on first
  Google sign-in using session.user.email
- Refactor updatePasswordAction to call update_user_password via pg
  (drops the Supabase REST hop)
- Delete orphaned src/actions/login.ts (replaced by auth-actions.ts)
- Drop remaining DEV_FORCE_UID references in users.ts; dev path now
  uses dev_session cookie (the only cookie the dev flow can set)
- Update AdminUser type: user_id is now string | null (Google users
  have no Supabase auth id); add email + auth_provider fields
- Fix downstream type errors: StopProductAssignment.callerUid accepts
  null; pickup.ts performedBy widens to string | null
- Bump vitest config, tests/, and other earlier cleanup changes
This commit is contained in:
2026-06-06 23:41:41 +00:00
parent 9374e63ae6
commit f96dcd01f2
53 changed files with 1837 additions and 1656 deletions
+7 -39
View File
@@ -1,41 +1,9 @@
"use client";
// Server-side logout. Signs the user out of the Auth.js v5 session and
// redirects to /login. The previous client-side implementation (which
// called Supabase auth.signOut) was replaced with this so logout goes
// through the same auth path the rest of the app uses.
import { signOut } from "@/lib/auth";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { supabase } from "@/lib/supabase";
export default function LogoutPage() {
const router = useRouter();
useEffect(() => {
// Clear all auth cookies — dev_session, rc_auth_uid, rc_auth_token
document.cookie = "dev_session=;path=/;max-age=0";
document.cookie = "rc_auth_uid=;path=/;max-age=0";
document.cookie = "rc_auth_token=;path=/;max-age=0";
// Clear shopping cart on logout
localStorage.removeItem("route_commerce_cart");
localStorage.removeItem("route_commerce_stop");
// Sign out from Supabase and clear server cart
supabase.auth.getUser().then(async ({ data }) => {
if (data.user?.id) {
const { clearServerCart } = await import("@/actions/checkout");
clearServerCart(data.user.id).catch(() => {});
}
supabase.auth.signOut().then(() => {
router.push("/login");
router.refresh();
});
});
}, [router]);
return (
<main className="min-h-screen bg-slate-100 px-6 py-12">
<div className="mx-auto max-w-md">
<div className="rounded-2xl bg-white p-8 shadow-sm ring-1 ring-slate-200 text-center">
<p className="text-slate-500">Signing out...</p>
</div>
</div>
</main>
);
export default async function LogoutPage() {
await signOut({ redirectTo: "/login" });
}