import { pool } from './db.js' export type AccessStatus = 'pending' | 'approved' | 'rejected' export type AppRole = 'fieldlens-operator' | 'fieldlens-supervisor' | 'fieldlens-admin' export type AccessRequest = { subject: string username: string | null email: string | null status: AccessStatus approvedRole: AppRole | null requestedAt: string reviewedAt: string | null reviewedBy: string | null } const APP_ROLES = new Set(['fieldlens-operator', 'fieldlens-supervisor', 'fieldlens-admin']) function role(value: unknown): AppRole | null { return typeof value === 'string' && APP_ROLES.has(value as AppRole) ? value as AppRole : null } export async function approvedRoleForSubject(subject: string): Promise { const result = await pool.query<{ approved_role: string | null }>( "SELECT approved_role FROM fieldlens_access_requests WHERE subject = $1 AND status = 'approved'", [subject], ) return role(result.rows[0]?.approved_role) } export async function getAccessRequest(subject: string): Promise { const result = await pool.query(` SELECT subject, username, email, status, approved_role AS "approvedRole", requested_at AS "requestedAt", reviewed_at AS "reviewedAt", reviewed_by AS "reviewedBy" FROM fieldlens_access_requests WHERE subject = $1 `, [subject]) return result.rows[0] ?? null } export async function requestAccess(identity: { subject: string; username?: string; email?: string }) { const result = await pool.query(` INSERT INTO fieldlens_access_requests (subject, username, email, status) VALUES ($1, $2, $3, 'pending') ON CONFLICT (subject) DO UPDATE SET username = EXCLUDED.username, email = EXCLUDED.email, status = CASE WHEN fieldlens_access_requests.status = 'approved' THEN 'approved' ELSE 'pending' END, requested_at = CASE WHEN fieldlens_access_requests.status = 'approved' THEN fieldlens_access_requests.requested_at ELSE now() END, reviewed_at = CASE WHEN fieldlens_access_requests.status = 'approved' THEN fieldlens_access_requests.reviewed_at ELSE NULL END, reviewed_by = CASE WHEN fieldlens_access_requests.status = 'approved' THEN fieldlens_access_requests.reviewed_by ELSE NULL END RETURNING subject, username, email, status, approved_role AS "approvedRole", requested_at AS "requestedAt", reviewed_at AS "reviewedAt", reviewed_by AS "reviewedBy" `, [identity.subject, identity.username ?? null, identity.email ?? null]) return result.rows[0]! } export async function listAccessRequests(status: AccessStatus = 'pending') { const result = await pool.query(` SELECT subject, username, email, status, approved_role AS "approvedRole", requested_at AS "requestedAt", reviewed_at AS "reviewedAt", reviewed_by AS "reviewedBy" FROM fieldlens_access_requests WHERE status = $1 ORDER BY requested_at ASC LIMIT 100 `, [status]) return result.rows } export async function reviewAccessRequest( subject: string, decision: 'approved' | 'rejected', reviewer: string, ) { const result = await pool.query(` UPDATE fieldlens_access_requests SET status = $2, approved_role = CASE WHEN $2 = 'approved' THEN 'fieldlens-operator' ELSE NULL END, reviewed_at = now(), reviewed_by = $3 WHERE subject = $1 RETURNING subject, username, email, status, approved_role AS "approvedRole", requested_at AS "requestedAt", reviewed_at AS "reviewedAt", reviewed_by AS "reviewedBy" `, [subject, decision, reviewer]) return result.rows[0] ?? null }