// Follow this setup guide to integrate the Deno language server with your editor: // https://deno.land/manual/getting_started/setup_your_environment // This enables autocomplete, go to definition, etc. // Setup type definitions for built-in Supabase Runtime APIs import "jsr:@supabase/functions-js/edge-runtime.d.ts"; import { serve } from "https://deno.land/std@0.177.0/http/server.ts"; const AWS_REGION = Deno.env.get('AWS_REGION'); const AWS_ACCESS_KEY = Deno.env.get('AWS_ACCESS_KEY'); const AWS_SECRET_KEY = Deno.env.get('AWS_SECRET_KEY'); serve(async (req)=>{ console.log('AWS_REGION:', AWS_REGION); console.log('AWS_ACCESS_KEY:', AWS_ACCESS_KEY?.slice(0, 5)); // for security, partial only console.log('AWS_SECRET_KEY:', AWS_SECRET_KEY?.slice(0, 5)); // for security, partial only try { // Check if we have AWS credentials if (!AWS_REGION || !AWS_ACCESS_KEY || !AWS_SECRET_KEY) { return new Response(JSON.stringify({ error: 'AWS credentials are not configured' }), { status: 500, headers: { 'Content-Type': 'application/json' } }); } // Parse the multipart form data to get the image const formData = await req.formData(); const image = formData.get('image'); if (!image || !(image instanceof File)) { return new Response(JSON.stringify({ error: 'Image file is required' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } // Convert image to base64 const imageBuffer = await image.arrayBuffer(); const base64Image = btoa(String.fromCharCode(...new Uint8Array(imageBuffer))); // Create AWS signature for authorization const date = new Date(); const amzDate = date.toISOString().replace(/[:-]|\.\d{3}/g, ''); const dateStamp = amzDate.substring(0, 8); const host = `rekognition.${AWS_REGION}.amazonaws.com`; const endpoint = `https://${host}/`; const request = { "Image": { "Bytes": base64Image }, "Attributes": [ "ALL" ] }; // AWS Signature V4 calculation const method = 'POST'; const service = 'rekognition'; const contentType = 'application/x-amz-json-1.1'; const amzTarget = 'RekognitionService.DetectFaces'; const canonicalUri = '/'; const canonicalQueryString = ''; const payloadHash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify(request))).then((hash)=>Array.from(new Uint8Array(hash)).map((b)=>b.toString(16).padStart(2, '0')).join('')); const canonicalHeaders = `content-type:${contentType}\n` + `host:${host}\n` + `x-amz-date:${amzDate}\n` + `x-amz-target:${amzTarget}\n`; const signedHeaders = 'content-type;host;x-amz-date;x-amz-target'; const canonicalRequest = `${method}\n${canonicalUri}\n${canonicalQueryString}\n${canonicalHeaders}\n${signedHeaders}\n${payloadHash}`; const algorithm = 'AWS4-HMAC-SHA256'; const credentialScope = `${dateStamp}/${AWS_REGION}/${service}/aws4_request`; const stringToSign = `${algorithm}\n${amzDate}\n${credentialScope}\n${await crypto.subtle.digest("SHA-256", new TextEncoder().encode(canonicalRequest)).then((hash)=>Array.from(new Uint8Array(hash)).map((b)=>b.toString(16).padStart(2, '0')).join(''))}`; const getSignatureKey = async (key, dateStamp, regionName, serviceName)=>{ const kDate = await crypto.subtle.importKey("raw", new TextEncoder().encode(`AWS4${key}`), { name: "HMAC", hash: "SHA-256" }, false, [ "sign" ]); const kRegion = await crypto.subtle.sign("HMAC", kDate, new TextEncoder().encode(regionName)); const kService = await crypto.subtle.sign("HMAC", await crypto.subtle.importKey("raw", kRegion, { name: "HMAC", hash: "SHA-256" }, false, [ "sign" ]), new TextEncoder().encode(serviceName)); return crypto.subtle.sign("HMAC", await crypto.subtle.importKey("raw", kService, { name: "HMAC", hash: "SHA-256" }, false, [ "sign" ]), new TextEncoder().encode("aws4_request")); }; const signingKey = await getSignatureKey(AWS_SECRET_KEY, dateStamp, AWS_REGION, service); const signature = await crypto.subtle.sign("HMAC", await crypto.subtle.importKey("raw", signingKey, { name: "HMAC", hash: "SHA-256" }, false, [ "sign" ]), new TextEncoder().encode(stringToSign)).then((hash)=>Array.from(new Uint8Array(hash)).map((b)=>b.toString(16).padStart(2, '0')).join('')); const authHeader = `${algorithm} ` + `Credential=${AWS_ACCESS_KEY}/${credentialScope}, ` + `SignedHeaders=${signedHeaders}, ` + `Signature=${signature}`; // Make request to AWS Rekognition const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': contentType, 'X-Amz-Date': amzDate, 'X-Amz-Target': amzTarget, 'Authorization': authHeader }, body: JSON.stringify(request) }); const data = await response.json(); return new Response(JSON.stringify({ success: true, faceDetails: data.FaceDetails || [], count: (data.FaceDetails || []).length }), { headers: { 'Content-Type': 'application/json' } }); } catch (error) { console.error("Error in detect-face function:", error); return new Response(JSON.stringify({ error: "Failed to process the image", details: error instanceof Error ? error.message : String(error) }), { status: 500, headers: { 'Content-Type': 'application/json' } }); } }); /* To invoke locally: 1. Run `supabase start` (see: https://supabase.com/docs/reference/cli/supabase-start) 2. Make an HTTP request: curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/detect-face' \ --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' \ --header 'Content-Type: application/json' \ --data '{"name":"Functions"}' */