GET/api/attachments
API 라우트: POST|GET /api/attachments
API 라우트: POST|GET /api/attachments
첨부파일 메타데이터 등록(POST)과 목록 조회(GET)를 담당한다.
실제 파일 바이너리는 이 라우트가 처리하지 않는다 —
클라이언트가 MinIO presigned PUT URL로 직접 업로드한 뒤, 이 엔드포인트로 메타를 등록한다.
첨부 대상(attachable): issue | page | comment
최대 파일 크기: 50 MB (createSchema sizeBytes 상한)
권한: JWT 로그인 필수 + 해당 프로젝트 멤버십 필수.
@author jijuta ([email protected])
@since 2026-05-03
@updated 2026-06-07 — JSDoc 표준(@author/@since/@param 등) 적용
@updated 2026-06-07 — #41 storagePath 가 projectId prefix 로 시작하는지 검증(cross-project IDOR 차단)
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X GET https://sit.in-bridge.com/api/attachments \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/attachments", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
POST/api/attachments
API 라우트: POST|GET /api/attachments
API 라우트: POST|GET /api/attachments
첨부파일 메타데이터 등록(POST)과 목록 조회(GET)를 담당한다.
실제 파일 바이너리는 이 라우트가 처리하지 않는다 —
클라이언트가 MinIO presigned PUT URL로 직접 업로드한 뒤, 이 엔드포인트로 메타를 등록한다.
첨부 대상(attachable): issue | page | comment
최대 파일 크기: 50 MB (createSchema sizeBytes 상한)
권한: JWT 로그인 필수 + 해당 프로젝트 멤버십 필수.
@author jijuta ([email protected])
@since 2026-05-03
@updated 2026-06-07 — JSDoc 표준(@author/@since/@param 등) 적용
@updated 2026-06-07 — #41 storagePath 가 projectId prefix 로 시작하는지 검증(cross-project IDOR 차단)
Request body
projectIdstring<uuid>required
storagePathstringrequired
attachableTypeanyrequired
attachableIdstring<uuid>required
Responses
200 성공400 입력 검증 실패(zod)401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X POST https://sit.in-bridge.com/api/attachments \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"projectId":"00000000-0000-0000-0000-000000000000","name":"string","mimeType":"string","sizeBytes":null,"storagePath":"string","attachableType":null,"attachableId":"00000000-0000-0000-0000-000000000000"}'
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/attachments", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"projectId": "00000000-0000-0000-0000-000000000000",
"name": "string",
"mimeType": "string",
"sizeBytes": null,
"storagePath": "string",
"attachableType": null,
"attachableId": "00000000-0000-0000-0000-000000000000"
})
});
const data = await res.json();
GET/api/attachments/{attachmentId}
API 라우트: GET|DELETE /api/attachments/[attachmentId]
API 라우트: GET|DELETE /api/attachments/[attachmentId]
개별 첨부파일에 대한 두 가지 작업을 담당한다.
- GET : MinIO presigned URL(15분 유효)을 발급해 파일 다운로드 링크를 반환.
- DELETE: MinIO 오브젝트와 DB 메타데이터를 모두 삭제하고 활동 로그 기록.
권한: JWT 로그인 필수 + 해당 첨부파일이 속한 프로젝트 멤버십 필수.
경로 파라미터: attachmentId (UUID)
@author jijuta ([email protected])
@since 2026-05-03
@updated 2026-06-07 — JSDoc 표준(@author/@since/@param 등) 적용
Path parameters
attachmentIdstringrequired
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X GET https://sit.in-bridge.com/api/attachments/{attachmentId} \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/attachments/{attachmentId}", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
DELETE/api/attachments/{attachmentId}
API 라우트: GET|DELETE /api/attachments/[attachmentId]
API 라우트: GET|DELETE /api/attachments/[attachmentId]
개별 첨부파일에 대한 두 가지 작업을 담당한다.
- GET : MinIO presigned URL(15분 유효)을 발급해 파일 다운로드 링크를 반환.
- DELETE: MinIO 오브젝트와 DB 메타데이터를 모두 삭제하고 활동 로그 기록.
권한: JWT 로그인 필수 + 해당 첨부파일이 속한 프로젝트 멤버십 필수.
경로 파라미터: attachmentId (UUID)
@author jijuta ([email protected])
@since 2026-05-03
@updated 2026-06-07 — JSDoc 표준(@author/@since/@param 등) 적용
Path parameters
attachmentIdstringrequired
Responses
200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X DELETE https://sit.in-bridge.com/api/attachments/{attachmentId} \
-H "Authorization: Bearer $TOKEN"
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/attachments/{attachmentId}", {
method: "DELETE",
headers: {
"Authorization": `Bearer ${token}`
}
});
const data = await res.json();
POST/api/attachments/presigned
POST /api/attachments/presigned
Request body
projectIdstring<uuid>required
contentTypestringrequired
Responses
200 성공400 입력 검증 실패(zod)401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURLcurl -X POST https://sit.in-bridge.com/api/attachments/presigned \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"projectId":"00000000-0000-0000-0000-000000000000","filename":"string","contentType":"string","sizeBytes":null}'
JavaScriptconst res = await fetch("https://sit.in-bridge.com/api/attachments/presigned", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"projectId": "00000000-0000-0000-0000-000000000000",
"filename": "string",
"contentType": "string",
"sizeBytes": null
})
});
const data = await res.json();