2026-03-04 18:25:09 +00:00

72 lines
1.8 KiB
TypeScript

import { createClient } from 'jsr:@supabase/supabase-js@2';
export interface AuthResult {
userId: string;
error?: never;
}
export interface AuthError {
userId?: never;
error: Response;
}
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};
export async function requireAuth(req: Request): Promise<AuthResult | AuthError> {
const authHeader = req.headers.get('Authorization') ?? '';
const token = authHeader.replace('Bearer ', '').trim();
if (!token) {
return {
error: new Response(JSON.stringify({ error: 'Unauthorized: missing token' }), {
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
}),
};
}
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_ANON_KEY')!
);
const { data: { user }, error } = await supabase.auth.getUser(token);
if (error || !user) {
return {
error: new Response(JSON.stringify({ error: 'Unauthorized: invalid token' }), {
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
}),
};
}
return { userId: user.id };
}
export async function checkRateLimit(
userId: string,
endpoint: string,
serviceClient: ReturnType<typeof createClient>
): Promise<Response | null> {
const clientIp = '0.0.0.0';
const { data, error } = await serviceClient.rpc('check_rate_limit', {
p_user_id: userId,
p_ip_address: clientIp,
p_endpoint: endpoint,
});
if (!error && data && !data.allowed) {
return new Response(
JSON.stringify({ error: 'Rate limit exceeded', reset_at: data.reset_at }),
{ status: 429, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
return null;
}