// Driver-side auth helper. Every driver-action endpoint first calls // requireDriverProfile: it proves the request is from a signed-in user and // that the user has completed onboarding (has a linked drivers row). A // driver-role user who hasn't onboarded yet gets a 403 so the client can // route them to the onboarding form rather than showing a bare 404. import { requireAuth } from "@/lib/jwt"; import { sql } from "@/lib/db"; import type { ServiceId } from "@/constants/services"; type Auth = { userId: string; email: string }; /** * Vetting state of a driver profile. * pending — onboarded, waiting on an owner review. Cannot go online. * approved — cleared to drive. The only state dispatch will match. * rejected — review failed; the driver sees why and can resubmit. * suspended — was approved, pulled by an owner. */ export const DRIVER_APPROVAL_STATUSES = [ "pending", "approved", "rejected", "suspended", ] as const; export type DriverApprovalStatus = (typeof DRIVER_APPROVAL_STATUSES)[number]; export const isApprovalStatus = (v: unknown): v is DriverApprovalStatus => typeof v === "string" && (DRIVER_APPROVAL_STATUSES as readonly string[]).includes(v); export type DriverProfile = { auth: Auth; driverId: number; service: ServiceId; online: boolean; approvalStatus: DriverApprovalStatus; }; export type AuthError = { error: Response }; const VALID_SERVICES = ["car", "moto", "courier", "chauffeur"] as const; export const isServiceId = (v: unknown): v is ServiceId => typeof v === "string" && (VALID_SERVICES as readonly string[]).includes(v); // Returns the driver profile for the authenticated user, or a 401/403 the // caller can return directly. A 403 with the onboarding code tells the client // to show the onboarding form instead of treating it as a hard error. export const requireDriverProfile = async ( req: Request, ): Promise => { const auth = requireAuth(req); if ("error" in auth) return { error: auth.error }; const rows = await sql<{ id: number; service: ServiceId; online: boolean; approval_status: DriverApprovalStatus; }>` SELECT id, service, online, approval_status FROM drivers WHERE user_id = ${auth.userId} `; if (!rows[0]) { return { error: Response.json( { error: "No driver profile — complete onboarding.", code: "ONBOARD" }, { status: 403 }, ), }; } const { id, service, online, approval_status } = rows[0]; return { auth, driverId: id, service, online, approvalStatus: approval_status, }; }; /** * Gate for anything a driver can only do once they've been cleared to drive: * going online, taking an offer, moving a ride through its states. Returns a * ready-to-return 403 carrying the current status, so the client can show the * pending / rejected screen instead of a bare error. */ export const requireApprovedDriver = async ( req: Request, ): Promise => { const result = await requireDriverProfile(req); if ("error" in result) return result; if (result.approvalStatus !== "approved") { return { error: Response.json( { error: "Your driver account is not approved yet.", code: "NOT_APPROVED", approval_status: result.approvalStatus, }, { status: 403 }, ), }; } return result; };