From 089f5c36f0d9967f34026d67f131806f94919b22 Mon Sep 17 00:00:00 2001 From: ht Date: Sun, 6 Sep 2026 18:16:27 +0300 Subject: [PATCH] allow configuring CORS origins --- docs/quality/uygulama-durumu.md | 6 ++++++ server/index.js | 20 ++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 9b6799c..8a01489 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -2018,3 +2018,9 @@ Doğrulama: 288 test geçti, üretim derlemesi başarılı. Kur doğrulaması artık boolean, hexadecimal ve virgüllü belirsiz biçimleri kabul etmiyor; yalnızca açık ondalık sayı biçimi veya sayısal değer işleniyor. Doğrulama: 288 test geçti, üretim derlemesi başarılı. + +### 2026-09-06 · Yapılandırılabilir CORS origin listesi + +API artık `CORS_ORIGINS` ile virgülle verilen izinli origin listesini destekliyor. Liste yapılandırıldığında yalnızca eşleşen origin CORS başlığı alıyor; değişken boş bırakıldığında mevcut yerel/geriye dönük davranış korunuyor. + +Doğrulama: 288 test geçti, üretim derlemesi başarılı. diff --git a/server/index.js b/server/index.js index cf5402d..3ff4b80 100644 --- a/server/index.js +++ b/server/index.js @@ -73,6 +73,7 @@ const CA_BUNDLE = [...tls.rootCertificates, EXTRA_CA]; const PORT = process.env.PORT || 3001; const ILAN_BASE = 'https://www.ilan.gov.tr'; +const CORS_ORIGINS = String(process.env.CORS_ORIGINS || '').split(',').map(value => value.trim()).filter(Boolean); function validateHealthPortfolio(value) { if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Firma profili geçersiz.'); @@ -621,13 +622,16 @@ async function getDetail(ilanId) { // HTTP sunucu // --------------------------------------------------------------------------- function json(res, code, body) { - res.writeHead(code, { + const headers = { 'Content-Type': 'application/json; charset=utf-8', - 'Access-Control-Allow-Origin': '*', 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'no-referrer', - }); + }; + const origin = res.__ihaleistCorsOrigin; + if (origin) headers['Access-Control-Allow-Origin'] = origin; + else if (!CORS_ORIGINS.length) headers['Access-Control-Allow-Origin'] = '*'; + res.writeHead(code, headers); res.end(JSON.stringify(body)); } @@ -732,13 +736,17 @@ const aiJobRunner = startAiJobRunner(async (job,{signal}) => { const server = http.createServer(async (req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`); + const requestOrigin = String(req.headers.origin || '').trim(); + res.__ihaleistCorsOrigin = CORS_ORIGINS.length && CORS_ORIGINS.includes(requestOrigin) ? requestOrigin : null; if (req.method === 'OPTIONS') { - res.writeHead(204, { - 'Access-Control-Allow-Origin': '*', + const headers = { 'Access-Control-Allow-Methods': 'GET,POST,PUT,PATCH,DELETE,OPTIONS', 'Access-Control-Allow-Headers': 'Authorization,Content-Type', 'Access-Control-Max-Age': '600', - }); + }; + if (res.__ihaleistCorsOrigin) headers['Access-Control-Allow-Origin'] = res.__ihaleistCorsOrigin; + else if (!CORS_ORIGINS.length) headers['Access-Control-Allow-Origin'] = '*'; + res.writeHead(204, headers); res.end(); return; }