generated from ttipo/project-base
d1ef0da488
메일 프로그램(IMAP · SMTP) 로그인 기록을 엔진 웹훅으로 받는다. - 루트 env MAIL_WEBHOOK_SECRET(setup · backend 공용, 운영 필수)과 backend 의 APP_NETWORK_SUBNET - setup 이 엔진 웹훅을 맞추고(표시 머리글 — 소금 + 키 지문) 서명한 빈 묶음으로 받는 곳을 확인한다 키를 바꾼 실행 · 게이트웨이 OTP · 테스트 직원의 402 를 따로 다룬다 - nginx 두 server 가 /api/internal/ 을 닫고, 요청 대상의 날 \(경로) · # · 0x80 이상은 location 전에 400 Express 가 이런 대상을 다시 읽어 닫은 경로(/api/internal · /api/health)가 백엔드에서 열렸다(2026-10-31 리뷰) - CI "닫는 경로 시험"(두 server × 세 경로의 변형) · 지시어만 세는 정적 검사 - mail/README: 웹훅 · 12단계 전부터 운영 중인 서버를 올리는 차례 · 휴대폰을 잃은 직원(관리 API 명령) 엔진 DNS 가 Custom(운영)이어도 웹훅은 backend 를 푼다 — 메일 컨테이너가 도커 DNS 를 쓴다(개발에서 확인). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
64 lines
3.6 KiB
Plaintext
64 lines
3.6 KiB
Plaintext
# ==========================================
|
|
# /api 의 nginx 오류 응답 — 두 server(포털 · 공개 내려받기)가 넣는다 (nginx.conf). 템플릿이 아니다(/etc/nginx/snippets/api-errors.conf)
|
|
# (잘못된 원문 요청 대상의 400 은 화면 경로에도 같은 JSON 이다 — 아래 /__request-target-rejected)
|
|
#
|
|
# message 는 비운다 (문구는 클라이언트가 code 로 사전에서 찾는다, 레지스트리와 겹치지 않게).
|
|
# traceId 는 이 nginx 의 요청 ID(로그의 rid=)다. 이름 있는 location 은 server 단계라 화면용 헤더 상속을 끈다
|
|
# (접근 로그는 server 단계의 쿼리 없는 main_rid 를 그대로 물려받는다).
|
|
# return 200 은 본문만 정한다 — error_page 를 = 없이 썼으므로 상태 코드는 원래 값(404·413·429·502·504 등)이 그대로 나간다 (2026-10-27 확인)
|
|
# ==========================================
|
|
|
|
# nginx 가 닫은 경로 404 — 공개 내려받기 server 의 /api/(공개 API 밖) · 포털의 공개 API(공개 호스트를 따로 둘 때) ·
|
|
# 두 server 의 /api/internal/(엔진만 부르는 경로 — 12단계, internal-api-closed.conf). 없는 경로와 같은 RES_001
|
|
location @api_not_found {
|
|
add_header_inherit off;
|
|
default_type application/json;
|
|
add_header Cache-Control "no-store" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-Request-Id $request_id always;
|
|
return 200 '{"success":false,"error":{"code":"RES_001","message":"","traceId":"$request_id"}}';
|
|
}
|
|
|
|
# 본문 한도 초과 413 — 첨부 · 업로드 · 대용량 조각 · 공개 내려받기 server 의 /api/(1m)
|
|
location @api_payload_too_large {
|
|
add_header_inherit off;
|
|
default_type application/json;
|
|
add_header Cache-Control "no-store" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-Request-Id $request_id always;
|
|
return 200 '{"success":false,"error":{"code":"FILE_002","message":"","traceId":"$request_id"}}';
|
|
}
|
|
|
|
# 동시 연결 수 초과 429 — 대용량 받기의 limit_conn (11단계 — nginx.conf 의 http 단계 "동시 연결 수"). 백엔드의 요청 수 한도와 같은 REQ_001
|
|
location @api_too_many_requests {
|
|
add_header_inherit off;
|
|
default_type application/json;
|
|
add_header Cache-Control "no-store" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-Request-Id $request_id always;
|
|
return 200 '{"success":false,"error":{"code":"REQ_001","message":"","traceId":"$request_id"}}';
|
|
}
|
|
|
|
# 원문 요청 대상이 잘못된 400 — 경로의 날 역슬래시 · 날 # · 0x80 이상 바이트 (nginx.conf 의 $request_target_rejected — 두 server 의 server 단계 if 가
|
|
# 이리로 넘긴다). 화면 경로든 /api 든 같은 JSON 이다 — 브라우저는 이런 요청을 보내지 않는다. 입력 형식 오류의 VAL_001.
|
|
# internal 이라 바깥에서 이 경로를 바로 부르면 404 다
|
|
location = /__request-target-rejected {
|
|
internal;
|
|
add_header_inherit off;
|
|
default_type application/json;
|
|
add_header Cache-Control "no-store" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-Request-Id $request_id always;
|
|
return 400 '{"success":false,"error":{"code":"VAL_001","message":"","traceId":"$request_id"}}';
|
|
}
|
|
|
|
# 백엔드 응답 없음 · 시간 초과 등 5xx
|
|
location @api_unavailable {
|
|
add_header_inherit off;
|
|
default_type application/json;
|
|
add_header Cache-Control "no-store" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-Request-Id $request_id always;
|
|
return 200 '{"success":false,"error":{"code":"SYS_001","message":"","traceId":"$request_id"}}';
|
|
}
|