72 lines
1.8 KiB
TypeScript
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;
|
|
}
|