import { auth } from '@/auth'; import { v5 as uuidV5 } from 'uuid'; interface ApiResponse { code: number; // code from server 0 message: string; data: T; } const clefApiBuilder = ( path: string, options?: { // default to GET method: 'GET' | 'POST'; // default to 'app' prefix: 'api' | 'app' | 'api.dev' | 'app.dev'; }, ) => { return async (params: Params): Promise => { const session = await auth(); const email = session?.user?.email; if (!email || !email.endsWith('@landing.ai')) { throw new Response('Unauthorized', { status: 401, }); } const sessionUser = { id: uuidV5(email, uuidV5.URL), orgId: '-1024', email: email, username: email.split('@')[0], userRole: 'adminPortal', bucket: 'fake_bucket', }; const prefix = options?.prefix ?? 'app'; const baseURL = `https://${prefix}.landing.ai/${path}`; const method = options?.method ?? 'GET'; // Create a URL object with query params const url = new URL(baseURL); if (method === 'GET') { Object.entries(params ?? {}).forEach(([key, value]) => url.searchParams.append(key, value), ); } const fetchParams: RequestInit = { method: options?.method ?? 'GET', headers: { sessionuser: JSON.stringify(sessionUser), // faked production user // apikey: 'land_sk_DKeoYtaZZrYqJ9TMMiXe4BIQgJcZ0s3XAoB0JT3jv73FFqnr6k', // dev key apikey: 'land_sk_Z0AWhSGURwC8UVTjyZDH7VMsYod6jfTK79fC2RTGD85NkV6hwO', // prod key 'X-ROUTE': 'mingruizhang-landing', }, }; if (method === 'POST' && params instanceof Object) { const formData = new FormData(); for (const [key, value] of Object.entries(params)) { formData.append(key, value); } fetchParams.body = formData; } const res = await fetch(url.toString(), fetchParams); if (!res.ok) { console.error('ERROR: fetch data failure', res.status, res.statusText); // This will activate the closest `error.js` Error Boundary throw new Error('Failed to fetch data'); } const { data }: ApiResponse = await res.json(); return data; }; }; export type ProjectBaseInfo = { id: number; name: string; created_at: Date; label_type: string; orgName: string; orgId: number; subscription: string; }; /** * Fetch recent projects from all organizations from past 30 days, excluding * 1. test organization such as bdd, cypress * 2. internal organization such as landing, landing-ai, orgId=1 * 3. projects not containing media or only contain sample media * @author https://github.com/landing-ai/landing-platform/blob/mingrui-04-08-meaningful-project/packages/server-clef/src/main_app/controllers/admin/get_admin_meaningful_project_controller.ts */ export const fetchRecentProjectList = clefApiBuilder( 'api/admin/vision-agent/projects/recent', ); export type MediaDetails = { id: number; name: string; path: string; url: string; projectId: number; thumbnails: string[]; properties: { width: number; height: number; format: string; orientation: number; }; }; /** * Randomly fetch 10 media from a given project * @author https://github.com/landing-ai/landing-platform/blob/mingrui-04-08-meaningful-project/packages/server-clef/src/main_app/controllers/admin/get_admin_meaningful_project_controller.ts */ export const fetchProjectMedia = clefApiBuilder< { projectId: number }, MediaDetails[] >('api/admin/vision-agent/project/media'); export type ClassDetails = { id: number; name: string; descriptionText?: string | null; orgId: number; projectId: number; createdAt: string; updatedAt: string | null; color?: string | null; }; /** * Fetch all active classes of a given project * @author https://github.com/landing-ai/landing-platform/blob/mingrui-04-08-meaningful-project/packages/server-clef/src/main_app/controllers/admin/get_admin_meaningful_project_controller.ts */ export const fetchProjectClass = clefApiBuilder< { projectId: number }, ClassDetails[] >('api/admin/vision-agent/project/class');