MIF_E31221222/sigap-website/supabase/functions/detect-face/index.ts

143 lines
6.0 KiB
TypeScript

// 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"}'
*/