-
Notifications
You must be signed in to change notification settings - Fork 0
Centralize CORS headers and response header builders for proxy endpoints #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,10 +10,13 @@ | |
| // Ideal para chamar via cron a cada 5 minutos com evaluate=1. | ||
|
|
||
| import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.49.1' | ||
| import { getCorsHeaders } from '../_shared/validation.ts' | ||
|
|
||
| const corsHeaders = { | ||
| 'Access-Control-Allow-Origin': '*', | ||
| 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', | ||
| function buildJsonHeaders(req: Request): Record<string, string> { | ||
| return { | ||
| ...getCorsHeaders(req), | ||
| 'Content-Type': 'application/json', | ||
| } | ||
| } | ||
|
|
||
| // Limiares de alerta — ajustáveis via env vars. | ||
|
|
@@ -167,8 +170,10 @@ export function evaluateAlerts(m: ComputedMetrics): AlertCandidate[] { | |
| } | ||
|
|
||
| Deno.serve(async (req) => { | ||
| const headers = buildJsonHeaders(req) | ||
|
|
||
| if (req.method === 'OPTIONS') { | ||
| return new Response('ok', { headers: corsHeaders }) | ||
| return new Response('ok', { headers }) | ||
|
Comment on lines
172
to
+176
|
||
| } | ||
|
|
||
| const url = new URL(req.url) | ||
|
|
@@ -192,7 +197,7 @@ Deno.serve(async (req) => { | |
|
|
||
| if (error) { | ||
| return new Response(JSON.stringify({ error: error.message }), { | ||
| status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' }, | ||
| status: 500, headers, | ||
| }) | ||
| } | ||
|
|
||
|
|
@@ -259,6 +264,6 @@ Deno.serve(async (req) => { | |
| alert_candidates: candidates, | ||
| fired_alerts: firedAlerts, | ||
| }, null, 2), { | ||
| headers: { ...corsHeaders, 'Content-Type': 'application/json' }, | ||
| headers, | ||
| }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,17 +15,18 @@ | |
| // Output: text/plain; version=0.0.4 (Prometheus exposition format). | ||
|
|
||
| import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.45.0' | ||
| import { getCorsHeaders } from '../_shared/validation.ts' | ||
|
|
||
| const SUPABASE_URL = Deno.env.get('SUPABASE_URL')! | ||
| const SERVICE_ROLE_KEY = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')! | ||
| const SCRAPE_TOKEN = Deno.env.get('PROXY_METRICS_TOKEN') ?? '' | ||
|
|
||
| const PROM_HEADERS = { | ||
| 'Content-Type': 'text/plain; version=0.0.4; charset=utf-8', | ||
| 'Cache-Control': 'no-store', | ||
| 'Access-Control-Allow-Origin': '*', | ||
| 'Access-Control-Allow-Headers': 'authorization, content-type', | ||
| 'Access-Control-Allow-Methods': 'GET, OPTIONS', | ||
| function buildPromHeaders(req: Request): Record<string, string> { | ||
| return { | ||
| ...getCorsHeaders(req), | ||
| 'Content-Type': 'text/plain; version=0.0.4; charset=utf-8', | ||
| 'Cache-Control': 'no-store', | ||
| } | ||
|
Comment on lines
17
to
+29
|
||
| } | ||
|
|
||
| type MetricRow = { | ||
|
|
@@ -194,24 +195,26 @@ function buildExposition(rows: MetricRow[], windowKey: WindowKey, generatedAtMs: | |
| } | ||
|
|
||
| Deno.serve(async (req) => { | ||
| const headers = buildPromHeaders(req) | ||
|
|
||
| if (req.method === 'OPTIONS') { | ||
| return new Response('ok', { headers: PROM_HEADERS }) | ||
| return new Response('ok', { headers }) | ||
| } | ||
| if (req.method !== 'GET') { | ||
| return new Response('Method not allowed\n', { status: 405, headers: PROM_HEADERS }) | ||
| return new Response('Method not allowed\n', { status: 405, headers }) | ||
| } | ||
|
|
||
| // Auth — fail-closed if no token configured. | ||
| if (!SCRAPE_TOKEN) { | ||
| return new Response( | ||
| '# proxy-metrics: PROXY_METRICS_TOKEN secret is not configured.\n', | ||
| { status: 503, headers: PROM_HEADERS }, | ||
| { status: 503, headers }, | ||
| ) | ||
| } | ||
| const auth = req.headers.get('Authorization') ?? '' | ||
| const provided = auth.startsWith('Bearer ') ? auth.slice(7) : auth | ||
| if (provided !== SCRAPE_TOKEN) { | ||
| return new Response('# unauthorized\n', { status: 401, headers: PROM_HEADERS }) | ||
| return new Response('# unauthorized\n', { status: 401, headers }) | ||
| } | ||
|
|
||
| const url = new URL(req.url) | ||
|
|
@@ -236,7 +239,7 @@ Deno.serve(async (req) => { | |
| if (error) { | ||
| return new Response( | ||
| `# error fetching proxy_metrics: ${error.message.replace(/\n/g, ' ')}\n`, | ||
| { status: 500, headers: PROM_HEADERS }, | ||
| { status: 500, headers }, | ||
| ) | ||
| } | ||
| if (!data || data.length === 0) break | ||
|
|
@@ -245,5 +248,5 @@ Deno.serve(async (req) => { | |
| } | ||
|
|
||
| const body = buildExposition(rows, windowKey, Date.now()) | ||
| return new Response(body, { status: 200, headers: PROM_HEADERS }) | ||
| return new Response(body, { status: 200, headers }) | ||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change switches from
Access-Control-Allow-Origin: *(previous inlinecorsHeaders) to origin-validatedgetCorsHeaders(req)(allowlist + fallback origin) and also adds defaultCache-Control: no-store/security headers. That’s a behavioral change for CORS/caching, so the PR description’s claim that functional behavior is “otherwise unchanged” is no longer accurate; please update the description/release notes (or explicitly confirm the new CORS policy is intended for these endpoints).