Add self-hosted auth, admin API, and owner web dashboard
- Replace Clerk with self-hosted JWT auth (register/login/verify, bcrypt passwords, Gmail OTP with console fallback) - Add lib/db.ts pg pool + transaction helpers; seed script migrates legacy Clerk-era schema (drop clerk_id, enforce UUID ids and unique email) - Add owner-gated admin API: stats, users, drivers CRUD, rides - Add dashboard/ Vite React owner dashboard (login, overview, users, fleet, rides) with dev-server proxy to avoid Expo CORS middleware - Add scripts/set-owner.mjs for role management
This commit is contained in:
+14
-53
@@ -1,16 +1,13 @@
|
||||
import { neon } from "@neondatabase/serverless";
|
||||
import { requireAuth } from "@/lib/jwt";
|
||||
import { sql } from "@/lib/db";
|
||||
|
||||
export async function GET(req: Request) {
|
||||
const sql = neon(process.env.DATABASE_URL!);
|
||||
const clerkId = new URL(req.url).searchParams.get("clerkId");
|
||||
|
||||
if (!clerkId) {
|
||||
return Response.json({ error: "Missing clerkId" }, { status: 400 });
|
||||
}
|
||||
const auth = requireAuth(req);
|
||||
if ("error" in auth) return auth.error;
|
||||
|
||||
try {
|
||||
const response = await sql`
|
||||
SELECT id, name, email, role FROM users WHERE clerk_id = ${clerkId}
|
||||
SELECT id, name, email, phone, role FROM users WHERE id = ${auth.userId}
|
||||
`;
|
||||
|
||||
return Response.json({ data: response[0] ?? null });
|
||||
@@ -21,57 +18,21 @@ export async function GET(req: Request) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const sql = neon(process.env.DATABASE_URL!);
|
||||
const { name, email, clerkId } = await req.json();
|
||||
|
||||
if (!name || !email || !clerkId) {
|
||||
return Response.json(
|
||||
{
|
||||
error: "Missing required fields!",
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await sql`
|
||||
INSERT INTO users (
|
||||
name,
|
||||
email,
|
||||
clerk_id
|
||||
)
|
||||
VALUES (
|
||||
${name},
|
||||
${email},
|
||||
${clerkId}
|
||||
)
|
||||
`;
|
||||
|
||||
return new Response(JSON.stringify({ data: response }));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
return Response.json({ error }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function PATCH(req: Request) {
|
||||
const sql = neon(process.env.DATABASE_URL!);
|
||||
const { clerkId, role } = await req.json();
|
||||
const auth = requireAuth(req);
|
||||
if ("error" in auth) return auth.error;
|
||||
|
||||
if (!clerkId || !["rider", "driver"].includes(role)) {
|
||||
return Response.json(
|
||||
{ error: "Missing clerkId or invalid role." },
|
||||
{ status: 400 },
|
||||
);
|
||||
const { role } = await req.json();
|
||||
|
||||
if (!["rider", "driver"].includes(role)) {
|
||||
return Response.json({ error: "Invalid role." }, { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await sql`
|
||||
UPDATE users SET role = ${role} WHERE clerk_id = ${clerkId} RETURNING id, role
|
||||
UPDATE users SET role = ${role}
|
||||
WHERE id = ${auth.userId}
|
||||
RETURNING id, role
|
||||
`;
|
||||
|
||||
if (response.length === 0) {
|
||||
|
||||
Reference in New Issue
Block a user