generated from ttipo/project-base
feat(backend): 대용량 첨부 — 조각 올리기 · 악성 검사 · 공개 링크 · 내려받기
- file-storage: /api/file-uploads(만들기 · 16MiB 조각 PUT — Content-Digest · datasync · 같은 조각은 하나만 · 끝내기 · 상태 · 지우기),
/api/file-quota. 보관량(직원 · 서버 · 디스크 여유)은 pg_advisory_xact_lock 안에서 센다. 저장 포트 FILE_BLOB_STORE(지금은 로컬 볼륨).
ClamAV INSTREAM 을 직접 구현했다 — 판정 글은 한 곳(암호 · 한도 → 검사하지 못함, 스트림 크기 한도 → 설정 오류로 검사 중 그대로 + error),
다 보낸 뒤 판정 대기 30분 + 1MB 마다 1초 · TCP keepalive. 검사 차례는 DB pending(동시 2), 정리 cron.
- file-sharing · mail: 메일 한 통에 링크 하나(토큰 256비트 — DB 에는 SHA-256 만). 보낼 때 서버가 본문에 덩이를 넣고(표시는 서버만 —
두 정화기가 사용자 HTML 의 data-ara-* · 주석을 지운다), 링크는 확실한 실패(SendNotStartedException)에서만 거둔다.
공개 /api/public-links/{token} · 파일 받기(Range · 조건부 UPDATE 로 받은 수), 링크 끊기 · 목록 · 읽기 화면의 largeFiles.
- 공개 호스트 미들웨어는 원문 · 라우터 경로 · WHATWG URL · 퍼센트를 푼 원문을 모두 보고(리뷰 — 절대 주소 꼴 · \ 요청이 막기를 지났다),
라우팅은 대소문자를 가린다(createHttpAdapter — /FILES/ 같은 변형이 앞단 상한을 비켜 갔다).
- env: SHARE_PUBLIC_URL(nginx 와 같은 모양 검사 · 운영에서 포털과 같은 호스트면 기동 중단) · MAIL_LARGE_FILE_* · FILE_STORAGE_* · CLAMAV_*.
- 에러 코드 FILE_003 ~ FILE_008 · MAIL_016 — 웹 · 앱 사전 다시 생성. 마이그레이션 둘.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+11
-6
@@ -9,7 +9,7 @@ import type {
|
||||
Response as ExpressResponse,
|
||||
} from 'express';
|
||||
|
||||
import { configureApp } from '@/app.setup';
|
||||
import { configureApp, createHttpAdapter } from '@/app.setup';
|
||||
import { ACCESS_TOKEN_COOKIE } from '@/modules/auth/auth.constants';
|
||||
import { winstonConfig } from '@/shared/logger/winston.config';
|
||||
import { parsePositiveInt } from '@/shared/utils/env';
|
||||
@@ -22,11 +22,16 @@ import {
|
||||
const DEFAULT_PORT = 3000;
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
|
||||
logger: WinstonModule.createLogger(winstonConfig),
|
||||
// 본문 파서는 configureApp 이 직접 등록한다 (JSON 만, 크기 한도 지정)
|
||||
bodyParser: false,
|
||||
});
|
||||
// 어댑터는 createHttpAdapter — 라우팅이 대소문자를 가린다(어댑터를 만들 때 켜야 한다 — app.setup.ts). e2e 도 같은 함수를 쓴다
|
||||
const app = await NestFactory.create<NestExpressApplication>(
|
||||
AppModule,
|
||||
createHttpAdapter(),
|
||||
{
|
||||
logger: WinstonModule.createLogger(winstonConfig),
|
||||
// 본문 파서는 configureApp 이 직접 등록한다 (JSON 만, 크기 한도 지정)
|
||||
bodyParser: false,
|
||||
},
|
||||
);
|
||||
|
||||
// 기본 주소('/') 접속 시 상태 확인용 텍스트 응답 (컨테이너 healthcheck 가 읽는다)
|
||||
const expressApp = app.getHttpAdapter().getInstance();
|
||||
|
||||
Reference in New Issue
Block a user