{"openapi":"3.0.0","paths":{"/":{"get":{"operationId":"AppController_getHello","parameters":[],"responses":{"200":{"description":""}},"tags":["App"]}},"/uploads/presign":{"post":{"description":"Allowed key prefixes: \"projects/\". Allowed content types: image/jpeg, image/png, image/webp. Recommended max file size: 10 MB (not enforced server-side via presigned URL).","operationId":"UploadsController_presign","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignDto"}}}},"responses":{"201":{"description":"Returns presigned PUT URL and public URL","content":{"application/json":{"schema":{"properties":{"presignedUrl":{"type":"string","description":"Presigned PUT URL (1 hour expiry)"},"publicUrl":{"type":"string","description":"Permanent public URL of the object"}}}}}},"400":{"description":"Validation error (invalid key prefix, content type, or path traversal)"}},"summary":"Generate a presigned PUT URL for S3 upload","tags":["uploads"]}},"/uploads/presign-get":{"post":{"description":"Returns a presigned URL valid for 10 minutes that allows reading a private S3 object.","operationId":"UploadsController_presignGet","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignGetDto"}}}},"responses":{"201":{"description":"Returns presigned GET URL","content":{"application/json":{"schema":{"properties":{"presignedUrl":{"type":"string","description":"Presigned GET URL (10 minute expiry)"}}}}}},"400":{"description":"Validation error"}},"summary":"Generate a presigned GET URL for a private S3 object","tags":["uploads"]}},"/projects":{"post":{"operationId":"ProjectsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectDto"}}}},"responses":{"201":{"description":"Project created"}},"summary":"Create a project","tags":["projects"]},"get":{"operationId":"ProjectsController_findAll","parameters":[],"responses":{"200":{"description":""}},"summary":"List all projects","tags":["projects"]}},"/projects/{id}":{"get":{"operationId":"ProjectsController_findOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"404":{"description":"Project not found"}},"summary":"Get a project by ID","tags":["projects"]}},"/projects/{id}/rooms":{"post":{"operationId":"ProjectsController_createRoom","parameters":[{"name":"id","required":true,"in":"path","description":"Project ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRoomDto"}}}},"responses":{"201":{"description":"Room created"},"404":{"description":"Project not found"}},"summary":"Add a room to a project","tags":["projects"]},"get":{"operationId":"ProjectsController_findRooms","parameters":[{"name":"id","required":true,"in":"path","description":"Project ID","schema":{"type":"string"}}],"responses":{"404":{"description":"Project not found"}},"summary":"List rooms for a project","tags":["projects"]}},"/photos":{"post":{"operationId":"PhotosController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePhotoDto"}}}},"responses":{"201":{"description":"Photo record created"}},"summary":"Save photo metadata after S3 upload","tags":["photos"]}},"/rooms/{id}/photos":{"get":{"operationId":"PhotosController_findByRoom","parameters":[{"name":"id","required":true,"in":"path","description":"Room ID","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"List photos for a room (includes 10-min presigned viewUrl)","tags":["photos"]}},"/auth/register":{"post":{"operationId":"AuthController_register","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDto"}}}},"responses":{"201":{"description":"JWT accessToken과 사용자 정보 반환"},"409":{"description":"이미 사용 중인 이메일"}},"summary":"회원가입","tags":["auth"]}},"/auth/login":{"post":{"operationId":"AuthController_login","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginDto"}}}},"responses":{"201":{"description":"JWT accessToken과 사용자 정보 반환"},"401":{"description":"이메일 또는 비밀번호 불일치"}},"summary":"로그인","tags":["auth"]}},"/auth/me":{"get":{"operationId":"AuthController_me","parameters":[],"responses":{"200":{"description":"현재 로그인된 사용자 정보"},"401":{"description":"인증 필요"}},"security":[{"bearer":[]}],"summary":"내 정보 조회","tags":["auth"]}},"/auth/me/password":{"post":{"operationId":"AuthController_changePassword","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordDto"}}}},"responses":{"201":{"description":"비밀번호 변경 완료"},"401":{"description":"인증 필요 또는 현재 비밀번호 불일치"}},"security":[{"bearer":[]}],"summary":"비밀번호 변경","tags":["auth"]}},"/estimates/anonymous":{"post":{"operationId":"EstimatesController_createAnonymous","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEstimateDto"}}}},"responses":{"201":{"description":"견적 저장 완료"}},"summary":"비로그인 견적 저장","tags":["estimates"]}},"/estimates":{"post":{"operationId":"EstimatesController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEstimateDto"}}}},"responses":{"201":{"description":"견적 저장 완료"}},"security":[{"bearer":[]}],"summary":"내 견적 저장","tags":["estimates"]},"get":{"operationId":"EstimatesController_findAll","parameters":[{"name":"projectId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"내 견적 목록"}},"security":[{"bearer":[]}],"summary":"내 견적 목록 조회","tags":["estimates"]}},"/estimates/{id}":{"get":{"operationId":"EstimatesController_findOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"404":{"description":"견적 없음 또는 접근 권한 없음"}},"security":[{"bearer":[]}],"summary":"내 견적 단건 조회","tags":["estimates"]},"put":{"operationId":"EstimatesController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEstimateDto"}}}},"responses":{"200":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"내 견적 수정","tags":["estimates"]},"delete":{"operationId":"EstimatesController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"내 견적 삭제","tags":["estimates"]}},"/ai-design/demo":{"post":{"operationId":"AiDesignController_createDemo","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"image":{"type":"string","format":"binary"},"prompt":{"type":"string"},"phone":{"type":"string","description":"완료 알림 수신 전화번호 (선택)"},"marketingConsent":{"type":"string","enum":["true","false"],"description":"마케팅 수신 동의 (선택)"}},"required":["image","prompt"]}}}},"responses":{"201":{"description":"{ imageBase64: string }"}},"summary":"AI 인테리어 데모 — 비로그인, 결과 저장 없음","tags":["ai-design"]}},"/ai-design/estimate-preview":{"post":{"operationId":"AiDesignController_createEstimatePreview","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"image":{"type":"string","format":"binary","description":"시공 전 사진 (선택)"},"buildingType":{"type":"string","description":"건물 유형"},"space":{"type":"string","description":"시각화할 공간"},"selectedCategories":{"type":"string","description":"선택 카테고리/자재 JSON (MaterialDetail[])"},"tier":{"type":"string","description":"등급 (budget/standard/premium)"},"area":{"type":"string","description":"평수 (선택)"}},"required":["buildingType","space","selectedCategories","tier"]}}}},"responses":{"201":{"description":"{ imageBase64: string }"}},"summary":"견적 기반 AI 시공 미리보기 — 비로그인, 결과 저장 없음","tags":["ai-design"]}},"/ai-design/estimate-preview-stream":{"post":{"operationId":"AiDesignController_createEstimatePreviewStream","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"image":{"type":"string","format":"binary","description":"시공 전 사진 (선택)"},"buildingType":{"type":"string"},"space":{"type":"string"},"selectedCategories":{"type":"string","description":"MaterialDetail[] JSON"},"tier":{"type":"string"},"area":{"type":"string"}},"required":["buildingType","space","selectedCategories","tier"]}}}},"responses":{"201":{"description":""}},"summary":"견적 기반 AI 시공 미리보기 (SSE 스트리밍) — 단계별 진행 상태 포함","tags":["ai-design"]}},"/ai-design":{"post":{"operationId":"AiDesignController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAiDesignDto"}}}},"responses":{"201":{"description":"생성 완료 (viewUrl 포함)"}},"security":[{"bearer":[]}],"summary":"AI 인테리어 이미지 생성 (로그인 필요, DB 저장)","tags":["ai-design"]}},"/photos/{photoId}/ai-designs":{"get":{"operationId":"AiDesignController_findByPhoto","parameters":[{"name":"photoId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"사진의 AI 디자인 이력 조회","tags":["ai-design"]}},"/construction-cases":{"get":{"operationId":"ConstructionCasesController_findPublic","parameters":[],"responses":{"200":{"description":""}},"summary":"공개 시공사례 목록 (isVisible=true)","tags":["construction-cases"]},"post":{"operationId":"ConstructionCasesController_create","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConstructionCaseDto"}}}},"responses":{"201":{"description":""}},"summary":"[Admin] 시공사례 등록","tags":["construction-cases"]}},"/construction-cases/all":{"get":{"operationId":"ConstructionCasesController_findAll","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"[Admin] 전체 시공사례 목록","tags":["construction-cases"]}},"/construction-cases/{id}":{"get":{"operationId":"ConstructionCasesController_findOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"시공사례 단건 조회 (isVisible=true)","tags":["construction-cases"]},"put":{"operationId":"ConstructionCasesController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateConstructionCaseDto"}}}},"responses":{"200":{"description":""}},"summary":"[Admin] 시공사례 수정","tags":["construction-cases"]},"delete":{"operationId":"ConstructionCasesController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"[Admin] 시공사례 삭제","tags":["construction-cases"]}},"/admin/stats":{"get":{"operationId":"AdminController_getStats","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"]}},"/admin/users":{"get":{"operationId":"AdminController_getUsers","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"]}},"/admin/stats/trend":{"get":{"operationId":"AdminController_getStatsTrend","parameters":[{"name":"period","required":true,"in":"query","schema":{"type":"string"}},{"name":"days","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Admin"]}},"/admin/stats/estimates":{"get":{"operationId":"AdminController_getEstimateStats","parameters":[],"responses":{"200":{"description":""}},"tags":["Admin"]}},"/consultations":{"post":{"operationId":"ConsultationsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConsultationDto"}}}},"responses":{"201":{"description":""}},"tags":["consultations"]}},"/admin/consultations":{"get":{"operationId":"ConsultationsController_findAll","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"startDate","required":true,"in":"query","schema":{"type":"string"}},{"name":"endDate","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["consultations"]}},"/admin/consultations/stats":{"get":{"operationId":"ConsultationsController_getStats","parameters":[],"responses":{"200":{"description":""}},"tags":["consultations"]}},"/admin/consultations/{id}":{"put":{"operationId":"ConsultationsController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateConsultationDto"}}}},"responses":{"200":{"description":""}},"tags":["consultations"]}},"/diagnosis/count":{"get":{"operationId":"DiagnosisController_getCount","parameters":[],"responses":{"200":{"description":"{ count: number }"}},"summary":"진단 신청 총 횟수 조회","tags":["diagnosis"]}},"/diagnosis/requests":{"get":{"operationId":"DiagnosisController_getRequests","parameters":[{"name":"page","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"진단 요청 내역 목록 (어드민)","tags":["diagnosis"]}},"/diagnosis":{"post":{"operationId":"DiagnosisController_analyze","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"string","format":"binary"},"description":"계약서/견적서 파일들 (최대 10개, 각 10MB)"},"chatLogs":{"type":"array","items":{"type":"string","format":"binary"},"description":"카카오톡 대화 내역 .txt 파일들 (최대 5개)"},"recordings":{"type":"array","items":{"type":"string","format":"binary"},"description":"통화 녹음 파일들 (최대 3개, 각 50MB)"}},"required":["files"]}}}},"responses":{"201":{"description":"진단 결과 JSON"}},"summary":"AI 종합 계약 진단 — 비로그인, 결과 저장 없음","tags":["diagnosis"]}},"/diagnosis/stream":{"post":{"operationId":"DiagnosisController_analyzeStream","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"string","format":"binary"},"description":"계약서/견적서 파일들 (최대 10개, 각 10MB)"},"chatLogs":{"type":"array","items":{"type":"string","format":"binary"},"description":"카카오톡 대화 내역 .txt 파일들 (최대 5개)"},"recordings":{"type":"array","items":{"type":"string","format":"binary"},"description":"통화 녹음 파일들 (최대 3개, 각 50MB)"}},"required":["files"]}}}},"responses":{"201":{"description":""}},"summary":"AI 종합 진단 (SSE 스트리밍) — 진행 상태 실시간 전송","tags":["diagnosis"]}},"/materials":{"get":{"operationId":"MaterialsController_findPublic","parameters":[{"name":"category","required":false,"in":"query","schema":{"example":"flooring","type":"string"}},{"name":"subcategory","required":false,"in":"query","schema":{"example":"강마루","type":"string"}},{"name":"search","required":false,"in":"query","schema":{"type":"string"}},{"name":"brand","required":false,"in":"query","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"example":1,"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"example":20,"type":"string"}}],"responses":{"200":{"description":""}},"summary":"공개 자재 목록 (필터/검색/페이지네이션)","tags":["materials"]},"post":{"operationId":"MaterialsController_create","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMaterialDto"}}}},"responses":{"201":{"description":""}},"summary":"[Admin] 자재 등록","tags":["materials"]}},"/materials/my-likes":{"get":{"operationId":"MaterialsController_getMyLikes","parameters":[],"responses":{"200":{"description":"좋아요한 자재 ID 배열"}},"security":[{"bearer":[]}],"summary":"내 좋아요 자재 ID 목록","tags":["materials"]}},"/materials/all":{"get":{"operationId":"MaterialsController_findAll","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"[Admin] 전체 자재 목록","tags":["materials"]}},"/materials/excel/download":{"get":{"operationId":"MaterialsController_downloadExcel","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"[Admin] 자재 Excel 다운로드","tags":["materials"]}},"/materials/excel/upload":{"post":{"operationId":"MaterialsController_uploadExcel","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"[Admin] 자재 Excel 업로드 (일괄 등록/수정)","tags":["materials"]}},"/materials/{id}/view":{"post":{"operationId":"MaterialsController_incrementView","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"404":{"description":"자재 없음"}},"summary":"자재 조회수 증가","tags":["materials"]}},"/materials/{id}/like":{"post":{"operationId":"MaterialsController_toggleLike","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"{ likeCount, isLiked }"},"401":{"description":"인증 필요"},"404":{"description":"자재 없음"}},"security":[{"bearer":[]}],"summary":"자재 좋아요 토글 (로그인 필수)","tags":["materials"]}},"/materials/{id}/unlike":{"post":{"operationId":"MaterialsController_decrementLike","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"404":{"description":"자재 없음"}},"summary":"자재 좋아요 취소 (비로그인 호환)","tags":["materials"]}},"/materials/{id}":{"get":{"operationId":"MaterialsController_findOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""},"404":{"description":"자재 없음"}},"summary":"자재 단건 조회 (isVisible=true)","tags":["materials"]},"put":{"operationId":"MaterialsController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMaterialDto"}}}},"responses":{"200":{"description":""}},"summary":"[Admin] 자재 수정","tags":["materials"]},"delete":{"operationId":"MaterialsController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"[Admin] 자재 삭제","tags":["materials"]}},"/satisfaction":{"post":{"operationId":"SatisfactionController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSatisfactionSurveyDto"}}}},"responses":{"201":{"description":""}},"summary":"만족도 설문 제출 (공개)","tags":["satisfaction"]}},"/admin/satisfaction":{"get":{"operationId":"SatisfactionController_findAll","parameters":[{"name":"startDate","required":false,"in":"query","schema":{"type":"string"}},{"name":"endDate","required":false,"in":"query","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"example":1,"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"example":20,"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"[Admin] 만족도 설문 목록","tags":["satisfaction"]}},"/admin/satisfaction/stats":{"get":{"operationId":"SatisfactionController_getStats","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"[Admin] 만족도 통계","tags":["satisfaction"]}},"/admin/satisfaction/{id}":{"delete":{"operationId":"SatisfactionController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"[Admin] 만족도 설문 삭제","tags":["satisfaction"]}},"/admin/scraper/sites":{"get":{"operationId":"ScraperController_getSites","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"등록된 크롤링 사이트 목록","tags":["Admin - Scraper"]}},"/admin/scraper/run":{"post":{"operationId":"ScraperController_startScrape","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartScrapeDto"}}}},"responses":{"201":{"description":""}},"summary":"크롤링 시작 (비동기)","tags":["Admin - Scraper"]}},"/admin/scraper/jobs":{"get":{"operationId":"ScraperController_listJobs","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"summary":"작업 목록 조회","tags":["Admin - Scraper"]}},"/admin/scraper/jobs/{id}":{"get":{"operationId":"ScraperController_getJob","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"작업 상태 조회","tags":["Admin - Scraper"]}},"/admin/scraper/jobs/{id}/cancel":{"post":{"operationId":"ScraperController_cancelJob","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"실행 중 작업 취소","tags":["Admin - Scraper"]}},"/ai-chat/stream":{"post":{"operationId":"AiChatController_streamChat","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiChatDto"}}}},"responses":{"201":{"description":""}},"summary":"AI 인테리어 상담 (스트리밍)","tags":["ai-chat"]}},"/admin/contractors":{"post":{"operationId":"ContractorsController_createContractor","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"시공팀 등록","tags":["contractors"]},"get":{"operationId":"ContractorsController_findAllContractors","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"시공팀 전체 목록 (어드민)","tags":["contractors"]}},"/admin/contractors/{id}":{"get":{"operationId":"ContractorsController_findContractor","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"시공팀 상세 조회","tags":["contractors"]},"put":{"operationId":"ContractorsController_updateContractor","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"시공팀 수정","tags":["contractors"]},"delete":{"operationId":"ContractorsController_deleteContractor","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"시공팀 삭제","tags":["contractors"]}},"/admin/contractor-matches":{"post":{"operationId":"ContractorsController_assignContractor","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"상담에 시공팀 배정","tags":["contractors"]},"get":{"operationId":"ContractorsController_findAllMatches","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"매칭 전체 목록","tags":["contractors"]}},"/admin/contractor-matches/stats":{"get":{"operationId":"ContractorsController_getMatchStats","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"매칭 통계","tags":["contractors"]}},"/admin/contractor-matches/{id}":{"get":{"operationId":"ContractorsController_findMatch","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"매칭 상세 조회","tags":["contractors"]},"put":{"operationId":"ContractorsController_updateMatch","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"매칭 상태 업데이트","tags":["contractors"]}},"/admin/contractor-matches/consultation/{consultationId}":{"get":{"operationId":"ContractorsController_findMatchesByConsultation","parameters":[{"name":"consultationId","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"상담별 매칭 목록","tags":["contractors"]}},"/contractors":{"get":{"operationId":"ContractorsController_findActiveContractors","parameters":[],"responses":{"200":{"description":""}},"summary":"활성 시공팀 목록 (공개)","tags":["contractors"]}},"/ai-agent/action":{"post":{"description":"AI Agent가 PlanV2 기능을 호출하는 통합 엔드포인트입니다.\n\n**방법 1: 자연어 입력**\n`{ \"naturalLanguage\": \"25평 아파트 욕실+주방 리모델링 견적 알려줘\" }`\n\n**방법 2: 구조화된 액션**\n`{ \"action\": \"estimate\", \"params\": { \"area\": 25, \"tier\": \"standard\", \"categories\": [\"kitchen\",\"bathroom\"], \"buildingType\": \"apartment\" } }`\n\n**사용 가능한 액션:**\n- `estimate` — 견적 계산\n- `materials_search` — 자재 검색\n- `contractors` — 시공팀 조회\n- `cases` — 시공사례 조회\n- `info` — 서비스 정보","operationId":"AiAgentController_handleAction","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"naturalLanguage":{"type":"string","description":"자연어 요청 (한국어)"},"action":{"type":"string","description":"액션 이름"},"params":{"type":"object","description":"액션 파라미터"}}}}}},"responses":{"200":{"description":"액션 실행 결과"}},"summary":"AI Agent 통합 액션 — 자연어 또는 구조화된 요청","tags":["ai-agent"]}},"/ai-agent/estimate":{"get":{"description":"예시: /ai-agent/estimate?area=30&tier=standard&categories=kitchen,bathroom&buildingType=apartment","operationId":"AiAgentController_getEstimate","parameters":[{"name":"area","required":true,"in":"query","description":"면적 (평수)","schema":{"type":"number"}},{"name":"tier","required":true,"in":"query","description":"등급","schema":{"enum":["budget","standard","premium"],"type":"string"}},{"name":"categories","required":true,"in":"query","description":"공사항목 (쉼표 구분)","schema":{"type":"string"}},{"name":"buildingType","required":true,"in":"query","description":"건물유형","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"견적 계산 (GET) — ChatGPT/AI가 URL로 바로 호출 가능","tags":["ai-agent"]}},"/ai-agent/materials":{"get":{"operationId":"AiAgentController_getMaterials","parameters":[{"name":"search","required":false,"in":"query","schema":{"type":"string"}},{"name":"brand","required":false,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"summary":"자재 검색 (GET) — ChatGPT/AI가 URL로 바로 호출 가능","tags":["ai-agent"]}},"/ai-agent/contractors":{"get":{"operationId":"AiAgentController_getContractors","parameters":[],"responses":{"200":{"description":""}},"summary":"시공팀 조회 (GET) — ChatGPT/AI가 URL로 바로 호출 가능","tags":["ai-agent"]}},"/ai-agent/info":{"get":{"operationId":"AiAgentController_getInfo","parameters":[],"responses":{"200":{"description":""}},"summary":"서비스 정보 (GET)","tags":["ai-agent"]}},"/ai-agent/estimate-page":{"get":{"description":"예시: /ai-agent/estimate-page?area=30&tier=standard&categories=kitchen,bathroom&buildingType=apartment","operationId":"AiAgentController_getEstimatePage","parameters":[{"name":"area","required":true,"in":"query","schema":{"type":"string"}},{"name":"tier","required":true,"in":"query","schema":{"type":"string"}},{"name":"categories","required":true,"in":"query","schema":{"type":"string"}},{"name":"buildingType","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"AI 읽기용 견적 HTML 페이지 — ChatGPT 웹 브라우징으로 접근 가능","tags":["ai-agent"]}},"/ai-agent/schema":{"get":{"operationId":"AiAgentController_getSchema","parameters":[],"responses":{"200":{"description":""}},"summary":"AI Agent용 기능 스키마 — 사용 가능한 액션과 파라미터 정의","tags":["ai-agent"]}},"/color-simulator/colors":{"get":{"operationId":"ColorSimulatorController_findPublic","parameters":[],"responses":{"200":{"description":""}},"summary":"공개 색상 목록 (visible only)","tags":["color-simulator"]},"post":{"operationId":"ColorSimulatorController_create","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSimulatorColorDto"}}}},"responses":{"201":{"description":""}},"summary":"색상 추가","tags":["color-simulator"]}},"/color-simulator/colors/all":{"get":{"operationId":"ColorSimulatorController_findAll","parameters":[{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"어드민 전체 색상 목록","tags":["color-simulator"]}},"/color-simulator/colors/{id}":{"put":{"operationId":"ColorSimulatorController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSimulatorColorDto"}}}},"responses":{"200":{"description":""}},"summary":"색상 수정","tags":["color-simulator"]},"delete":{"operationId":"ColorSimulatorController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"x-admin-key","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"색상 삭제","tags":["color-simulator"]}}},"info":{"title":"PlanV2 API","description":"PlanV2 — AI 기반 인테리어 견적·시각화 플랫폼 API\n\n## 주요 기능\n- **견적 자동 산출**: 건물유형, 평수, 공사항목 선택 → 즉시 예상 견적\n- **자재 카탈로그**: 200+ 자재 검색, 상세 조회 (이미지/가격/스펙)\n- **시공팀 매칭**: 검증된 협력 시공팀 조회 및 상담 연결\n- **AI 시공 미리보기**: 선택 자재로 시공 후 모습 AI 이미지 생성\n- **AI 계약서 진단**: PDF/Word 업로드 → 항목별 자동 분석\n- **시공사례 갤러리**: 실제 시공 포트폴리오\n\n## AI Agent 연동\n- OpenAPI JSON: `/api-json`\n- MCP Server: `packages/mcp-server`\n- AI 전용 엔드포인트: `/ai-agent/action`\n\n## 인증\n- 공개 API: 인증 불필요 (자재 조회, 시공팀 목록, 시공사례 등)\n- 회원 API: JWT Bearer Token\n- 어드민 API: `x-admin-key` 헤더","version":"2.0","contact":{}},"tags":[],"servers":[{"url":"https://planv2.com/api-backend","description":"Production"},{"url":"http://localhost:3001","description":"Local Development"}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"PresignDto":{"type":"object","properties":{"key":{"type":"string","example":"projects/photo.jpg","description":"Object key. Must start with \"projects/\". Must not contain \"..\" or start with \"/\"."},"contentType":{"type":"string","example":"image/jpeg","enum":["image/jpeg","image/png","image/webp"],"description":"Allowed content types: image/jpeg, image/png, image/webp. Recommended max file size: 10 MB (not enforced server-side via presigned URL)."}},"required":["key","contentType"]},"PresignGetDto":{"type":"object","properties":{"key":{"type":"string","example":"projects/photo.jpg"}},"required":["key"]},"CreateProjectDto":{"type":"object","properties":{"name":{"type":"string","example":"강남 아파트 리모델링"},"region":{"type":"string","example":"서울 강남구"},"areaPyeong":{"type":"number","example":25.74,"description":"면적 (평)"}},"required":["name","region","areaPyeong"]},"CreateRoomDto":{"type":"object","properties":{"type":{"type":"string","enum":["living","kitchen","bath","bed","etc"],"example":"living"},"name":{"type":"string","example":"안방"}},"required":["type"]},"CreatePhotoDto":{"type":"object","properties":{"roomId":{"type":"string","example":"uuid-of-room"},"s3Key":{"type":"string","example":"projects/1234567890-photo.jpg"},"contentType":{"type":"string","enum":["image/jpeg","image/png","image/webp"],"example":"image/jpeg"}},"required":["roomId","s3Key","contentType"]},"RegisterDto":{"type":"object","properties":{"loginId":{"type":"string","example":"hong_gildong","description":"로그인 아이디 (4~20자, 영문·숫자·밑줄)"},"email":{"type":"string","example":"user@example.com"},"password":{"type":"string","example":"password123","description":"비밀번호 (최소 8자)"}},"required":["loginId","email","password"]},"LoginDto":{"type":"object","properties":{"email":{"type":"string","example":"user@example.com"},"password":{"type":"string","example":"password123"}},"required":["email","password"]},"ChangePasswordDto":{"type":"object","properties":{"currentPassword":{"type":"string","description":"현재 비밀번호"},"newPassword":{"type":"string","description":"새 비밀번호 (최소 8자)"}},"required":["currentPassword","newPassword"]},"CreateEstimateDto":{"type":"object","properties":{"title":{"type":"string","example":"강남 아파트 리모델링 견적"},"buildingType":{"type":"string","enum":["apartment","villa","house","officetel","office","commercial"],"example":"apartment"},"areaPreset":{"type":"object","example":"21-25"},"areaCustom":{"type":"string","example":""},"useCustomArea":{"type":"boolean","example":false},"categories":{"type":"object","description":"Record<CategoryId, { selected: boolean; optionId: string }>"},"selectedSpaces":{"example":["kitchen","bath"],"type":"array","items":{"type":"string"}},"otherSpace":{"type":"string","example":""},"budget":{"type":"object","example":3000},"tier":{"type":"string","enum":["budget","standard","premium"],"example":"standard"},"totalPrice":{"type":"number","example":1850,"description":"계산된 총 견적 금액 (만원)"},"projectId":{"type":"object","example":"uuid-of-project"}},"required":["title","buildingType","areaCustom","useCustomArea","categories","selectedSpaces","otherSpace","tier","totalPrice"]},"UpdateEstimateDto":{"type":"object","properties":{"title":{"type":"string","example":"강남 아파트 리모델링 견적"},"buildingType":{"type":"string","enum":["apartment","villa","house","officetel","office","commercial"],"example":"apartment"},"areaPreset":{"type":"object","example":"21-25"},"areaCustom":{"type":"string","example":""},"useCustomArea":{"type":"boolean","example":false},"categories":{"type":"object","description":"Record<CategoryId, { selected: boolean; optionId: string }>"},"selectedSpaces":{"example":["kitchen","bath"],"type":"array","items":{"type":"string"}},"otherSpace":{"type":"string","example":""},"budget":{"type":"object","example":3000},"tier":{"type":"string","enum":["budget","standard","premium"],"example":"standard"},"totalPrice":{"type":"number","example":1850,"description":"계산된 총 견적 금액 (만원)"},"projectId":{"type":"object","example":"uuid-of-project"}}},"CreateAiDesignDto":{"type":"object","properties":{"photoId":{"type":"string","example":"uuid-of-photo"},"prompt":{"type":"string","example":"미니멀 북유럽 스타일, 밝고 따뜻한 분위기"}},"required":["photoId","prompt"]},"CreateConstructionCaseDto":{"type":"object","properties":{"title":{"type":"string","example":"강남구 아파트 30평 리모델링"},"description":{"type":"string","example":"기존 답답한 구조를 오픈형으로 변경하고 북유럽 무드를 더했습니다."},"area":{"type":"string","example":"30평"},"style":{"type":"string","example":"미니멀 모던"},"imageKey":{"type":"string","example":"construction-cases/uuid.jpg"},"sortOrder":{"type":"number","example":0}},"required":["title","imageKey"]},"UpdateConstructionCaseDto":{"type":"object","properties":{"title":{"type":"string","example":"강남구 아파트 30평 리모델링"},"description":{"type":"string","example":"기존 답답한 구조를 오픈형으로 변경하고 북유럽 무드를 더했습니다."},"area":{"type":"string","example":"30평"},"style":{"type":"string","example":"미니멀 모던"},"imageKey":{"type":"string","example":"construction-cases/uuid.jpg"},"sortOrder":{"type":"number","example":0}}},"CreateConsultationDto":{"type":"object","properties":{"name":{"type":"string","example":"홍길동"},"phone":{"type":"string","example":"010-1234-5678"},"message":{"type":"string","example":"주방 리모델링 상담 원합니다"},"estimateId":{"type":"string","example":"uuid-of-estimate"}},"required":["name","phone"]},"UpdateConsultationDto":{"type":"object","properties":{"status":{"type":"string","enum":["pending","contacted","completed","cancelled"]},"adminNote":{"type":"string"}}},"CreateMaterialDto":{"type":"object","properties":{"category":{"type":"string","enum":["flooring","wall","tile","kitchen","bathroom","door_window","lighting","electrical_plumbing","furniture","finish"],"example":"flooring"},"subcategory":{"type":"string","example":"마루"},"name":{"type":"string","example":"LG 하우시스 강마루 내추럴오크"},"brand":{"type":"string","example":"LG 하우시스"},"modelName":{"type":"string","example":"HEF2051-01"},"price":{"type":"number","example":45000,"description":"단가 (원)"},"unit":{"type":"string","example":"㎡"},"description":{"type":"string","example":"내구성이 뛰어난 강마루"},"imageKey":{"type":"string","example":"materials/uuid.jpg"},"specs":{"type":"object","description":"상세 스펙 (규격, 두께, 색상 등)"},"sortOrder":{"type":"number","example":0}},"required":["category","subcategory","name"]},"UpdateMaterialDto":{"type":"object","properties":{"category":{"type":"string","enum":["flooring","wall","tile","kitchen","bathroom","door_window","lighting","electrical_plumbing","furniture","finish"],"example":"flooring"},"subcategory":{"type":"string","example":"마루"},"name":{"type":"string","example":"LG 하우시스 강마루 내추럴오크"},"brand":{"type":"string","example":"LG 하우시스"},"modelName":{"type":"string","example":"HEF2051-01"},"price":{"type":"number","example":45000,"description":"단가 (원)"},"unit":{"type":"string","example":"㎡"},"description":{"type":"string","example":"내구성이 뛰어난 강마루"},"imageKey":{"type":"string","example":"materials/uuid.jpg"},"specs":{"type":"object","description":"상세 스펙 (규격, 두께, 색상 등)"},"sortOrder":{"type":"number","example":0}}},"CreateSatisfactionSurveyDto":{"type":"object","properties":{"npsScore":{"type":"number","example":8,"description":"NPS 점수 (0-10)"},"categoryRatings":{"type":"object","example":{"상담 만족도":4,"시공 품질":5,"가격 만족도":3,"소통/응대":4,"일정 준수":5},"description":"카테고리별 평점 (1-5)"},"comment":{"type":"string","example":"전반적으로 만족합니다."},"respondentName":{"type":"string","example":"홍길동"},"respondentPhone":{"type":"string","example":"010-1234-5678"}},"required":["npsScore","categoryRatings"]},"StartScrapeDto":{"type":"object","properties":{"siteName":{"type":"string","enum":["lxzin","ianmall","yongtile"]},"categories":{"description":"특정 카테고리만 크롤링","type":"array","items":{"type":"string"}},"delayMs":{"type":"number","description":"요청 간격 ms (기본 700)","default":700},"maxProducts":{"type":"number","description":"최대 제품 수 (테스트용)"}},"required":["siteName"]},"AiChatDto":{"type":"object","properties":{}},"CreateSimulatorColorDto":{"type":"object","properties":{"type":{"type":"string","example":"grout","enum":["grout","silicon"]},"name":{"type":"string","example":"화이트"},"hex":{"type":"string","example":"#FFFFFF"},"sortOrder":{"type":"number","example":0}},"required":["type","name","hex"]},"UpdateSimulatorColorDto":{"type":"object","properties":{"type":{"type":"string","example":"grout","enum":["grout","silicon"]},"name":{"type":"string","example":"화이트"},"hex":{"type":"string","example":"#FFFFFF"},"sortOrder":{"type":"number","example":0},"isVisible":{"type":"boolean"}},"required":["type","name","hex"]}}}}