브라우저가 공개 키로 테이블을 직접 읽고 쓰던 구조를 없앤다.
- api/db.js: 정해진 작업만 서비스 키로 실행 (임의 쿼리·열 지정 불가)
- api/_origin.js: 서버 함수 공용 출처 검사, 공개 배포에서는 404
- src/lib/db.ts: 화면용 호출 함수, 반환 형태 { data, error } 유지
- 빌드 큐에 바로 넣던 예전 진단 신청 모달 삭제
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
60 lines
2.2 KiB
JavaScript
60 lines
2.2 KiB
JavaScript
/**
|
|
* 서버 함수 공용 출처 검사 (Vercel 서버 함수 전용, '_' 로 시작해 주소로 열리지 않는다).
|
|
*
|
|
* infinith-demo 와 그 미리보기, 로컬 개발에서 온 요청만 받는다. 데모는 Vercel 배포 보호(팀원 로그인) 뒤에 있어
|
|
* 서버 함수도 로그인한 팀원만 부를 수 있다. 공개 배포(webforai.kr, VITE_SITE=discovery)에서는 내부 함수를 열지 않는다.
|
|
*/
|
|
|
|
const ALLOWED_ORIGINS = [
|
|
'https://infinith-demo.vercel.app',
|
|
'http://localhost:3000',
|
|
'http://127.0.0.1:3000',
|
|
];
|
|
|
|
/** 이 프로젝트의 미리보기 배포 주소. 미리보기는 Vercel 로그인(배포 보호) 뒤에 있어 팀원만 연다. */
|
|
const PREVIEW_ORIGIN = /^https:\/\/infinith-demo-[a-z0-9]+-o2odev-5530s-projects\.vercel\.app$/;
|
|
const isAllowedOrigin = (o) => ALLOWED_ORIGINS.includes(o) || PREVIEW_ORIGIN.test(o);
|
|
|
|
/**
|
|
* CORS·메서드·출처를 확인한다. 통과하면 null, 아니면 이미 응답을 보낸 뒤 true 를 돌려준다.
|
|
* @param {string[]} methods 허용 메서드
|
|
*/
|
|
export function guard(req, res, methods) {
|
|
if (process.env.VITE_SITE === 'discovery') {
|
|
res.status(404).json({ ok: false, error: 'not found' });
|
|
return true;
|
|
}
|
|
const origin = req.headers.origin || '';
|
|
const allowed = isAllowedOrigin(origin);
|
|
if (allowed) {
|
|
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
res.setHeader('Vary', 'Origin');
|
|
res.setHeader('Access-Control-Allow-Methods', [...methods, 'OPTIONS'].join(', '));
|
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
}
|
|
if (req.method === 'OPTIONS') {
|
|
res.status(allowed ? 204 : 403).end();
|
|
return true;
|
|
}
|
|
if (!methods.includes(req.method)) {
|
|
res.status(405).json({ ok: false, error: 'method not allowed' });
|
|
return true;
|
|
}
|
|
// 같은 출처 GET 은 브라우저가 Origin 을 붙이지 않을 수 있어 Referer 로도 확인한다.
|
|
const referer = req.headers.referer || '';
|
|
const refOrigin = (referer.match(/^https?:\/\/[^/]+/) || [''])[0];
|
|
if (!allowed && !isAllowedOrigin(refOrigin)) {
|
|
res.status(403).json({ ok: false, error: 'forbidden origin' });
|
|
return true;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function safeParse(s) {
|
|
try {
|
|
return JSON.parse(s);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|