diff --git a/AGENTS.md b/AGENTS.md index 65342db..2518695 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,8 @@ # AGENTS.md — 이 레포에서 작업하기 전에 +> 2026-09-15 발행 버전 전환: [docs/PUBLISH_VERSION.md](docs/PUBLISH_VERSION.md)가 +> 아래의 상시 프리렌더·재굽기·자산 주소 교체 절차를 대체한다. 목업은 변경하지 않는다. + 에이전트와 신규 합류자가 **먼저 읽는 파일**이다. 여기에는 *밟기 쉬운 함정*과 *규약*만 둔다. 설명은 각 문서가 단일 출처다 — 여기로 복사하지 말고 링크한다. diff --git a/deploy.sh b/deploy.sh index ba8c083..73738d8 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,127 +1,40 @@ #!/usr/bin/env bash -# 배포 — 코드를 당기고, 지정한 서비스만 다시 빌드해 갈아끼운다. -# -# ./deploy.sh 전체 -# ./deploy.sh solution-backend 그 서비스만 -# ./deploy.sh solution-backend solution-worker 여럿 -# -# 서비스명 대신 컨테이너명(o2o-web4ai-solution-backend)으로 불러도 받는다. +# 서버 변경과 꺼진 admin을 보존한다. 렌더러 배포도 기존 사이트를 재굽지 않는다. set -euo pipefail cd "$(dirname "$0")" - -PREFIX=o2o-web4ai -# api·worker·api-admin 은 이미지 한 벌(o2o-web4ai-backend)을 나눠 쓴다. -BACKEND_SVCS=(solution-backend solution-worker admin-backend) -BRANCH=${DEPLOY_BRANCH:-main} - PULL=1 ONLY=0 TARGETS=() - -usage() { - cat <<'USAGE' -사용법: ./deploy.sh [옵션] [서비스...] - - 옵션 - --no-pull 코드를 당기지 않는다(디스크에 있는 코드 그대로 빌드) - --only 백엔드 형제 서비스를 함께 갈아끼우지 않는다 (아래 ★ 참고) - -h, --help - - 환경변수 - DEPLOY_BRANCH 기본 main. 다른 브랜치를 배포할 때만 쓴다 - - 서비스: solution-backend · solution-worker · solution-prerender · solution-site - admin-backend · admin-frontend (프로필 admin, 기본 기동에서 빠져 있다) - solution-frontend (프로필 dev, 로컬 HMR 전용 — 운영에서 띄우지 않는다) - o2o-web4ai-solution-backend 처럼 컨테이너명으로 적어도 된다 - -★ solution-backend·solution-worker·admin-backend 는 이미지가 한 벌이다. 하나를 빌드하면 나머지도 새 이미지로 - 갈아끼워야 한다 — 안 그러면 옛 코드로 도는 컨테이너가 남는데, 셋 다 "살아 있음" 이라 - 화면상으로는 배포가 끝난 것처럼 보인다. --only 는 그걸 알고 건너뛸 때만 쓴다. -USAGE -} - -while [ $# -gt 0 ]; do - case "$1" in +for arg in "$@"; do + case "$arg" in --no-pull) PULL=0 ;; - --only) ONLY=1 ;; - -h|--help) usage; exit 0 ;; - -*) echo "모르는 옵션: $1" >&2; usage >&2; exit 2 ;; - *) TARGETS+=("${1#"$PREFIX"-}") ;; # 컨테이너명으로 불러도 받는다 + --only) ONLY=1 ;; + -h|--help) echo "사용법: ./deploy.sh [--no-pull] [--only] [서비스...]"; exit 0 ;; + -*) echo "모르는 옵션: $arg" >&2; exit 2 ;; + *) TARGETS+=("${arg#o2o-web4ai-}") ;; esac - shift done - -ALL_SVCS=$(docker compose config --services) -for t in ${TARGETS+"${TARGETS[@]}"}; do - grep -qx "$t" <<<"$ALL_SVCS" || { - echo "그런 서비스가 없다: $t" >&2 - echo "있는 것: $(tr '\n' ' ' <<<"$ALL_SVCS")" >&2 - exit 2 - } -done - -# ── 코드 ──────────────────────────────────────────────────────────── -# 배포 서버의 작업트리는 main 의 **사본**이지 작업 공간이 아니다. 그래서 머지(pull)가 아니라 -# origin/$BRANCH 로 하드 리셋한다 — 밖에서 force-push 가 나도 --ff-only 로 막히지 않고, -# 서버에서 누가 손댄 흔적이 다음 배포까지 살아남지 않는다. -# -# ★ fetch 가 실패하면 **리셋하지 않는다.** 이 서버엔 gitea 자격증명이 없어 fetch 가 죽는데, -# 그 상태의 origin/$BRANCH 는 마지막으로 fetch 된 낡은 ref 다. -# 실측(2026-09-01 킹서버): HEAD=9b4fe40 인데 origin/main=4871e50 — 믿고 리셋하면 한 커밋 -# 롤백된다. 그러고도 빌드는 성공하고 컨테이너는 뜬다. 조용히 틀리는 종류다. -# -# ★ git clean 은 하지 않는다. .env 와 nginx/site.conf 는 추적되지 않는 파일이고 서버마다 -# 다르다 — reset --hard 는 이 둘을 건드리지 않지만 clean 은 지운다. if [ "$PULL" = 1 ]; then - echo "▶ git fetch origin $BRANCH" - if git fetch --prune origin "$BRANCH" 2>&1; then - DIRTY=$(git status --porcelain) - if [ -n "$DIRTY" ]; then - echo " ! 작업트리 변경을 버린다:" - sed 's/^/ /' <<<"$DIRTY" - fi - echo "▶ git reset --hard origin/$BRANCH" - git reset --hard "origin/$BRANCH" - else - echo " ! fetch 실패 — 리셋을 건너뛰고 디스크에 있는 코드 그대로 간다." >&2 - echo " origin/$BRANCH 가 낡았을 수 있어 그걸로 리셋하면 배포가 조용히 롤백된다." >&2 - echo " (밖에서 밀어넣었으면 이대로 두면 되고, 자동화하려면 gitea 배포키를 건다)" >&2 - fi + git diff --quiet && git diff --cached --quiet || { echo "서버 변경을 먼저 정리하세요" >&2; exit 1; } + git pull --ff-only origin "${DEPLOY_BRANCH:-main}" fi -echo "▶ 지금 코드: $(git log --oneline -1)" - -# ── 대상 정하기 ───────────────────────────────────────────────────── -if [ ${#TARGETS[@]} -eq 0 ]; then - SVCS=() # 빈 인자 = compose 가 전부로 해석한다 - echo "▶ 대상: 전체" -else - SVCS=("${TARGETS[@]}") - # 백엔드 하나를 건드리면 같은 이미지를 쓰는 형제도 함께 갈아끼운다. - if [ "$ONLY" = 0 ]; then - for t in "${TARGETS[@]}"; do - for b in "${BACKEND_SVCS[@]}"; do [ "$t" = "$b" ] || continue - for sib in "${BACKEND_SVCS[@]}"; do - printf '%s\n' "${SVCS[@]}" | grep -qx "$sib" || { - SVCS+=("$sib") - echo " + $sib — 같은 이미지를 쓴다(옛 코드로 남지 않게 함께 간다)" - } - done - done - done - fi - echo "▶ 대상: ${SVCS[*]}" +[ ${#TARGETS[@]} -gt 0 ] || TARGETS=(solution-backend solution-worker solution-site) +RUNNING=$(docker compose ps --services --status running) +if [ "$ONLY" = 0 ]; then + case " ${TARGETS[*]} " in + *" solution-backend "*|*" solution-worker "*) + TARGETS+=(solution-backend solution-worker) + if grep -qx admin-backend <<<"$RUNNING"; then TARGETS+=(admin-backend); fi ;; + esac fi - -# ── 빌드 · 교체 ───────────────────────────────────────────────────── -# build 절이 없는 서비스(node:24-alpine · nginx:alpine)는 build 가 조용히 건너뛴다. -echo "▶ build" -docker compose build ${SVCS+"${SVCS[@]}"} - -echo "▶ up -d" -docker compose up -d --force-recreate ${SVCS+"${SVCS[@]}"} - -echo +SERVICES=() +for target in "${TARGETS[@]}"; do + case " ${SERVICES[*]-} " in *" $target "*) ;; *) SERVICES+=("$target");; esac +done +# site의 렌더러도 worker 이미지에 들어간다. +case " ${SERVICES[*]} " in + *" solution-site "*) case " ${SERVICES[*]} " in *" solution-worker "*) ;; *) SERVICES+=(solution-worker);; esac ;; +esac +docker compose build "${SERVICES[@]}" +docker compose up -d --no-deps --force-recreate "${SERVICES[@]}" docker compose ps -echo -echo "로그: ./log.sh" diff --git a/docker-compose.yml b/docker-compose.yml index c354fdd..77b5dc4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,8 +28,12 @@ x-common-env: &common-env # ★ 기본값은 localhost 다. 운영 도메인을 기본으로 두면 .env 를 안 채운 로컬 빌드가 # 조용히 운영 주소를 번들에 굽는다(실측 2026-09-03: 로컬 링크가 킹서버로 갔다). SITE_PUBLIC_HOST: ${SITE_PUBLIC_HOST:-localhost} - SITE_PAYLOAD_DIR: /app/out/payloads - SITE_OUTPUT_DIR: /app/out/sites + # ★ 렌더러(solution/site/scripts/prerender.ts)가 songs·out 디렉토리를 **자기 파일 위치 + # 기준 상대경로**로 찾는다(SITE_ROOT = dist/prerender/../..) — 워커 컨테이너 안에서 그 + # 렌더러를 직접 띄우므로(render_service.py) 세 디렉토리가 실제 설치 자리 + # (/app/solution/site/) 아래 나란히 있어야 한다. 아래 볼륨 마운트와 짝이 맞아야 한다. + SITE_PAYLOAD_DIR: /app/solution/site/payloads + SITE_OUTPUT_DIR: /app/solution/site/out # ★ 프리렌더와 같은 값이어야 한다. 어긋나면 색인 통보가 403 이다. INDEXNOW_KEY: ${INDEXNOW_KEY:-} @@ -48,10 +52,10 @@ services: # ★ 크론은 이 컨테이너에서만 돈다. 프로세스가 여럿이면 같은 시각에 중복 실행된다. SCHEDULER_ENABLED: "1" volumes: - - ./solution/site/payloads:/app/out/payloads - # 노래 파일. payload 와 나란히 둔다 — 백엔드가 mp3 를 여기 떨구면 프리렌더가 - # 사이트 디렉토리로 복사한다(services/song_service · site/scripts/prerender.ts). - - ./solution/site/songs:/app/out/songs + - ./solution/site/payloads:/app/solution/site/payloads + # 노래 파일. payload 와 나란히 둔다 — 백엔드가 mp3 를 여기 떨구면 워커가 렌더러를 + # 돌릴 때 사이트 디렉토리로 복사된다(services/song_service · site/scripts/prerender.ts). + - ./solution/site/songs:/app/solution/site/songs # ★ 스키마 마이그레이션 SQL. 이미지에 굽지 않고 마운트한다 — 파일이 자주 늘고, # 이미 세운 DB 를 따라오게 하는 것이 목적이라 코드 배포와 별개로 돌 수 있어야 한다. - ./postgres-init:/app/postgres-init:ro @@ -64,13 +68,17 @@ services: driver: json-file options: { max-size: "10m", max-file: "5" } + # ★ BUILD·ROLLBACK 잡이 렌더러(solution/site)를 subprocess 로 직접 돌린다 + # (services/render_service.py) — 예전에 solution-prerender 컨테이너가 하던 일이다. + # 그래서 이 서비스만 **다른 이미지**(Dockerfile.worker, Node 런타임 + 컴파일된 렌더러 + # 포함)를 쓴다. solution-backend·admin-backend 는 그 Node 를 쓸 일이 없다. solution-worker: build: context: . - dockerfile: solution/backend/Dockerfile - image: o2o-web4ai-backend + dockerfile: solution/backend/Dockerfile.worker + image: o2o-web4ai-worker container_name: o2o-web4ai-solution-worker - command: ["python", "worker_main.py"] + command: ["sh", "-c", "node /app/solution/site/dist/prerender/prerender.js --seed-assets --out=/app/solution/site/out && exec python worker_main.py"] env_file: - .env environment: @@ -79,15 +87,20 @@ services: WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-1} JOB_DEADLINE_SEC: ${JOB_DEADLINE_SEC:-900} JOB_LEASE_SEC: ${JOB_LEASE_SEC:-120} + # 렌더러 subprocess 가 굽는 동안 기다리는 시간(사진 내려받기 포함). BUILD 의 + # job_deadline_sec 보다 짧아야 한다 — 안 그러면 잡 전체가 먼저 타임아웃된다. + RENDER_TIMEOUT_SEC: ${RENDER_TIMEOUT_SEC:-180} # 이미지의 HEALTHCHECK 는 API 용(HTTP :9800)이다. 워커는 포트가 없어 그대로 두면 늘 unhealthy 다. healthcheck: disable: true volumes: - - ./solution/site/payloads:/app/out/payloads - # 노래 파일. payload 와 나란히 둔다 — 백엔드가 mp3 를 여기 떨구면 프리렌더가 + - ./solution/site/payloads:/app/solution/site/payloads + # 노래 파일. payload 와 나란히 둔다 — 백엔드가 mp3 를 여기 떨구면 렌더러가 # 사이트 디렉토리로 복사한다(services/song_service · site/scripts/prerender.ts). - - ./solution/site/songs:/app/out/songs - - site-out:/app/out/sites:ro + - ./solution/site/songs:/app/solution/site/songs + # ★ 이제 이 컨테이너가 굽는 쪽이다 — 읽기전용이 아니다(예전 solution-prerender 가 + # 쓰던 자리를 그대로 이어받는다). + - site-out:/app/solution/site/out extra_hosts: - "host.docker.internal:host-gateway" stop_grace_period: 300s @@ -124,10 +137,10 @@ services: start_period: 20s retries: 3 volumes: - - ./solution/site/payloads:/app/out/payloads + - ./solution/site/payloads:/app/solution/site/payloads # 노래 파일. payload 와 나란히 둔다 — 백엔드가 mp3 를 여기 떨구면 프리렌더가 # 사이트 디렉토리로 복사한다(services/song_service · site/scripts/prerender.ts). - - ./solution/site/songs:/app/out/songs + - ./solution/site/songs:/app/solution/site/songs ports: # ★ 내부망에만 연다. 0.0.0.0 으로 열면 API 를 가른 의미가 없다. - "${ADMIN_API_BIND:-127.0.0.1}:${ADMIN_API_PORT_PUBLIC:-9801}:9801" @@ -140,48 +153,6 @@ services: driver: json-file options: { max-size: "10m", max-file: "5" } - # 발행 사이트를 굽는다. **굽기만 한다** — 서빙은 solution-site(nginx)가 맡는다. - # - # ★ 사장님 앱 dev 서버는 여기서 빠졌다. 운영에 `vite dev` 를 띄우면 요청마다 트랜스파일하고 - # 기동이 npm install 네트워크에 의존하고 /src 원본이 그대로 나간다. 번들은 solution-site - # 이미지가 굽는다(nginx/Dockerfile). 로컬에서 HMR 이 필요하면 `--profile dev`. - solution-prerender: - image: node:24-alpine - container_name: o2o-web4ai-solution-prerender - working_dir: /app - command: - - sh - - -c - - | - cd /app - # ★ `-d node_modules` 로 판단하면 안 된다. 익명 볼륨은 빈 디렉토리로 이미 존재해서 - # 설치를 건너뛰고 `vite: not found`(exit 127)로 죽는다. - [ -x node_modules/.bin/vite ] || npm install - exec node solution/site/scripts/watch-payloads.mjs - environment: - INDEXNOW_KEY: ${INDEXNOW_KEY:-} - # ★ 발행 호스트를 프론트 .env 에 따로 적지 않는다 — 루트 .env 의 SITE_PUBLIC_HOST 를 - # 그대로 흘려보낸다. 두 곳에 적으면 canonical 과 화면 주소가 조용히 갈라진다. - VITE_PUBLISH_HOST: ${SITE_PUBLIC_HOST:-localhost} - volumes: - - ./package.json:/app/package.json - - ./package-lock.json:/app/package-lock.json - - ./tsconfig.base.json:/app/tsconfig.base.json - - ./solution:/app/solution - - ./admin:/app/admin - # ★ node_modules 는 컨테이너 것을 쓴다. 호스트가 macOS(arm64-darwin)라 그 안의 - # rollup·esbuild 네이티브 바이너리를 리눅스 컨테이너가 못 쓴다. - - /app/node_modules - - /app/solution/site/node_modules - - /app/solution/frontend/node_modules - - /app/admin/frontend/node_modules - # ★ 산출물은 named volume. 호스트 경로면 재배포로 코드를 갈아엎는 순간 전 사이트가 404 다. - - site-out:/app/solution/site/out - restart: unless-stopped - logging: - driver: json-file - options: { max-size: "10m", max-file: "5" } - # 사장님 앱 dev 서버 + 발행본 정적서버(:3001). **로컬 전용**이다 — `--profile dev`. # 운영에서 이게 뜨면 안 된다(위 solution-prerender 주석). solution-frontend: @@ -294,7 +265,7 @@ services: # 앞단 프록시를 세울 거면 여기서 포트만 옮기고 프록시가 이쪽을 가리키게 한다. - "${SITE_HTTP_BIND:-0.0.0.0}:${SITE_HTTP_PORT:-80}:80" depends_on: - - solution-prerender + - solution-backend restart: unless-stopped logging: driver: json-file diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 73a171b..73b51e1 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,5 +1,8 @@ # ARCHITECTURE +> 2026-09-15: 현재 발행 실행·산출물 버전·배포는 [PUBLISH_VERSION.md](PUBLISH_VERSION.md). +> 아래의 별도 프리렌더 컨테이너·폴링·전체 재굽기 설명은 이전 구조다. + 제품 판단은 [PRODUCT.md](PRODUCT.md), 배포 절차는 [DEPLOY.md](DEPLOY.md), 에이전트가 밟기 쉬운 함정 목록은 [AGENTS.md](../AGENTS.md). 여기는 **구조와 경계**만 다룬다. diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md index 9c180f5..1d82214 100644 --- a/docs/DEPLOY.md +++ b/docs/DEPLOY.md @@ -1,5 +1,8 @@ # 배포 · 스토리지 +> 2026-09-15 이후 절차는 [PUBLISH_VERSION.md](PUBLISH_VERSION.md)를 따른다. +> 기존 사이트 전체 재굽기는 하지 않는다. 최초 전환 때 구 프리렌더를 중지한다. + > **현재 결정 (2026-08-31): 발행 사이트는 서버 안에서 nginx 가 정적 서빙한다.** > Azure Blob 은 코드에 있으나 **켜지 않는다**(`AZURE_STORAGE_CONNECTION_STRING` 비움). > 클라우드는 고도화 때 붙인다 — 근거는 [ARCHITECTURE.md 3절](ARCHITECTURE.md). diff --git a/docs/DEVLOG.md b/docs/DEVLOG.md index 73ac8d1..73147fd 100644 --- a/docs/DEVLOG.md +++ b/docs/DEVLOG.md @@ -1,5 +1,14 @@ # 개발 일지 +## 2026-09-15 — 워커 렌더·발행 버전·예약 안내·미리보기 대기 + +- 상시 프리렌더를 제거하고 워커가 컴파일된 Node 렌더러를 실행한다. +- 버전별 HTML을 보존하고 게이트 통과 뒤 공개 링크를 전환한다. 재시도는 저장된 성공본을 사용한다. +- 예약 전 확인을 이용안내에 통합하고 iframe 렌더 완료까지 스피너를 표시한다. +- 배포는 기존 HTML과 목업을 재굽지 않는다. 상세: [PUBLISH_VERSION.md](PUBLISH_VERSION.md). +- 읽기 생성 토큰 상한을 늘리고 추첨 배열을 고정해 반복 렌더를 방지한다. +- 검증: 사이트 81건, 백엔드 발행·롤백·서치콘솔 45건 통과. 빌더·사이트 빌드 통과. + 무엇을 왜 바꿨는지 날짜순으로 남긴다. 새 항목을 **위에** 추가한다. 결론과 배경은 각 문서가 단일 출처다 — 여기에는 요약과 링크만 둔다. diff --git a/docs/PUBLISH_VERSION.md b/docs/PUBLISH_VERSION.md new file mode 100644 index 0000000..89a117b --- /dev/null +++ b/docs/PUBLISH_VERSION.md @@ -0,0 +1,32 @@ +# 발행 버전과 워커 (2026-09-15) + +이 문서가 이전 문서의 프리렌더 상시 기동·전체 재굽기 절차를 대체한다. + +`BUILD → snapshot → payload → Node 렌더 → 결과 게이트 → 공개 링크 전환 → DB 기록` + +- Python은 HTML을 만들지 않는다. 미리 컴파일된 Node를 실행하고 JSON 보고서만 읽는다. +- 워커 이미지에 Node와 렌더러를 포함한다. 실행 중 npm 설치·번들 빌드는 없다. +- 공유 볼륨의 파일 잠금으로 렌더·공개 전환을 직렬화한다. 수집 등 다른 잡은 동시 실행한다. +- `out/versions//`에 성공한 HTML과 렌더 보고서를 보존한다. +- `out/s/`는 공개 버전의 상대 심볼릭 링크다. 게이트 통과 후 전환한다. +- 성공한 버전은 재시도·롤백 때 다시 쓰지 않는다. 기존 일반 디렉토리는 첫 재발행 때 legacy로 보존한다. +- 배포 시 HTML·기존 HTML의 자산 주소를 수정하지 않는다. 공용 자산과 미리보기 셸만 준비한다. +- 목업과 보관 버전의 참조 자산도 삭제 대상에서 제외한다. versions는 Azure 공용 업로드에서 제외한다. +- 롤백 API: `POST /v1/place/{place_id}/site/version/rollback`, `target_version`. 소유권 검사와 BUILD 중복 방지 키를 공유한다. +- 로컬 공개 전환과 DB/Azure는 단일 트랜잭션이 아니다. 외부 저장소·DB 실패 시 재시도 및 운영 확인이 필요하다. +- 최초 일반 디렉토리→링크 전환은 두 rename 사이 짧은 공백이 가능하다. 이후 링크 교체는 원자적이다. + +## 배포 + +1. 진행 중 잡·서버 변경·목업 및 참조 자산 해시를 확인하고 site-out을 백업한다. +2. backend·worker·site 이미지를 빌드한다. admin은 기본 대상이 아니다. +3. 기존 worker와 solution-prerender를 중지한 뒤 새 worker를 기동한다. 두 렌더러를 동시에 실행하지 않는다. +4. API·미리보기·테스트 발행을 확인하고 목업 해시를 대조한다. 전체 재굽기·republish_all은 실행하지 않는다. + +워커 경로: `SITE_PAYLOAD_DIR=/app/solution/site/payloads`, `SITE_OUTPUT_DIR=/app/solution/site/out`. +DB 테이블 추가는 없다. 기존 버전·잡·발행 로그를 사용한다. + +## UI + +예약 전 확인과 요약을 이용안내 및 예약에 통합한다. 별도 요약 섹션과 예약 카드의 중복 규정은 제거한다. +빌더 미리보기는 iframe 내부 렌더 완료 신호까지 스피너를 표시한다. 출처·iframe을 확인하고 12초 상한을 둔다. diff --git a/postgres-init/init-data/init.sql b/postgres-init/init-data/init.sql index a47dbf3..dd3dfbf 100644 --- a/postgres-init/init-data/init.sql +++ b/postgres-init/init-data/init.sql @@ -93,7 +93,7 @@ CREATE TABLE IF NOT EXISTS public.users ( -- ============================================================ CREATE TABLE IF NOT EXISTS public.jobs ( job_id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- ★ 이 표만 server_default 가 꼭 필요하다 — 큐 전이가 raw SQL(RETURNING)이라 ORM 의 파이썬 default 가 안 먹는다 - job_type SMALLINT NOT NULL, -- JobType: 1=collect 2=vision 3=copy 4=build 5=local_sync 6=ai_check + job_type SMALLINT NOT NULL, -- JobType: 1=collect 2=vision 3=copy 4=build 5=local_sync 6=ai_check 7=song 8=rollback status SMALLINT NOT NULL DEFAULT 1, -- JobStatus: 1=pending 2=running 3=done 4=dead priority SMALLINT NOT NULL DEFAULT 100, -- 낮을수록 우선 payload JSONB NOT NULL DEFAULT '{}'::jsonb, diff --git a/solution/backend/Dockerfile.worker b/solution/backend/Dockerfile.worker new file mode 100644 index 0000000..4f1ecd8 --- /dev/null +++ b/solution/backend/Dockerfile.worker @@ -0,0 +1,70 @@ +# 워커 전용 이미지 — solution-backend(:9800)·admin-backend(:9801) 와 **다른 이미지**다. +# +# ★ 왜 갈랐나 (프리렌더를 워커 실행으로 통합, 2026-09-15) +# BUILD·ROLLBACK 잡은 이제 컴파일된 Node 렌더러(solution/site)를 subprocess 로 직접 +# 돌린다(services/render_service.py) — 예전에 별도 컨테이너(solution-prerender)가 하던 +# 일이다. 그 렌더러가 필요한 건 **워커뿐**이다. Dockerfile(api/admin 겸용)에 Node 를 +# 같이 넣으면 API 를 여는 요청만 받는 컨테이너에도 쓰지 않는 Node 런타임과 렌더러 +# 번들이 실려 나간다 — 이미지가 커지고 공격 표면도 는다. +# +# ★ 기동·잡 실행 중에 npm install 을 하지 않는다. site-builder 스테이지가 이미지 빌드 +# 시점에 한 번 굽고, 최종 이미지는 컴파일된 산출물만 COPY 한다. +# ★ node_modules 는 안 들어간다 — solution/site/vite.config.ts 가 SSR 번들에 +# `ssr.noExternal: true` 로 react 등 의존성까지 전부 접어 넣는다(실측: node_modules 를 +# 지우고 실행해도 그대로 돈다, dist/prerender/prerender.js 단일 파일 1.8MB). +# +# ★ 빌드 컨텍스트는 solution-backend 와 같은 **레포 루트**다 — solution/site 와 +# solution/backend 를 한 이미지에 담아야 한다. + +# ── 1단계: 렌더러 컴파일 (Node) ────────────────────────────────────────────── +FROM node:24-bookworm AS site-builder +WORKDIR /app + +COPY package.json package-lock.json tsconfig.base.json ./ +COPY solution/shared/package.json solution/shared/ +COPY solution/frontend/package.json solution/frontend/ +COPY solution/site/package.json solution/site/ +COPY admin/frontend/package.json admin/frontend/ +# ★ npm workspaces 는 루트 package.json 의 workspaces 목록에 있는 자리마다 package.json 을 +# 요구한다 — site 만 빌드해도 4개를 전부 COPY 해야 npm ci 가 성공한다(nginx/Dockerfile 과 같다). +RUN npm ci + +COPY solution/shared ./solution/shared +COPY solution/site ./solution/site +RUN npm run build:client -w @o2o/site \ + && npm run build:prerender -w @o2o/site + +# ── 2단계: Node 런타임만 (바이너리를 뽑아 쓴다) ────────────────────────────── +# ★ python:3.12-slim 과 같은 계열(Debian bookworm)의 태그를 써야 한다 — glibc ABI 가 +# 어긋나면 바이너리만 복사해서는 못 돈다("cannot execute binary file" 류로 죽는다). +FROM node:24-bookworm-slim AS node-runtime + +# ── 3단계: 최종 워커 이미지 ─────────────────────────────────────────────── +FROM python:3.12-slim-bookworm +WORKDIR /app + +COPY solution/backend/requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +# ★ yanolja_adapter.py 가 Playwright 로 페이지를 렌더링한다(수집 파이프라인, 워커 전용 경로) — +# 패키지(pip)만으로는 브라우저 실행 파일이 없다. --with-deps 가 Chromium 이 필요로 하는 +# 시스템 라이브러리(libnss3 등)까지 apt 로 같이 깐다. +RUN playwright install --with-deps chromium + +# Node 바이너리만 옮겨 온다 — npm·corepack 은 쓰지 않는다(빌드는 site-builder 스테이지가 +# 이미 끝냈다). /usr/local/bin/node 하나로 `node dist/prerender/prerender.js` 를 돌리는 +# 데 충분하다(실측: 아래 COPY 만으로 `node --version` 과 렌더 스모크 테스트가 통과한다). +COPY --from=node-runtime /usr/local/bin/node /usr/local/bin/node + +COPY solution/backend ./solution/backend +# ★ SITE_ROOT(solution/site/scripts/prerender.ts)가 자기 파일 위치 기준 상대경로로 +# payloads·songs·out 을 찾는다 — dist·public 이 이 자리(/app/solution/site/)에 있어야 +# 워커가 컨테이너 안에서 렌더러를 그대로 실행할 수 있다. +COPY --from=site-builder /app/solution/site/dist ./solution/site/dist +COPY --from=site-builder /app/solution/site/public ./solution/site/public + +ENV APP_ENV=local +ENV PYTHONPATH=/app/solution/backend +WORKDIR /app/solution/backend + +# 워커는 포트가 없다 — 이미지 공통 HEALTHCHECK 를 두지 않는다(compose 가 disable 한다). +CMD ["python", "worker_main.py"] diff --git a/solution/backend/common/enums.py b/solution/backend/common/enums.py index b1e06e3..a26cd67 100644 --- a/solution/backend/common/enums.py +++ b/solution/backend/common/enums.py @@ -389,6 +389,7 @@ class PublishAction(CodeEnum): REBUILD = 3 SUSPEND = 4 RESUME = 5 + ROLLBACK = 6 # 예전 버전으로 공개 주소를 되돌림 — services/rollback_service.py class PublishResult(CodeEnum): @@ -434,6 +435,7 @@ class JobType(CodeEnum): LOCAL_SYNC = 5 # 지역 정보 갱신 — 행정구역 코드 단위(같은 지역 사이트 50개여도 1회) AI_CHECK = 6 # AI 검색 노출 점검 SONG = 7 # 이 숙소의 노래 한 곡 (가사 Gemini → 작곡 Suno). 발행이 이 잡을 건다 + ROLLBACK = 8 # 예전 버전 스냅샷으로 다시 굽고 공개 주소를 그 버전으로 되돌림 class JobStatus(CodeEnum): diff --git a/solution/backend/conftest.py b/solution/backend/conftest.py index ed984ce..7e70da2 100644 --- a/solution/backend/conftest.py +++ b/solution/backend/conftest.py @@ -166,7 +166,7 @@ def fake_renderer(monkeypatch, tmp_path_factory): ★ 왜 필요한가 발행 게이트는 이제 **실제로 나갈 HTML** 을 보고 판정한다. 그 HTML 은 Node 렌더러가 - 굽고, BUILD 잡은 그 결과 보고서를 기다린다(services/render_report.wait_for). + 굽고, BUILD 잡은 렌더러 subprocess 를 직접 돌린다(services/render_service.render_site). 파이썬 테스트 환경에는 Node 렌더러가 없으므로, payload 를 읽어 보고서를 만들어 주는 대역을 끼운다 — 여기서 검사하려는 건 **백엔드가 보고서를 어떻게 처리하는가** 다. @@ -175,8 +175,9 @@ def fake_renderer(monkeypatch, tmp_path_factory): 두 벌로 두면 어긋나고, 어긋난 걸 아무도 모르는 게 원래 문제였다. """ import json + from pathlib import Path - from services import render_report, site_payload + from services import render_report, render_service, site_payload payload_dir = tmp_path_factory.mktemp("payloads") monkeypatch.setattr(site_payload, "payload_dir", lambda: payload_dir) @@ -229,13 +230,16 @@ def fake_renderer(monkeypatch, tmp_path_factory): count += sum(1 for m in payload.get("media") or [] if long(m.get("alt"))) return count - async def _wait_for(slug, site_version, timeout_sec, poll_sec=1.0): - path = payload_dir / f"{slug}.json" + async def _render_site(payload_path, site_version, timeout_sec): + path = Path(payload_path) + slug = path.stem if not path.is_file(): - return None + raise render_service.RenderFailed(f"payload 파일이 없다: {payload_path}") payload = json.loads(path.read_text(encoding="utf-8")) if payload["site"]["version"] != site_version: - return None + raise render_service.RenderFailed( + f"버전이 다르다(payload={payload['site']['version']} 요청={site_version})" + ) place = payload.get("place") or {} count = _count(payload) # ★ 고유 콘텐츠 0건이면 렌더러는 **페이지를 쓰지 않는다** @@ -276,5 +280,8 @@ def fake_renderer(monkeypatch, tmp_path_factory): "error": None, } - monkeypatch.setattr(render_report, "wait_for", _wait_for) + monkeypatch.setattr(render_service, "render_site", _render_site) + async def _activate_site(slug, site_version): + return None + monkeypatch.setattr(render_service, "activate_site", _activate_site) return payload_dir diff --git a/solution/backend/crud/site_crud.py b/solution/backend/crud/site_crud.py index 922af6b..3cb663d 100644 --- a/solution/backend/crud/site_crud.py +++ b/solution/backend/crud/site_crud.py @@ -45,6 +45,10 @@ class ISiteCRUD(ABC): async def get_version(self, cdb: AsyncSession, site_id, site_version_id) -> Tuple[ErrorType, site_versions]: pass + @abstractmethod + async def get_version_by_number(self, cdb: AsyncSession, site_id, version: int) -> Tuple[ErrorType, site_versions]: + pass + @abstractmethod async def finish_version(self, cdb: AsyncSession, site_version_id, data: dict) -> Tuple[ErrorType, int]: pass @@ -189,6 +193,31 @@ class SiteCRUD(ISiteCRUD): LOG.e_no_callstack(ex) return ErrorType.DB_RUN_FAILED, None + async def get_version_by_number(self, cdb: AsyncSession, site_id, version: int) -> Tuple[ErrorType, site_versions]: + """롤백 대상 조회 — site_versions.version(사람이 보는 번호) 로 찾는다. + + ★ site_version_id(uuid) 가 아니다. 화면·API 는 버전 번호로 고르는 게 자연스럽고, + 그 번호가 곧 out/versions/// 디렉토리 이름이다(prerender.ts).""" + try: + query = ( + select(site_versions) + .where( + site_versions.site_id == site_id, + site_versions.version == version, + site_versions.deleted == False, # noqa: E712 + ) + .limit(1) + ) + err_type, rows = await DB_SESSION_MNG.execute(cdb, query) + if err_type != ErrorType.SUCCESS: + return err_type, None + if len(rows) != 1: + return ErrorType.DB_INVALID_KEY, None + return ErrorType.SUCCESS, rows[0] + except Exception as ex: + LOG.e_no_callstack(ex) + return ErrorType.DB_RUN_FAILED, None + async def finish_version(self, cdb: AsyncSession, site_version_id, data: dict) -> Tuple[ErrorType, int]: """빌드 결과를 버전에 기록한다(BUILT 또는 FAILED).""" try: diff --git a/solution/backend/router/v1/site/protocol.py b/solution/backend/router/v1/site/protocol.py index 816980c..657d84f 100644 --- a/solution/backend/router/v1/site/protocol.py +++ b/solution/backend/router/v1/site/protocol.py @@ -36,6 +36,17 @@ class Res_StartBuild(Res_WebPacketProtocol): created: bool = True +class Req_Rollback(SiteProtocol): + """예전 버전으로 공개 주소를 되돌린다. + + ★ 재굽기가 아니다 — 대상 버전이 디스크에 아직 있으면 심볼릭 링크만 돌린다. 지워졌으면 + (보관 정책, prerender.ts pruneOldVersions) site_versions.snapshot 으로 다시 굽고 나서 + 돌린다. 어느 경우든 게이트를 다시 통과해야 한다(사장님이 이미 확인한 값이라 대개는 + 그대로 통과한다).""" + + target_version: int + + class SiteVersionData(WebPacketProtocol): model_config = ConfigDict(from_attributes=True) diff --git a/solution/backend/router/v1/site/site.py b/solution/backend/router/v1/site/site.py index bbf8361..76dc87c 100644 --- a/solution/backend/router/v1/site/site.py +++ b/solution/backend/router/v1/site/site.py @@ -7,6 +7,7 @@ from common.models.gmodel import PageParams, UserInfo from router.v1.validator.dependencies import IsValidAccessToken, RemoveNoneResponse from services.site_service import SiteService from .protocol import ( + Req_Rollback, Req_SiteSlug, Req_SiteStatus, Req_SiteTemplate, @@ -178,6 +179,20 @@ async def site_preview( return JSONResponse(content=payload) +@router.post( + path="/version/rollback", + response_model=Res_StartBuild, + summary="예전 버전으로 되돌리기(비동기)", + description="target_version 을 다시 공개 주소로 내보낸다. 재수집·재생성을 하지 않는다 — " + "그 버전이 발행될 때 박제된 snapshot 을 그대로 다시 굽는다. 렌더러가 그 사이 " + "바뀌었을 수 있어 게이트는 다시 통과해야 한다. 실패하면 지금 공개된 버전은 그대로다.", +) +async def rollback_version( + place_id: UUID, req: Req_Rollback, service: SiteService = Depends(), user_info: UserInfo = Depends(IsValidAccessToken) +): + return RemoveNoneResponse(await service.start_rollback(user_info, str(place_id), req)) + + @router.get(path="/version/list", response_model=Res_SiteVersions, summary="빌드 버전 목록") async def list_versions(place_id: UUID, service: SiteService = Depends(), user_info: UserInfo = Depends(IsValidAccessToken)): return RemoveNoneResponse(await service.list_versions(user_info, str(place_id))) diff --git a/solution/backend/services/azure_static.py b/solution/backend/services/azure_static.py index 8fea4e7..b2a37b8 100644 --- a/solution/backend/services/azure_static.py +++ b/solution/backend/services/azure_static.py @@ -22,7 +22,7 @@ def is_configured() -> bool: def output_dir() -> Path: - return Path(os.environ.get("SITE_OUTPUT_DIR", "/app/out/sites")) + return Path(os.environ.get("SITE_OUTPUT_DIR", "/app/solution/site/out")) def _content_type(path: Path) -> str: @@ -104,7 +104,7 @@ def _upload_shared(container, root: Path, prefix: str) -> set[str]: for entry in sorted(root.iterdir()): # `s/` 는 사이트별 디렉터리다 — 발행한 사이트 하나만 따로 올린다. # (여기서 함께 올리면 한 명이 발행할 때마다 전체 사이트를 다시 올리게 된다.) - if entry.name.startswith(".") or entry.name == SITE_ROOT_DIR: + if entry.name.startswith(".") or entry.name in {SITE_ROOT_DIR, "versions"}: continue if entry.is_dir(): uploaded |= _upload_tree(container, root, entry.name, prefix) diff --git a/solution/backend/services/build_service.py b/solution/backend/services/build_service.py index 76aee5a..b09d697 100644 --- a/solution/backend/services/build_service.py +++ b/solution/backend/services/build_service.py @@ -33,7 +33,7 @@ from services import ( azure_static, indexnow, publish_gate, - render_report, + render_service, seo_keywords, site_payload, site_thumbnail, @@ -46,9 +46,9 @@ from services.snapshot import build_snapshot _site_crud = SiteCRUD() _place_crud = PlaceCRUD() -# 렌더러가 이 버전을 굽고 보고서를 돌려줄 때까지 기다리는 시간. -# ★ 넉넉해야 한다. 짧으면 멀쩡한 발행이 "렌더 대기 초과"로 실패한다 — -# 렌더러는 payload 를 2초 주기로 보고, 사이트 하나 굽는 데 1초 남짓 걸린다. +# 렌더러 subprocess 가 끝나기를 기다리는 시간(사진 내려받기 포함). +# ★ 넉넉해야 한다. 짧으면 멀쩡한 발행이 "렌더 시간 초과"로 실패한다 — 처음 보는 사진을 +# 내려받는 발행은 몇 초가 더 걸린다(mirrorMedia, prerender.ts). RENDER_TIMEOUT_SEC = float(os.environ.get("RENDER_TIMEOUT_SEC") or 180) @@ -73,10 +73,13 @@ async def ensure_site(place_id: str) -> "sites": return row -async def _load_links(place_id: str) -> list: +async def load_channel_links(place_id: str) -> list: """채널 링크(야놀자·네이버 플레이스·인스타…). - 스냅샷에 담기지 않는 유일한 발행 재료라 여기서 읽어 payload 로 넘긴다. + 스냅샷에 담기지 않는 유일한 발행 재료라 여기서 읽어 payload 로 넘긴다 — 재빌드(run_build) + 도 롤백(rollback_service.run_rollback)도 같은 함수를 쓴다. **스냅샷에는 안 싣는다** — + 롤백이 옛 스냅샷으로 다시 구워도 링크는 항상 지금 확정된 것을 보여줘야 한다(끊긴 링크를 + 옛 버전째 되살리면 안 된다). ★ 실패해도 빈 목록으로 진행한다 — 링크가 없다고 발행을 막을 이유가 없다.""" err, rows = await DB_SESSION_MNG.execute_lambda( place_channels.DBType(), @@ -249,24 +252,25 @@ async def run_build(job: dict) -> dict: site.status = SiteStatus.PUBLISHED.value site.published_at = site.published_at or now - links = await _load_links(place_id) - payload_path = await emit_payload(place, snapshot, site, version, links) + links = await load_channel_links(place_id) + payload_path = await emit_payload(place, snapshot, site, version, links, publish=want_publish) if not payload_path: return await _fail("payload 를 쓰지 못했다 — 렌더러에 넘길 입력이 없다") result["payload_path"] = payload_path slug = site_payload.publish_slug(place, site) - # ---- 렌더 결과를 기다린다 ---- + # ---- 렌더러를 직접 돌린다 ---- # ★ 게이트는 **실제로 나갈 HTML** 을 보고 판정해야 한다. 렌더러가 자기 산출물을 대조해 # 구조화 데이터 불일치와 고유 콘텐츠 수를 보고서로 돌려준다. - report = await render_report.wait_for(slug, version_no, RENDER_TIMEOUT_SEC) - if report is None: + # ★ payload=False(미발행)면 렌더러는 `out/versions///` 에만 굽고 공개 + # 주소(`out/s/`)는 건드리지 않는다 — emit_payload 에 실은 publish 플래그가 정한다. + try: + report = await render_service.render_site(payload_path, version_no, RENDER_TIMEOUT_SEC) + except render_service.RenderFailed as ex: # ★ 발행하지 않는다. 페이지가 있는지 확인하지 못한 채 "발행됨"으로 남기면 # 사장님은 [사이트 열기] 를 눌러야 404 로 알게 된다. - return await _fail( - f"렌더 결과를 {RENDER_TIMEOUT_SEC}초 안에 받지 못했다 — 렌더러가 도는지 확인이 필요하다" - ) + return await _fail(str(ex)) mismatches = list(report.get("mismatches") or []) # ★ None(재지 못했다)과 0(재 봤더니 0건)을 뭉개지 않는다. 게이트는 raw 를 보고, @@ -308,8 +312,9 @@ async def run_build(job: dict) -> dict: thumbnail_url = None if want_publish: try: + await render_service.activate_site(slug, version_no) azure_result = await azure_static.publish(slug) - except azure_static.AzurePublishError as ex: + except (azure_static.AzurePublishError, render_service.RenderFailed) as ex: return await _fail(str(ex), None, stamp) if azure_result: result["azure"] = azure_result @@ -383,4 +388,3 @@ async def run_build(job: dict) -> dict: version.build_status = BuildStatus.BUILT.value version.built_at = now return result - diff --git a/solution/backend/services/indexnow.py b/solution/backend/services/indexnow.py index 69d7731..74ba052 100644 --- a/solution/backend/services/indexnow.py +++ b/solution/backend/services/indexnow.py @@ -44,7 +44,7 @@ def is_configured() -> bool: def output_dir() -> Path: - return Path(os.environ.get("SITE_OUTPUT_DIR", "/app/out/sites")) + return Path(os.environ.get("SITE_OUTPUT_DIR", "/app/solution/site/out")) def site_urls(slug: str) -> list[str]: diff --git a/solution/backend/services/render_report.py b/solution/backend/services/render_report.py index b9844b9..7c642fa 100644 --- a/solution/backend/services/render_report.py +++ b/solution/backend/services/render_report.py @@ -1,22 +1,18 @@ """렌더 보고서 — SSG 가 실제로 페이지를 구웠는지 확인하는 창구. ★ 왜 필요한가 - 발행 잡은 payload JSON 을 쓰는 것까지가 끝이고, 방문자가 볼 HTML 은 프론트의 SSG 가 굽는다. - 그 사이가 끊겨 있었다. 프리렌더가 깨져도 DB 에는 "발행됨"으로 남고, 페이지는 없는데 - 아무도 그걸 모른다 — 사장님이 [사이트 열기] 를 눌러야 404 로 발견된다. + BUILD 잡은 payload JSON 을 쓰고 렌더러를 직접 돌린다(services/render_service). 렌더러는 + 굽는 동안 있었던 일(성공/실패, 구조화 데이터 대조, 고유 콘텐츠 수)을 사이트마다 + `/.status/.json` 에 남긴다 — 백엔드에 마운트된 디렉토리가 payload + 디렉토리뿐이라 보고서도 그 안에 둔다. - 이제 프리렌더가 사이트마다 결과를 `/.status/.json` 에 쓴다. - 백엔드에 마운트된 디렉토리가 payload 디렉토리뿐이라 보고서도 그 안에 둔다. - -★ 여기서 하는 두 가지 - 1. 렌더 상태 — 구워졌는가, 언제, 실패했으면 왜 (사이트 조회 API 가 그대로 내보낸다) - 2. 발행 대기 — BUILD 잡이 이 버전의 보고서를 기다렸다가, 그 결과로 발행 여부를 정한다 - (wait_for). 게이트는 **실제로 나갈 HTML** 을 보고 판정해야 한다. +★ 여기서 하는 일 — 그 보고서를 읽는다. + render_service 가 렌더러 subprocess 를 기다린 **직후** 한 번 읽어 발행 여부를 정하고, + 사이트 조회 API(render_status)는 아무 때나 다시 읽어 "지금 이 버전이 구워져 있나" 를 보여준다. ★ 읽기 전용이다. 보고서가 없거나 깨져 있어도 예외를 밖으로 내보내지 않는다 — 사이트 조회가 부수 산출물 때문에 실패하면 안 된다. """ -import asyncio import json from pathlib import Path @@ -58,29 +54,6 @@ def read_report(slug: str) -> dict | None: return report -async def wait_for(slug: str, site_version: int, timeout_sec: float, poll_sec: float = 1.0) -> dict | None: - """이 버전의 렌더 보고서가 나올 때까지 기다린다. 시간 안에 안 나오면 None. - - ★ 왜 기다리나 - 발행 게이트는 **실제로 나갈 HTML** 을 보고 판정해야 한다. 그 HTML 은 프론트 렌더러가 - 굽고, 렌더러는 payload 파일을 보고 도는 별도 프로세스다. 그래서 잡이 payload 를 쓴 뒤 - 보고서가 올 때까지 기다렸다가, 그 결과로 발행 여부를 정한다. - - ★ 버전이 맞는 보고서만 받는다. 직전 버전 보고서를 보고 통과시키면 이번에 바뀐 값은 - 아무도 검사하지 않은 채로 발행된다. - - ★ BUILD 잡은 heartbeat 로 lease 를 갱신하므로 여기서 기다려도 reaper 에 회수되지 않는다.""" - deadline = asyncio.get_running_loop().time() + timeout_sec - while True: - report = read_report(slug) - if report is not None and report.get("siteVersion") == site_version: - return report - if asyncio.get_running_loop().time() >= deadline: - LOG.w(f"[render] 보고서 대기 시간 초과 slug={slug} v{site_version} ({timeout_sec}초)") - return None - await asyncio.sleep(poll_sec) - - def render_status(slug: str, site_version: int | None = None) -> dict: """사이트 조회 API 가 그대로 내보내는 렌더 상태. diff --git a/solution/backend/services/render_service.py b/solution/backend/services/render_service.py new file mode 100644 index 0000000..f9dc15a --- /dev/null +++ b/solution/backend/services/render_service.py @@ -0,0 +1,153 @@ +"""렌더러 실행 — Python 워커가 미리 컴파일된 Node 렌더러를 subprocess 로 직접 돌린다. + +★ 왜 (프리렌더를 워커 실행으로 통합, 2026-09-15) + 예전에는 `solution-prerender` 라는 별도 상시 컨테이너가 payloads/ 디렉토리를 2초마다 + 폴링하며 바뀐 파일을 구웠고, BUILD 잡은 `.status/.json` 이 나타나기를 또 폴링했다 + (render_report.wait_for). 컨테이너 하나·폴링 두 겹이 전부 "굽는 걸 어떻게 아느냐"는 + 같은 문제를 풀고 있었다 — 워커가 굽기를 직접 돌리면 둘 다 필요 없다: subprocess 가 + 끝나는 순간이 곧 "구워졌다"는 신호다. + +★ 이 파일은 payload → 렌더러 실행 → 구조화된 결과, 그 경계만 다룬다. + Python 은 여전히 HTML 을 만들지 않는다(ARCHITECTURE 1절) — 여기서 하는 일은 미리 빌드된 + JS 파일 하나를 `node` 로 실행하고 그 결과 보고서(.status/.json, prerender.ts 가 쓴다) + 를 읽어 돌려주는 것뿐이다. 렌더러 코드 자체는 solution/site 소유 그대로다. + +★ 워커 이미지에는 컴파일된 렌더러(`dist/client` · `dist/prerender/prerender.js`)와 `public/` + 만 있으면 된다 — node_modules 는 필요 없다(vite.config.ts `ssr.noExternal: true`, 실측: + node_modules 를 지우고 실행해도 그대로 돈다). 기동·잡 실행 중에 npm install 을 하지 않는다. +""" +import asyncio +import fcntl +import os +from pathlib import Path + +from common.logger import LOG +from services import render_report + +# 컴파일된 렌더러 진입점. Dockerfile 의 site-builder 스테이지가 여기에 굽는다. +RENDERER_ENTRY_ENV = "RENDERER_ENTRY" +DEFAULT_RENDERER_ENTRY = "/app/solution/site/dist/prerender/prerender.js" + +NODE_BIN_ENV = "NODE_BIN" +DEFAULT_NODE_BIN = "node" + +# 렌더러가 굽는 산출물 루트(out/). services/azure_static·indexnow 와 **같은 env** 를 본다 — +# 렌더러가 쓴 자리와 백엔드가 훑는 자리가 어긋나면 "구웠는데 없다"는 조용한 실패가 난다. +OUTPUT_DIR_ENV = "SITE_OUTPUT_DIR" +DEFAULT_OUTPUT_DIR = "/app/solution/site/out" + + +class RenderFailed(RuntimeError): + """렌더러 프로세스 자체가 죽었거나(비정상 종료) 보고서를 남기지 못했다. + + ★ 게이트 반려(구조화 데이터 불일치·고유 콘텐츠 0건)는 여기 안 걸린다 — 그건 exit code + 1 이어도 보고서가 정상적으로 남으므로 호출측이 report.ok 로 판단한다. 이 예외는 + "무슨 일이 있었는지조차 모른다" 는 경우만 위한 것이다.""" + + +def renderer_entry() -> str: + return os.environ.get(RENDERER_ENTRY_ENV) or DEFAULT_RENDERER_ENTRY + + +def output_dir() -> str: + return os.environ.get(OUTPUT_DIR_ENV) or DEFAULT_OUTPUT_DIR + + +async def _run_subprocess(args: list[str], timeout_sec: float) -> tuple[int | None, str]: + """같은 볼륨의 자산 대장·사이트맵은 한 프로세스만 갱신한다.""" + root = Path(output_dir()) + root.mkdir(parents=True, exist_ok=True) + with (root / ".render.lock").open("a") as lock: + try: + async with asyncio.timeout(timeout_sec): + while True: + try: + fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) + break + except BlockingIOError: + await asyncio.sleep(0.1) + return await _run_unlocked(args, timeout_sec) + except TimeoutError: + return None, "렌더 대기 또는 실행 시간 초과" + finally: + fcntl.flock(lock, fcntl.LOCK_UN) + + +async def _run_unlocked(args: list[str], timeout_sec: float) -> tuple[int | None, str]: + """렌더러 프로세스를 돌리고 (exit_code, stderr 일부) 를 돌려준다. + + ★ 타임아웃이면 프로세스를 죽이고 회수한다 — 좀비로 남겨 워커 컨테이너의 프로세스 표를 + 채우면 안 된다. exit_code 는 None 으로 남아 "시간 안에 안 끝났다" 를 구분한다.""" + proc = await asyncio.create_subprocess_exec( + *args, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + try: + _stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout_sec) + except (asyncio.TimeoutError, TimeoutError): + proc.kill() + # kill 후에도 반드시 wait 한다 — 안 하면 좀비 프로세스로 남는다. + await proc.wait() + return None, f"{timeout_sec:.0f}초 안에 끝나지 않아 강제 종료했다" + except asyncio.CancelledError: + if proc.returncode is None: + proc.kill() + await proc.wait() + raise + # 사유는 앞부분에 나온다(watch-payloads.mjs 의 run() 과 같은 규칙) — 스택 전체를 들 이유가 없다. + return proc.returncode, stderr.decode("utf-8", errors="replace")[:4000] + + +async def render_site(payload_path: str, site_version: int, timeout_sec: float) -> dict: + """payload 파일 하나를 굽는다. 끝나면 렌더 보고서(dict)를 돌려준다. + + ★ 게이트 판정은 여기서 하지 않는다 — 반환된 보고서의 `ok`·`mismatches`· + `uniqueContentCount` 를 build_service.run_build 가 그대로 판단한다(예전과 같은 계약, + render_report.wait_for 가 폴링해서 얻던 것과 같은 모양이다). + ★ 멱등이다 — 같은 payload 파일로 다시 부르면 같은 결과가 나온다(재시도 안전). 렌더러 + 자신이 사이트별로 실패를 격리하므로, 이미 성공한 사이트를 다시 구워도 결과는 같다.""" + slug = Path(payload_path).stem + # 재시도에서 같은 버전의 옛 성공 보고서를 이번 실행 결과로 읽지 않는다. + render_report.report_path(slug).unlink(missing_ok=True) + args = [ + os.environ.get(NODE_BIN_ENV) or DEFAULT_NODE_BIN, + renderer_entry(), + f"--payload={payload_path}", + f"--out={output_dir()}", + "--stage-only", + ] + try: + code, stderr = await _run_subprocess(args, timeout_sec) + except OSError as ex: + raise RenderFailed(f"렌더러 실행 실패: {type(ex).__name__}") from ex + + report = render_report.read_report(slug) + if code in (0, 1) and report is not None and report.get("siteVersion") == site_version: + return report + + # ★ 프로세스가 죽거나 시간 초과였는데 보고서도 없으면(사이트를 하나도 돌기 전에 죽은 경우) + # 대신 실패로 남긴다 — 안 그러면 build_service 가 "렌더 결과를 못 받았다" 는 것만 알고 + # 진짜 사유(모듈 누락·OOM 등)는 워커 로그에만 남는다. + reason = ( + f"렌더러가 {timeout_sec:.0f}초 안에 끝나지 않았다" + if code is None + else f"렌더러가 비정상 종료했다(exit {code})" if code != 0 else "보고서를 남기지 못했다(버전 불일치 포함)" + ) + detail = f"{reason}" + (f" — {stderr.strip()}" if stderr.strip() else "") + LOG.w(f"[render] {slug} v{site_version}: {detail}") + raise RenderFailed(detail) + + +async def activate_site(slug: str, site_version: int) -> None: + """두 게이트를 통과한 뒤 Node가 공개 링크만 전환한다.""" + args = [ + os.environ.get(NODE_BIN_ENV) or DEFAULT_NODE_BIN, + renderer_entry(), f"--out={output_dir()}", f"--activate={slug}:{site_version}", + ] + try: + code, _stderr = await _run_subprocess(args, 30) + except OSError as ex: + raise RenderFailed(f"공개 전환 실행 실패: {type(ex).__name__}") from ex + if code != 0: + raise RenderFailed("공개 버전 전환 실패") diff --git a/solution/backend/services/rollback_service.py b/solution/backend/services/rollback_service.py new file mode 100644 index 0000000..86d2dca --- /dev/null +++ b/solution/backend/services/rollback_service.py @@ -0,0 +1,158 @@ +"""예전 버전으로 되돌리기 — ROLLBACK 잡이 하는 일. + + 대상 버전 스냅샷 로드 → payload 재조립 → 렌더 → 게이트 → 공개 주소 전환 + +★ 재수집·재생성을 하지 않는다. run_build 와 다른 점이 이것 하나다 — 대상 버전이 + 박제해 둔 snapshot(site_versions.snapshot)을 **그대로** 다시 payload 로 옮길 뿐이다. + 사장님이 그때 이미 확인하고 발행했던 값이므로, 지금 fact 상태가 바뀌었어도 롤백은 + 그 시점의 값을 그대로 복원한다 — "롤백했는데 최신 수정이 섞여 있다"를 막는다. + +★ 그래도 게이트는 다시 돈다. 스냅샷은 그대로지만 **렌더러는 그때와 다를 수 있다** + (그 사이 배포됐을 수 있다) — 새 렌더러가 옛 스냅샷을 어떻게 굽는지는 다시 확인해야 한다. + +★ 재굽기가 필요 없을 수도 있다 — 대상 버전 디렉토리가 보관 기간 안에 아직 디스크에 있으면 + 렌더러가 그 사실을 알아서 이용한다(prerender.ts publishVersion 은 렌더 자체는 매번 다시 + 하지만, 결과가 같으면 파일 내용도 같다 — 멱등이다). 디스크에서 지워졌어도 문제없다 — + snapshot 이 있으니 처음부터 다시 굽는다. + +★ 실패하면 지금 공개된 버전은 그대로다. render_service.render_site 가 실패를 던지거나 + report.ok=False 면 심볼릭 링크를 아예 안 돌린다(prerender.ts publishVersion 은 성공한 + 렌더 결과에만 닿는다) — 발행 파이프라인과 같은 원자성 보장이다. +""" +import os +import uuid + +from common.database.db_session_manager import DB_SESSION_MNG +from common.database.model.models import places, site_publish_logs, site_versions, sites +from common.enums import ( + BuildStatus, + DBWRType, + ErrorType, + PlaceCategory, + PublishAction, + PublishResult, + SiteStatus, +) +from common.logger import LOG +from common.utils.gtime import GTime +from crud.place_crud import PlaceCRUD +from crud.site_crud import SiteCRUD +from services import azure_static, indexnow, publish_gate, render_service, site_payload +from services.build_service import ensure_site, load_channel_links + +_site_crud = SiteCRUD() +_place_crud = PlaceCRUD() + +RENDER_TIMEOUT_SEC_ENV = "RENDER_TIMEOUT_SEC" + + +class RollbackAborted(RuntimeError): + """재시도해도 소용없는 중단 — 잡의 last_error 로 남는다.""" + + +async def _log(site_id, version_id, result: PublishResult, gate=None, actor=None): + row = site_publish_logs( + site_id=site_id, + site_version_id=version_id, + action=PublishAction.ROLLBACK.value, + result=result.value, + reject_reason=(gate.reason.value if gate is not None and gate.reason else None), + detail=(gate.as_log() if gate is not None and not gate.passed else None), + actor_user_id=uuid.UUID(actor) if actor else None, + ) + await DB_SESSION_MNG.execute_lambda_run([site_publish_logs.DBType()], [lambda s: _site_crud.add_log(s, row)]) + + +async def run_rollback(job: dict) -> dict: + """ROLLBACK 잡 핸들러. payload: {place_id, owner_user_id, target_version, requested_by?}""" + payload = job["payload"] + place_id = payload["place_id"] + owner_user_id = payload["owner_user_id"] + target_version = int(payload["target_version"]) + render_timeout_sec = float(os.environ.get(RENDER_TIMEOUT_SEC_ENV) or 180) + + err, place = await DB_SESSION_MNG.execute_lambda( + places.DBType(), + DBWRType.DB_READ.value, + lambda s: _place_crud.get_place(s, uuid.UUID(owner_user_id), uuid.UUID(place_id)), + ) + if err != ErrorType.SUCCESS or place is None: + raise RollbackAborted(f"사업장을 찾을 수 없다: {place_id}") + + site = await ensure_site(place_id) + + v_err, version = await DB_SESSION_MNG.execute_lambda( + site_versions.DBType(), + DBWRType.DB_READ.value, + lambda s: _site_crud.get_version_by_number(s, site.site_id, target_version), + ) + if v_err != ErrorType.SUCCESS or version is None: + raise RollbackAborted(f"버전 {target_version} 을 찾을 수 없다") + if version.build_status != BuildStatus.BUILT.value or not version.snapshot: + raise RollbackAborted(f"버전 {target_version} 은 성공적으로 빌드된 적이 없어 되돌릴 수 없다") + if site.current_version_id == version.site_version_id: + raise RollbackAborted(f"이미 지금 공개 중인 버전이다(v{target_version})") + + result = { + "place_id": place_id, "site_id": str(site.site_id), + "target_version": target_version, "site_version_id": str(version.site_version_id), + } + + async def _fail(reason: str, gate: publish_gate.GateResult | None = None): + await _log(site.site_id, version.site_version_id, PublishResult.REJECTED if gate else PublishResult.FAILED, + gate, payload.get("requested_by")) + result["rolled_back"] = False + result["error"] = reason + LOG.w(f"[rollback] place={place_id} v{target_version} 실패: {reason}") + return result + + # ★ snapshot 을 그대로 옮긴다 — 재수집하지 않는다(파일 머리주석 참조). + links = await load_channel_links(place_id) + payload_dict = await site_payload.prepare_site_payload( + place, version.snapshot, site, version, links, publish=True + ) + payload_path = site_payload.write_payload(payload_dict) + result["payload_path"] = payload_path + + try: + report = await render_service.render_site(payload_path, target_version, render_timeout_sec) + except render_service.RenderFailed as ex: + return await _fail(str(ex)) + + mismatches = list(report.get("mismatches") or []) + unique_count_raw = report.get("uniqueContentCount") + + # ★ 렌더러가 그때와 달라졌을 수 있으므로 게이트를 다시 돈다(파일 머리주석 참조) — + # fact 검증 게이트(1차)는 건너뛴다. snapshot 은 이미 확인된 값만 담아 발행했던 것이라 + # 다시 물을 것이 없다 — 여기서 또 물으면 "그때는 됐는데 지금은 안 된다"는 모순이 난다. + gate = publish_gate.evaluate( + PlaceCategory(place.category), version.snapshot.get("facts") or [], unique_count_raw, mismatches + ) + result["gate"] = {"passed": gate.passed, **gate.as_log()} + if not gate.passed: + return await _fail(f"{gate.reason.name}: {gate.as_log()}", gate) + if not report.get("ok"): + return await _fail(str(report.get("error") or "렌더 실패")) + + slug = site_payload.publish_slug(place, site) + try: + await render_service.activate_site(slug, target_version) + await azure_static.publish(slug) + except (render_service.RenderFailed, azure_static.AzurePublishError) as ex: + return await _fail(str(ex)) + await indexnow.submit(slug) + + now = GTime.UTC() + site_update = { + "status": SiteStatus.PUBLISHED.value, + "current_version_id": version.site_version_id, + "published_at": now, + } + await DB_SESSION_MNG.execute_lambda_claim( + sites.DBType(), lambda s: _site_crud.update_site(s, site.site_id, site_update) + ) + await _log(site.site_id, version.site_version_id, PublishResult.SUCCESS, None, payload.get("requested_by")) + + result["rolled_back"] = True + LOG.i(f"[rollback] place={place_id} v{target_version} 로 되돌림") + return result diff --git a/solution/backend/services/site_payload.py b/solution/backend/services/site_payload.py index e8f6ae3..b242d32 100644 --- a/solution/backend/services/site_payload.py +++ b/solution/backend/services/site_payload.py @@ -39,8 +39,13 @@ from services.stay_guide import nol_stay_guide SCHEMA_VERSION = 1 # 출력 디렉토리. 컨테이너 밖(볼륨·오브젝트 스토리지)으로 빼기 쉬우라고 env 로 둔다. +# ★ 기본값이 `solution/site/payloads` 아래인 이유 — 렌더러(prerender.ts)가 songs·out 디렉토리를 +# **자기 파일 위치 기준 상대경로**로 찾는다(SITE_ROOT = dist/prerender/../..). 워커가 그 +# 렌더러를 subprocess 로 직접 띄우면서(render_service.py) 세 디렉토리(payloads·songs·out)가 +# 그 렌더러가 실제로 설치된 자리(`/app/solution/site/`) 아래에 나란히 있어야 한다 — +# 어긋나면 워커는 payload 를 잘 쓰는데 렌더러는 다른 곳에서 songs 를 찾다가 못 찾는다. PAYLOAD_DIR_ENV = "SITE_PAYLOAD_DIR" -DEFAULT_PAYLOAD_DIR = "/app/out/payloads" +DEFAULT_PAYLOAD_DIR = "/app/solution/site/payloads" # 커스텀 도메인이 없을 때 쓰는 기본 호스트. sites.domain 이 채워지면 그 값이 이긴다. # @@ -780,11 +785,15 @@ def region_label(*addresses: str | None) -> str | None: # ── payload 조립 ────────────────────────────────────────────────────────── -def to_site_payload(place, snapshot: dict, site, version, links) -> dict: +def to_site_payload(place, snapshot: dict, site, version, links, publish: bool = False) -> dict: """스냅샷 + 사이트/버전 행 + 채널 링크 → SitePayload(dict). 순수 변환 함수다. DB 도 파일도 건드리지 않는다 — 그래야 테스트가 쉽고, - 같은 입력이면 언제나 같은 payload 가 나온다.""" + 같은 입력이면 언제나 같은 payload 가 나온다. + + ★ `publish` 는 렌더러(prerender.ts publishVersion)에게 "이 버전으로 공개 주소를 + 넘겨도 되는가"를 알리는 신호다. False(미리보기·게이트 통과 전 재빌드)면 렌더러가 + `out/versions///` 에만 굽고 `out/s/` 심볼릭 링크는 그대로 둔다.""" snapshot = snapshot or {} snap_place = snapshot.get("place") or {} place_id = str(_get(place, "place_id")) @@ -929,6 +938,7 @@ def to_site_payload(place, snapshot: dict, site, version, links) -> dict: "publishedAt": published_at or updated_at, "updatedAt": updated_at, "version": int(_get(version, "version") or 1), + "publish": bool(publish), }, "place": { "name": name, @@ -1037,9 +1047,9 @@ def write_payload(payload: dict) -> str: return str(path) -async def prepare_site_payload(place, snapshot: dict, site, version, links) -> dict: +async def prepare_site_payload(place, snapshot: dict, site, version, links, publish: bool = False) -> dict: """미리보기·발행 공통 보강. 요약은 DB 스냅샷이 아니라 응답/산출물에만 싣는다.""" - payload = to_site_payload(place, snapshot, site, version, links) + payload = to_site_payload(place, snapshot, site, version, links, publish) for fact in payload["facts"]: if fact["key"] != "intro" or fact["status"] not in {s.value for s in PUBLISHABLE_FACT_STATUSES}: continue @@ -1054,14 +1064,14 @@ async def prepare_site_payload(place, snapshot: dict, site, version, links) -> d return payload -async def emit_payload(place, snapshot: dict, site, version, links) -> str | None: +async def emit_payload(place, snapshot: dict, site, version, links, publish: bool = False) -> str | None: """payload 조립 + 파일 쓰기. 실패해도 예외를 밖으로 내보내지 않는다. ★ 발행 자체를 실패시키면 안 된다 — 게이트를 통과해 DB 에 남은 발행 기록은 이미 정확하고, payload 는 그것을 화면으로 옮기는 부수 산출물이다. 디스크가 없거나 권한이 없어서 발행이 되돌려지는 게 더 나쁘다. 대신 경고 로그로 반드시 드러낸다.""" try: - payload = await prepare_site_payload(place, snapshot, site, version, links) + payload = await prepare_site_payload(place, snapshot, site, version, links, publish) path = write_payload(payload) LOG.i( f"[payload] {payload['site']['slug']} → {path} " diff --git a/solution/backend/services/site_service.py b/solution/backend/services/site_service.py index 31c8341..61ab399 100644 --- a/solution/backend/services/site_service.py +++ b/solution/backend/services/site_service.py @@ -29,6 +29,7 @@ from router.v1.site.protocol import ( Req_SiteStatus, Req_SiteTemplate, Req_SiteTheme, + Req_Rollback, Req_StartBuild, Res_MySites, Res_PublishLogs, @@ -166,7 +167,7 @@ class SiteService: (`snapshot.build_snapshot` → `site_payload.prepare_site_payload`)를 그대로 거치므로, 여기서 갈릴 자리가 없다. 버전은 아직 없으니 0 으로 넘긴다 — 화면에 안 쓰인다. """ - from services.build_service import ensure_site, _load_links + from services.build_service import ensure_site, load_channel_links from services.site_payload import prepare_site_payload from services.snapshot import build_snapshot @@ -175,7 +176,7 @@ class SiteService: return None site = await ensure_site(place_id) - links = await _load_links(place_id) + links = await load_channel_links(place_id) snapshot = await build_snapshot(place) # ★ version 은 None 이다. 발행 전이라 버전 행이 없고, payload 의 site.version 은 # 캐시 무효화 키라 미리보기에서는 뜻이 없다(to_site_payload 가 0 으로 떨어뜨린다). @@ -570,6 +571,35 @@ class SiteService: res.created = created return res + async def start_rollback(self, user_info: UserInfo, place_id: str, req: Req_Rollback) -> Res_StartBuild: + """예전 버전으로 공개 주소를 되돌리는 잡을 큐에 넣는다. + + ★ dedupe_key 를 빌드와 **같은 것**(`build:{place_id}`)을 쓴다 — 롤백 도중에 새 발행이 + 끼어들거나 그 반대가 되면 어느 쪽이 이겼는지 알 수 없는 상태가 된다. 이 사이트의 + 공개 주소를 바꾸는 작업은 항상 하나만 활성화된다.""" + res = Res_StartBuild() + err_type, _place = await self._load_place(user_info, place_id) + if err_type != ErrorType.SUCCESS: + res.result.SetResult(err_type) + return res + + job_id, created = await enqueue_job( + self.queue, JobType.ROLLBACK, + { + "place_id": place_id, "owner_user_id": user_info.user_id, + "target_version": req.target_version, "requested_by": user_info.user_id, + }, + dedupe_key=f"build:{place_id}", + ) + if job_id is None: + res.result.SetResult(ErrorType.COLLECT_ALREADY_RUNNING) + return res + + res.job_id = uuid.UUID(job_id) + res.status = JobStatus.PENDING + res.created = created + return res + async def list_versions(self, user_info: UserInfo, place_id: str) -> Res_SiteVersions: from sqlalchemy import select diff --git a/solution/backend/services/story_service.py b/solution/backend/services/story_service.py index 75f101d..fdcc82d 100644 --- a/solution/backend/services/story_service.py +++ b/solution/backend/services/story_service.py @@ -64,7 +64,7 @@ async def _generate_kind(client: httpx.AsyncClient, kind: str, region_label: str {"role": "system", "content": prompts.SYSTEM_PROMPT}, {"role": "user", "content": prompts.build_prompt(kind, region_label)}, ], - "max_tokens": perplexity.DEFAULT_MAX_TOKENS, + "max_tokens": _MAX_TOKENS.get(kind, perplexity.DEFAULT_MAX_TOKENS), } try: payload = await perplexity.call(body, client=client) @@ -80,6 +80,15 @@ async def _generate_kind(client: httpx.AsyncClient, kind: str, region_label: str return items, dropped +# 종류별 응답 토큰 상한. 기본(2048)으로 모자란 종류만 적는다. +# +# ★ 왜 (실측 2026-09-15) `reading` 은 30~34꼭지 × 서너 문장이라 다른 종류의 서너 배다. +# 2048 로 부르면 응답이 **문장 한가운데서 잘려** 오고, 파서는 그걸 "JSON 이 아니다" 로 +# 통째로 버린다 — 여섯 지역 전부 0건이었다. 잘린 것은 재시도해도 같은 자리에서 잘린다. +# ★ 상한만 올린다. 꼭지 수를 줄이면 화면이 매번 5~6개만 뽑는 의미가 없어진다 +# (`site/sections/items/ReadingSection.tsx`). +_MAX_TOKENS = {"reading": 8000} + # 사진을 무엇으로 찾을지. 종류마다 출처가 다르다. # # chronicle · postcard 공공데이터(TourAPI) — 장소·시설 사진. 공공누리 Type1/Type3 만 diff --git a/solution/backend/tests/test_build_publish.py b/solution/backend/tests/test_build_publish.py index 844fc4f..f233d98 100644 --- a/solution/backend/tests/test_build_publish.py +++ b/solution/backend/tests/test_build_publish.py @@ -100,10 +100,10 @@ async def test_unverified_fact_blocks_publish(auth_headers, client, db_engine): (필수 항목 누락은 2026-08-27 부터 막지 않는다. 그래서 여기서 걸리는 사유가 바뀌었다.)""" h = await auth_headers("u1") pid = await _place(client, h, "미검증펜션") - # 크롤링으로만 들어온 값 = 후보 상태 + # 크롤링은 바로 노출되는 정책이다. 실제 미검증 상태를 만드는 API 출처로 검사한다. for k, v in REQUIRED.items(): await client.post(f"/v1/place/{pid}/fact", headers=h, json={ - "key": k, "value": v, "source_type": SourceType.CRAWL.value, "source_url": "https://ota.test/1"}) + "key": k, "value": v, "source_type": SourceType.API.value, "source_url": "https://api.test/1"}) job_id = (await client.post(f"/v1/place/{pid}/site/build", headers=h, json={"publish": True})).json()["job_id"] await _run() @@ -292,17 +292,17 @@ async def _build(client, h, pid, publish=True): async def test_렌더가_실패하면_발행하지_않는다(auth_headers, client, db_engine, monkeypatch): """검증: 렌더러가 페이지를 굽지 못했다고 보고한다. 기대결과: ★ FAILED. 페이지가 없는데 "발행됨"으로 남으면 사장님은 404 를 눌러야 안다.""" - from services import render_report + from services import render_service - async def _broken(slug, site_version, timeout_sec, poll_sec=1.0): - return {"schemaVersion": 1, "slug": slug, "siteId": "", "placeId": "", + async def _broken(payload_path, site_version, timeout_sec): + return {"schemaVersion": 1, "slug": "t", "siteId": "", "placeId": "", "siteVersion": site_version, "ok": False, "renderedAt": "2026-01-01T00:00:00Z", "routes": 0, "bundle": "t.js", "uniqueContentCount": 5, "jsonld": [], "mismatches": [], "error": "번들이 없다"} h = await auth_headers("u1") pid = await _built_place(client, h, db_engine) - monkeypatch.setattr(render_report, "wait_for", _broken) + monkeypatch.setattr(render_service, "render_site", _broken) r = await _build(client, h, pid) assert r["build_status"] == "FAILED" @@ -316,10 +316,10 @@ async def test_렌더가_실패하면_발행하지_않는다(auth_headers, clien async def test_구조화데이터가_화면과_다르면_발행하지_않는다(auth_headers, client, db_engine, monkeypatch): """검증: 렌더러가 JSON-LD 불일치를 보고한다. 기대결과: ★ JSONLD_MISMATCH — 검색엔진에만 다른 말을 하는 페이지는 나가면 안 된다.""" - from services import render_report + from services import render_service - async def _mismatch(slug, site_version, timeout_sec, poll_sec=1.0): - return {"schemaVersion": 1, "slug": slug, "siteId": "", "placeId": "", + async def _mismatch(payload_path, site_version, timeout_sec): + return {"schemaVersion": 1, "slug": "t", "siteId": "", "placeId": "", "siteVersion": site_version, "ok": False, "renderedAt": "2026-01-01T00:00:00Z", "routes": 0, "bundle": "t.js", "uniqueContentCount": 5, "jsonld": [], "mismatches": ["telephone: '02-0000-0000' 이 화면에 없다"], @@ -327,7 +327,7 @@ async def test_구조화데이터가_화면과_다르면_발행하지_않는다( h = await auth_headers("u1") pid = await _built_place(client, h, db_engine) - monkeypatch.setattr(render_report, "wait_for", _mismatch) + monkeypatch.setattr(render_service, "render_site", _mismatch) r = await _build(client, h, pid) assert r["build_status"] == "FAILED" @@ -339,14 +339,14 @@ async def test_구조화데이터가_화면과_다르면_발행하지_않는다( async def test_렌더_결과가_안_오면_발행하지_않는다(auth_headers, client, db_engine, monkeypatch): """검증: 렌더러가 죽어 보고서가 오지 않는다(타임아웃). 기대결과: ★ FAILED. 확인하지 못한 것을 발행됨으로 남기지 않는다.""" - from services import render_report + from services import render_service - async def _timeout(slug, site_version, timeout_sec, poll_sec=1.0): - return None + async def _timeout(payload_path, site_version, timeout_sec): + raise render_service.RenderFailed("렌더 결과를 받지 못했다 — 렌더러가 도는지 확인이 필요하다") h = await auth_headers("u1") pid = await _built_place(client, h, db_engine) - monkeypatch.setattr(render_report, "wait_for", _timeout) + monkeypatch.setattr(render_service, "render_site", _timeout) r = await _build(client, h, pid) assert r["build_status"] == "FAILED" diff --git a/solution/backend/tests/test_render_process.py b/solution/backend/tests/test_render_process.py new file mode 100644 index 0000000..f4f28d0 --- /dev/null +++ b/solution/backend/tests/test_render_process.py @@ -0,0 +1,38 @@ +import asyncio +import sys + +import pytest + +from services import render_service + + +@pytest.fixture(autouse=True) +def fake_renderer(monkeypatch, tmp_path): + # 이 파일은 conftest의 렌더 대역 대신 실제 자식 프로세스 수명만 검사한다. + monkeypatch.setenv("SITE_OUTPUT_DIR", str(tmp_path)) + + +async def test_process_success(): + code, _ = await render_service._run_subprocess([sys.executable, "-c", "print(1)"], 2) + assert code == 0 + + +async def test_timeout_releases_lock(): + code, _ = await render_service._run_subprocess( + [sys.executable, "-c", "import time;time.sleep(5)"], 0.1 + ) + assert code is None + code, _ = await render_service._run_subprocess([sys.executable, "-c", "print(1)"], 2) + assert code == 0 + + +async def test_cancel_releases_lock(): + task = asyncio.create_task(render_service._run_subprocess( + [sys.executable, "-c", "import time;time.sleep(5)"], 10 + )) + await asyncio.sleep(0.1) + task.cancel() + with pytest.raises(asyncio.CancelledError): + await task + code, _ = await render_service._run_subprocess([sys.executable, "-c", "print(1)"], 2) + assert code == 0 diff --git a/solution/backend/tests/test_rollback.py b/solution/backend/tests/test_rollback.py new file mode 100644 index 0000000..0056152 --- /dev/null +++ b/solution/backend/tests/test_rollback.py @@ -0,0 +1,131 @@ +"""롤백 — 예전 버전으로 공개 주소를 되돌린다. + +★ 이 경로가 절대 하면 안 되는 것: + - 재수집·재생성으로 스냅샷을 새로 만드는 것 (그 버전이 발행됐던 그대로여야 한다) + - 실패했는데 site.current_version_id 를 바꾸는 것 (직전 공개본이 그대로 있어야 한다) + - 지금 공개 중인 버전으로 "롤백"을 허용하는 것 (의미 없는 잡을 만든다) +""" +import uuid + +from common.enums import BuildStatus, JobStatus, SiteStatus +from crud.job_crud import JobQueue +from worker.handlers import build_handler +from worker.runner import Worker + +from tests.test_build_publish import _approved_media, _place, _verified_facts + + +async def _run(): + assert await Worker("w", JobQueue(), build_handler(), job_deadline_sec=60).process_one() is True + + +async def _publish(client, h, pid, intro="하조대 해변 도보 3분 거리의 펜션입니다."): + await _verified_facts(client, h, pid, {"intro": intro}) + job_id = (await client.post(f"/v1/place/{pid}/site/build", headers=h, json={"publish": True})).json()["job_id"] + await _run() + return (await client.get(f"/v1/job/{job_id}", headers=h)).json()["job"]["result"] + + +async def test_rollback_to_earlier_version(auth_headers, client, db_engine): + """검증: v1 발행 → v2 발행 → v1 로 롤백. + 기대결과: 공개 버전이 다시 v1 이 된다. 롤백은 새 site_versions 행을 만들지 않는다.""" + h = await auth_headers("u1") + pid = await _place(client, h) + await _approved_media(db_engine, pid) + + r1 = await _publish(client, h, pid, "v1 소개 — 하조대 해변 도보 3분 거리의 펜션입니다.") + assert r1["published"] is True + r2 = await _publish(client, h, pid, "v2 소개 — 리모델링을 마친 하조대 앞 오션뷰 펜션입니다.") + assert r2["published"] is True + assert r2["version"] == r1["version"] + 1 + + job_id = (await client.post( + f"/v1/place/{pid}/site/version/rollback", headers=h, json={"target_version": r1["version"]} + )).json()["job_id"] + await _run() + job = (await client.get(f"/v1/job/{job_id}", headers=h)).json()["job"] + assert job["status"] == JobStatus.DONE.value, job.get("last_error") + result = job["result"] + assert result["rolled_back"] is True, result.get("error") + assert result["target_version"] == r1["version"] + + site = (await client.get(f"/v1/place/{pid}/site", headers=h)).json() + assert site["site"]["status"] == SiteStatus.PUBLISHED.value + assert site["current_version"]["version"] == r1["version"] + assert site["current_version"]["build_status"] == BuildStatus.BUILT.value + + versions = (await client.get(f"/v1/place/{pid}/site/version/list", headers=h)).json()["versions"] + # ★ 롤백은 새 버전을 만들지 않는다 — v1·v2 두 개뿐이어야 한다. + assert len(versions) == 2 + + +async def test_rollback_rejects_current_version(auth_headers, client, db_engine): + """검증: 지금 공개 중인 버전으로 롤백을 시도한다. + 기대결과: ★ 성공으로 끝나지 않는다(last_error) — 의미 없는 재굽기를 하지 않는다. + (BuildAborted 와 같은 패턴 — 재시도해도 소용없는 중단이라 PENDING(백오프)으로 남고, + 소진되면 DEAD 된다. 여기서 보는 건 "그래도 site 상태는 안 바뀐다"는 것이다.)""" + h = await auth_headers("u1") + pid = await _place(client, h) + await _approved_media(db_engine, pid) + r1 = await _publish(client, h, pid) + + job_id = (await client.post( + f"/v1/place/{pid}/site/version/rollback", headers=h, json={"target_version": r1["version"]} + )).json()["job_id"] + await _run() + job = (await client.get(f"/v1/job/{job_id}", headers=h)).json()["job"] + assert job["status"] != JobStatus.DONE.value + assert "이미" in (job.get("last_error") or "") + + site = (await client.get(f"/v1/place/{pid}/site", headers=h)).json() + assert site["current_version"]["version"] == r1["version"] + + +async def test_rollback_rejects_unknown_version(auth_headers, client, db_engine): + """검증: 존재하지 않는 버전 번호로 롤백을 시도한다. + 기대결과: ★ 잡이 성공으로 끝나지 않는다 — 공개 상태는 그대로다.""" + h = await auth_headers("u1") + pid = await _place(client, h) + await _approved_media(db_engine, pid) + r1 = await _publish(client, h, pid) + + job_id = (await client.post( + f"/v1/place/{pid}/site/version/rollback", headers=h, json={"target_version": 999} + )).json()["job_id"] + await _run() + job = (await client.get(f"/v1/job/{job_id}", headers=h)).json()["job"] + assert job["status"] != JobStatus.DONE.value + + site = (await client.get(f"/v1/place/{pid}/site", headers=h)).json() + assert site["site"]["status"] == SiteStatus.PUBLISHED.value + assert site["current_version"]["version"] == r1["version"] + + +async def test_failed_rollback_keeps_current_published_version(auth_headers, client, db_engine, monkeypatch): + """검증: 롤백 대상 렌더가 실패한다(구조화 데이터 불일치로 시뮬레이션). + 기대결과: ★ site.current_version_id 는 바뀌지 않는다 — 직전 공개본이 그대로 서비스된다.""" + from services import render_service + + h = await auth_headers("u1") + pid = await _place(client, h) + await _approved_media(db_engine, pid) + r1 = await _publish(client, h, pid, "v1 소개 — 하조대 해변 도보 3분 거리의 펜션입니다.") + r2 = await _publish(client, h, pid, "v2 소개 — 리모델링을 마친 하조대 앞 오션뷰 펜션입니다.") + site_before = (await client.get(f"/v1/place/{pid}/site", headers=h)).json() + assert site_before["current_version"]["version"] == r2["version"] + + async def _broken(payload_path, site_version, timeout_sec): + raise render_service.RenderFailed("렌더러가 죽었다(시뮬레이션)") + + monkeypatch.setattr(render_service, "render_site", _broken) + + job_id = (await client.post( + f"/v1/place/{pid}/site/version/rollback", headers=h, json={"target_version": r1["version"]} + )).json()["job_id"] + await _run() + job = (await client.get(f"/v1/job/{job_id}", headers=h)).json()["job"] + assert job["status"] == JobStatus.DONE.value + assert job["result"]["rolled_back"] is False + + site_after = (await client.get(f"/v1/place/{pid}/site", headers=h)).json() + assert site_after["current_version"]["version"] == r2["version"], "실패했는데 공개 버전이 바뀌었다" diff --git a/solution/backend/worker/handlers.py b/solution/backend/worker/handlers.py index 05da067..10903e2 100644 --- a/solution/backend/worker/handlers.py +++ b/solution/backend/worker/handlers.py @@ -16,6 +16,7 @@ JobType.BUILD ✓ services/build_service.run_build — 정적 빌드 + 발행 검수 게이트 JobType.LOCAL_SYNC ✓ services/story_service.run_local_sync — 지역 이야기 생성(지역당 1회) JobType.SONG ✓ services/song_service.run_song — 이 숙소의 노래 한 곡(가사 Gemini → 작곡 Suno) + JobType.ROLLBACK ✓ services/rollback_service.run_rollback — 예전 버전으로 공개 주소를 되돌림 JobType.AI_CHECK → reports 모듈이 붙을 때 """ @@ -82,6 +83,7 @@ def _register_builtin(): from services.copy_service import run_copy from services.story_service import run_local_sync from services.song_service import run_song + from services.rollback_service import run_rollback from services.vision_service import run_vision if JobType.COLLECT.value not in HANDLERS: @@ -96,6 +98,8 @@ def _register_builtin(): HANDLERS[JobType.LOCAL_SYNC.value] = run_local_sync if JobType.SONG.value not in HANDLERS: HANDLERS[JobType.SONG.value] = run_song + if JobType.ROLLBACK.value not in HANDLERS: + HANDLERS[JobType.ROLLBACK.value] = run_rollback _register_builtin() diff --git a/solution/frontend/src/api/generated/auth/auth.ts b/solution/frontend/src/api/generated/auth/auth.ts index 65237d5..abfa289 100644 --- a/solution/frontend/src/api/generated/auth/auth.ts +++ b/solution/frontend/src/api/generated/auth/auth.ts @@ -4,10 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import { - useMutation, - useQuery -} from '@tanstack/react-query'; +import { useMutation, useQuery } from "@tanstack/react-query"; import type { DataTag, DefinedInitialDataOptions, @@ -20,8 +17,8 @@ import type { UseMutationOptions, UseMutationResult, UseQueryOptions, - UseQueryResult -} from '@tanstack/react-query'; + UseQueryResult, +} from "@tanstack/react-query"; import type { HTTPValidationError, @@ -31,429 +28,576 @@ import type { ReqUpdateMe, ResLogin, ResMe, - ResRefreshToken -} from '.././model'; - -import { customFetch } from '../../mutator/custom-fetch'; + ResRefreshToken, +} from ".././model"; +import { customFetch } from "../../mutator/custom-fetch"; type SecondParameter unknown> = Parameters[1]; - - /** * id/pw 로 로그인하고 JWT 토큰을 발급한다. * @summary 로그인 */ export const login = ( - reqLogin: ReqLogin, - options?: SecondParameter,signal?: AbortSignal + reqLogin: ReqLogin, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/auth/login`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqLogin, signal + return customFetch( + { + url: `/v1/auth/login`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqLogin, + signal, }, - options); - } - + options, + ); +}; +export const getLoginMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ReqLogin }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: ReqLogin }, + TContext +> => { + const mutationKey = ["login"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getLoginMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqLogin}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{data: ReqLogin}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { data: ReqLogin } + > = (props) => { + const { data } = props ?? {}; -const mutationKey = ['login']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return login(data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type LoginMutationResult = NonNullable< + Awaited> +>; +export type LoginMutationBody = ReqLogin; +export type LoginMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {data: ReqLogin}> = (props) => { - const {data} = props ?? {}; - - return login(data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type LoginMutationResult = NonNullable>> - export type LoginMutationBody = ReqLogin - export type LoginMutationError = void | HTTPValidationError - - /** +/** * @summary 로그인 */ -export const useLogin = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqLogin}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {data: ReqLogin}, - TContext - > => { +export const useLogin = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ReqLogin }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: ReqLogin }, + TContext +> => { + const mutationOptions = getLoginMutationOptions(options); - const mutationOptions = getLoginMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * id/pw 로 가입한다. 회사(테넌트) 1개가 함께 생기고, 성공하면 곧바로 로그인 토큰을 발급한다. * @summary 회원가입 */ export const signup = ( - reqSignup: ReqSignup, - options?: SecondParameter,signal?: AbortSignal + reqSignup: ReqSignup, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/auth/signup`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqSignup, signal + return customFetch( + { + url: `/v1/auth/signup`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqSignup, + signal, }, - options); - } - + options, + ); +}; +export const getSignupMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ReqSignup }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: ReqSignup }, + TContext +> => { + const mutationKey = ["signup"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getSignupMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqSignup}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{data: ReqSignup}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { data: ReqSignup } + > = (props) => { + const { data } = props ?? {}; -const mutationKey = ['signup']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return signup(data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type SignupMutationResult = NonNullable< + Awaited> +>; +export type SignupMutationBody = ReqSignup; +export type SignupMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {data: ReqSignup}> = (props) => { - const {data} = props ?? {}; - - return signup(data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type SignupMutationResult = NonNullable>> - export type SignupMutationBody = ReqSignup - export type SignupMutationError = void | HTTPValidationError - - /** +/** * @summary 회원가입 */ -export const useSignup = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqSignup}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {data: ReqSignup}, - TContext - > => { +export const useSignup = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ReqSignup }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: ReqSignup }, + TContext +> => { + const mutationOptions = getSignupMutationOptions(options); - const mutationOptions = getSignupMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * 구글 ID 토큰(GIS credential)을 검증하고 JWT 토큰을 발급한다. 처음 온 계정은 그 자리에서 만든다. * @summary 구글 로그인 */ export const googleLogin = ( - reqGoogleLogin: ReqGoogleLogin, - options?: SecondParameter,signal?: AbortSignal + reqGoogleLogin: ReqGoogleLogin, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/auth/google`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqGoogleLogin, signal + return customFetch( + { + url: `/v1/auth/google`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqGoogleLogin, + signal, }, - options); - } - + options, + ); +}; +export const getGoogleLoginMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ReqGoogleLogin }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: ReqGoogleLogin }, + TContext +> => { + const mutationKey = ["googleLogin"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getGoogleLoginMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqGoogleLogin}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{data: ReqGoogleLogin}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { data: ReqGoogleLogin } + > = (props) => { + const { data } = props ?? {}; -const mutationKey = ['googleLogin']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return googleLogin(data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type GoogleLoginMutationResult = NonNullable< + Awaited> +>; +export type GoogleLoginMutationBody = ReqGoogleLogin; +export type GoogleLoginMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {data: ReqGoogleLogin}> = (props) => { - const {data} = props ?? {}; - - return googleLogin(data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type GoogleLoginMutationResult = NonNullable>> - export type GoogleLoginMutationBody = ReqGoogleLogin - export type GoogleLoginMutationError = void | HTTPValidationError - - /** +/** * @summary 구글 로그인 */ -export const useGoogleLogin = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqGoogleLogin}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {data: ReqGoogleLogin}, - TContext - > => { +export const useGoogleLogin = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ReqGoogleLogin }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: ReqGoogleLogin }, + TContext +> => { + const mutationOptions = getGoogleLoginMutationOptions(options); - const mutationOptions = getGoogleLoginMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * refresh 토큰으로 access 토큰을 재발급한다. * @summary 액세스 토큰 갱신 */ export const refreshToken = ( - - options?: SecondParameter,signal?: AbortSignal + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/auth/refresh_token`, method: 'POST', signal - }, - options); - } - + return customFetch( + { url: `/v1/auth/refresh_token`, method: "POST", signal }, + options, + ); +}; +export const getRefreshTokenMutationOptions = < + TError = void, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + void, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + void, + TContext +> => { + const mutationKey = ["refreshToken"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getRefreshTokenMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,void, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,void, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + void + > = () => { + return refreshToken(requestOptions); + }; -const mutationKey = ['refreshToken']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return { mutationFn, ...mutationOptions }; +}; - +export type RefreshTokenMutationResult = NonNullable< + Awaited> +>; +export type RefreshTokenMutationError = void; - const mutationFn: MutationFunction>, void> = () => { - - - return refreshToken(requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type RefreshTokenMutationResult = NonNullable>> - - export type RefreshTokenMutationError = void - - /** +/** * @summary 액세스 토큰 갱신 */ -export const useRefreshToken = (options?: { mutation?:UseMutationOptions>, TError,void, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - void, - TContext - > => { +export const useRefreshToken = ( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + void, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + void, + TContext +> => { + const mutationOptions = getRefreshTokenMutationOptions(options); - const mutationOptions = getRefreshTokenMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * 유효한 access 토큰이 있어야 호출 가능. 토큰의 유저+회사 정보를 반환한다. * @summary 내 정보 */ export const me = ( - - options?: SecondParameter,signal?: AbortSignal + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/auth/me`, method: 'GET', signal - }, - options); - } - - - + return customFetch( + { url: `/v1/auth/me`, method: "GET", signal }, + options, + ); +}; export const getMeQueryKey = () => { - return [ - `/v1/auth/me` - ] as const; - } + return [`/v1/auth/me`] as const; +}; - -export const getMeQueryOptions = >, TError = void>( options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} -) => { +export const getMeQueryOptions = < + TData = Awaited>, + TError = void, +>(options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getMeQueryKey(); - const queryKey = queryOptions?.queryKey ?? getMeQueryKey(); + const queryFn: QueryFunction>> = ({ signal }) => + me(requestOptions, signal); - - - const queryFn: QueryFunction>> = ({ signal }) => me(requestOptions, signal); - - - - - - return { queryKey, queryFn, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type MeQueryResult = NonNullable>> -export type MeQueryError = void + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; +export type MeQueryResult = NonNullable>>; +export type MeQueryError = void; export function useMe>, TError = void>( - options: { query:Partial>, TError, TData>> & Pick< + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; export function useMe>, TError = void>( - options?: { query?:Partial>, TError, TData>> & Pick< + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; export function useMe>, TError = void>( - options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 내 정보 */ export function useMe>, TError = void>( - options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getMeQueryOptions(options); - const queryOptions = getMeQueryOptions(options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * 본인 이름/이메일/연락처/비밀번호를 수정한다(권한·소속·ID 변경 불가). * @summary 내 정보 수정 */ export const updateMe = ( - reqUpdateMe: ReqUpdateMe, - options?: SecondParameter,) => { - - - return customFetch( - {url: `/v1/auth/me`, method: 'PATCH', - headers: {'Content-Type': 'application/json', }, - data: reqUpdateMe + reqUpdateMe: ReqUpdateMe, + options?: SecondParameter, +) => { + return customFetch( + { + url: `/v1/auth/me`, + method: "PATCH", + headers: { "Content-Type": "application/json" }, + data: reqUpdateMe, }, - options); - } - + options, + ); +}; +export const getUpdateMeMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ReqUpdateMe }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: ReqUpdateMe }, + TContext +> => { + const mutationKey = ["updateMe"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getUpdateMeMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqUpdateMe}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{data: ReqUpdateMe}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { data: ReqUpdateMe } + > = (props) => { + const { data } = props ?? {}; -const mutationKey = ['updateMe']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return updateMe(data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type UpdateMeMutationResult = NonNullable< + Awaited> +>; +export type UpdateMeMutationBody = ReqUpdateMe; +export type UpdateMeMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {data: ReqUpdateMe}> = (props) => { - const {data} = props ?? {}; - - return updateMe(data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type UpdateMeMutationResult = NonNullable>> - export type UpdateMeMutationBody = ReqUpdateMe - export type UpdateMeMutationError = void | HTTPValidationError - - /** +/** * @summary 내 정보 수정 */ -export const useUpdateMe = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqUpdateMe}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {data: ReqUpdateMe}, - TContext - > => { +export const useUpdateMe = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ReqUpdateMe }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: ReqUpdateMe }, + TContext +> => { + const mutationOptions = getUpdateMeMutationOptions(options); - const mutationOptions = getUpdateMeMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - \ No newline at end of file + return useMutation(mutationOptions, queryClient); +}; diff --git a/solution/frontend/src/api/generated/default/default.ts b/solution/frontend/src/api/generated/default/default.ts index 6b08931..3e5ee12 100644 --- a/solution/frontend/src/api/generated/default/default.ts +++ b/solution/frontend/src/api/generated/default/default.ts @@ -4,9 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import { - useQuery -} from '@tanstack/react-query'; +import { useQuery } from "@tanstack/react-query"; import type { DataTag, DefinedInitialDataOptions, @@ -16,105 +14,163 @@ import type { QueryKey, UndefinedInitialDataOptions, UseQueryOptions, - UseQueryResult -} from '@tanstack/react-query'; - -import { customFetch } from '../../mutator/custom-fetch'; + UseQueryResult, +} from "@tanstack/react-query"; +import { customFetch } from "../../mutator/custom-fetch"; type SecondParameter unknown> = Parameters[1]; - - /** * @summary Healthz */ export const healthzHealthzGet = ( - - options?: SecondParameter,signal?: AbortSignal + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/healthz`, method: 'GET', signal - }, - options); - } - - - + return customFetch( + { url: `/healthz`, method: "GET", signal }, + options, + ); +}; export const getHealthzHealthzGetQueryKey = () => { - return [ - `/healthz` - ] as const; - } + return [`/healthz`] as const; +}; - -export const getHealthzHealthzGetQueryOptions = >, TError = void>( options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} -) => { +export const getHealthzHealthzGetQueryOptions = < + TData = Awaited>, + TError = void, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getHealthzHealthzGetQueryKey(); - const queryKey = queryOptions?.queryKey ?? getHealthzHealthzGetQueryKey(); + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => healthzHealthzGet(requestOptions, signal); - + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; - const queryFn: QueryFunction>> = ({ signal }) => healthzHealthzGet(requestOptions, signal); +export type HealthzHealthzGetQueryResult = NonNullable< + Awaited> +>; +export type HealthzHealthzGetQueryError = void; - - - - - return { queryKey, queryFn, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type HealthzHealthzGetQueryResult = NonNullable>> -export type HealthzHealthzGetQueryError = void - - -export function useHealthzHealthzGet>, TError = void>( - options: { query:Partial>, TError, TData>> & Pick< +export function useHealthzHealthzGet< + TData = Awaited>, + TError = void, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useHealthzHealthzGet>, TError = void>( - options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useHealthzHealthzGet< + TData = Awaited>, + TError = void, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useHealthzHealthzGet>, TError = void>( - options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useHealthzHealthzGet< + TData = Awaited>, + TError = void, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary Healthz */ -export function useHealthzHealthzGet>, TError = void>( - options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useHealthzHealthzGet< + TData = Awaited>, + TError = void, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getHealthzHealthzGetQueryOptions(options); - const queryOptions = getHealthzHealthzGetQueryOptions(options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - - diff --git a/solution/frontend/src/api/generated/fact/fact.ts b/solution/frontend/src/api/generated/fact/fact.ts index a068d43..e8d8d14 100644 --- a/solution/frontend/src/api/generated/fact/fact.ts +++ b/solution/frontend/src/api/generated/fact/fact.ts @@ -4,10 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import { - useMutation, - useQuery -} from '@tanstack/react-query'; +import { useMutation, useQuery } from "@tanstack/react-query"; import type { DataTag, DefinedInitialDataOptions, @@ -20,8 +17,8 @@ import type { UseMutationOptions, UseMutationResult, UseQueryOptions, - UseQueryResult -} from '@tanstack/react-query'; + UseQueryResult, +} from "@tanstack/react-query"; import type { HTTPValidationError, @@ -32,407 +29,595 @@ import type { ResCategorySchema, ResExtractFacts, ResFact, - ResFactList -} from '.././model'; - -import { customFetch } from '../../mutator/custom-fetch'; + ResFactList, +} from ".././model"; +import { customFetch } from "../../mutator/custom-fetch"; type SecondParameter unknown> = Parameters[1]; - - /** * 이 사업장의 업종에 존재하는 fact 필드 정의. 관리 화면이 입력 폼을 그리는 데 쓴다. * @summary 업종 fact 스키마 */ export const getSchema = ( - placeId: string, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/fact/schema`, method: 'GET', signal - }, - options); - } - + return customFetch( + { url: `/v1/place/${placeId}/fact/schema`, method: "GET", signal }, + options, + ); +}; +export const getGetSchemaQueryKey = (placeId?: string) => { + return [`/v1/place/${placeId}/fact/schema`] as const; +}; - -export const getGetSchemaQueryKey = (placeId?: string,) => { - return [ - `/v1/place/${placeId}/fact/schema` - ] as const; - } - - -export const getGetSchemaQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getGetSchemaQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, ) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getGetSchemaQueryKey(placeId); - const queryKey = queryOptions?.queryKey ?? getGetSchemaQueryKey(placeId); + const queryFn: QueryFunction>> = ({ + signal, + }) => getSchema(placeId, requestOptions, signal); - + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: DataTag; + }; +}; - const queryFn: QueryFunction>> = ({ signal }) => getSchema(placeId, requestOptions, signal); +export type GetSchemaQueryResult = NonNullable< + Awaited> +>; +export type GetSchemaQueryError = void | HTTPValidationError; - - - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type GetSchemaQueryResult = NonNullable>> -export type GetSchemaQueryError = void | HTTPValidationError - - -export function useGetSchema>, TError = void | HTTPValidationError>( - placeId: string, options: { query:Partial>, TError, TData>> & Pick< +export function useGetSchema< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useGetSchema>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetSchema< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useGetSchema>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetSchema< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 업종 fact 스키마 */ -export function useGetSchema>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useGetSchema< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetSchemaQueryOptions(placeId, options); - const queryOptions = getGetSchemaQueryOptions(placeId,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * publishable_only=true 면 ★ 사이트에 나갈 수 있는 것(VERIFIED·CORRECTED)만. * @summary fact 목록 */ export const listFacts = ( - placeId: string, - params?: ListFactsParams, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + params?: ListFactsParams, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/fact/list`, method: 'GET', - params, signal - }, - options); - } - + return customFetch( + { url: `/v1/place/${placeId}/fact/list`, method: "GET", params, signal }, + options, + ); +}; - - -export const getListFactsQueryKey = (placeId?: string, - params?: ListFactsParams,) => { - return [ - `/v1/place/${placeId}/fact/list`, ...(params ? [params]: []) - ] as const; - } - - -export const getListFactsQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, - params?: ListFactsParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getListFactsQueryKey = ( + placeId?: string, + params?: ListFactsParams, ) => { + return [ + `/v1/place/${placeId}/fact/list`, + ...(params ? [params] : []), + ] as const; +}; -const {query: queryOptions, request: requestOptions} = options ?? {}; +export const getListFactsQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListFactsParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; - const queryKey = queryOptions?.queryKey ?? getListFactsQueryKey(placeId,params); + const queryKey = + queryOptions?.queryKey ?? getListFactsQueryKey(placeId, params); - + const queryFn: QueryFunction>> = ({ + signal, + }) => listFacts(placeId, params, requestOptions, signal); - const queryFn: QueryFunction>> = ({ signal }) => listFacts(placeId,params, requestOptions, signal); + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: DataTag; + }; +}; - +export type ListFactsQueryResult = NonNullable< + Awaited> +>; +export type ListFactsQueryError = void | HTTPValidationError; - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type ListFactsQueryResult = NonNullable>> -export type ListFactsQueryError = void | HTTPValidationError - - -export function useListFacts>, TError = void | HTTPValidationError>( - placeId: string, - params: undefined | ListFactsParams, options: { query:Partial>, TError, TData>> & Pick< +export function useListFacts< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params: undefined | ListFactsParams, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useListFacts>, TError = void | HTTPValidationError>( - placeId: string, - params?: ListFactsParams, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useListFacts< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListFactsParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useListFacts>, TError = void | HTTPValidationError>( - placeId: string, - params?: ListFactsParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useListFacts< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListFactsParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary fact 목록 */ -export function useListFacts>, TError = void | HTTPValidationError>( - placeId: string, - params?: ListFactsParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useListFacts< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListFactsParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getListFactsQueryOptions(placeId, params, options); - const queryOptions = getListFactsQueryOptions(placeId,params,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * 같은 key 의 활성 fact 는 EXPIRED 로 밀리고 새 행이 들어간다(값 이력 보존). ★ 자동 수집(api/crawl/llm)은 사장님 수정본(CORRECTED)을 덮어쓸 수 없다. * @summary fact 기록 */ export const upsertFact = ( - placeId: string, - reqUpsertFact: ReqUpsertFact, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqUpsertFact: ReqUpsertFact, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/fact`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqUpsertFact, signal + return customFetch( + { + url: `/v1/place/${placeId}/fact`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqUpsertFact, + signal, }, - options); - } - + options, + ); +}; +export const getUpsertFactMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqUpsertFact }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqUpsertFact }, + TContext +> => { + const mutationKey = ["upsertFact"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getUpsertFactMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqUpsertFact}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqUpsertFact}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqUpsertFact } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['upsertFact']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return upsertFact(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type UpsertFactMutationResult = NonNullable< + Awaited> +>; +export type UpsertFactMutationBody = ReqUpsertFact; +export type UpsertFactMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqUpsertFact}> = (props) => { - const {placeId,data} = props ?? {}; - - return upsertFact(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type UpsertFactMutationResult = NonNullable>> - export type UpsertFactMutationBody = ReqUpsertFact - export type UpsertFactMutationError = void | HTTPValidationError - - /** +/** * @summary fact 기록 */ -export const useUpsertFact = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqUpsertFact}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqUpsertFact}, - TContext - > => { +export const useUpsertFact = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqUpsertFact }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqUpsertFact }, + TContext +> => { + const mutationOptions = getUpsertFactMutationOptions(options); - const mutationOptions = getUpsertFactMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * 허용 전이만 통과한다(FACT_STATUS_TRANSITIONS). CORRECTED 로 갈 때는 value 가 필수다. * @summary 검증 상태 전이 */ export const transitionFact = ( - placeId: string, - factId: string, - reqTransitionFact: ReqTransitionFact, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + factId: string, + reqTransitionFact: ReqTransitionFact, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/fact/${factId}/transition`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqTransitionFact, signal + return customFetch( + { + url: `/v1/place/${placeId}/fact/${factId}/transition`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqTransitionFact, + signal, }, - options); - } - + options, + ); +}; +export const getTransitionFactMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; factId: string; data: ReqTransitionFact }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; factId: string; data: ReqTransitionFact }, + TContext +> => { + const mutationKey = ["transitionFact"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getTransitionFactMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;factId: string;data: ReqTransitionFact}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;factId: string;data: ReqTransitionFact}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; factId: string; data: ReqTransitionFact } + > = (props) => { + const { placeId, factId, data } = props ?? {}; -const mutationKey = ['transitionFact']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return transitionFact(placeId, factId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type TransitionFactMutationResult = NonNullable< + Awaited> +>; +export type TransitionFactMutationBody = ReqTransitionFact; +export type TransitionFactMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;factId: string;data: ReqTransitionFact}> = (props) => { - const {placeId,factId,data} = props ?? {}; - - return transitionFact(placeId,factId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type TransitionFactMutationResult = NonNullable>> - export type TransitionFactMutationBody = ReqTransitionFact - export type TransitionFactMutationError = void | HTTPValidationError - - /** +/** * @summary 검증 상태 전이 */ -export const useTransitionFact = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;factId: string;data: ReqTransitionFact}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;factId: string;data: ReqTransitionFact}, - TContext - > => { +export const useTransitionFact = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; factId: string; data: ReqTransitionFact }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; factId: string; data: ReqTransitionFact }, + TContext +> => { + const mutationOptions = getTransitionFactMutationOptions(options); - const mutationOptions = getTransitionFactMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * 사장님이 붙여넣은 소개글·안내문에서 업종 스키마 fact 를 뽑는다. ★ 뽑은 값의 **근거 문장이 원문에 실제로 있는지 대조**해 통과한 것만 저장한다. 저장된 값은 전부 후보(UNVERIFIED)라 사장님이 확인해야 사이트에 나간다. 버려진 항목은 사유와 함께 돌려준다. * @summary 붙여넣은 텍스트에서 fact 추출 */ export const extractFactsFromText = ( - placeId: string, - reqExtractFacts: ReqExtractFacts, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqExtractFacts: ReqExtractFacts, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/fact/extract`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqExtractFacts, signal + return customFetch( + { + url: `/v1/place/${placeId}/fact/extract`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqExtractFacts, + signal, }, - options); - } - + options, + ); +}; +export const getExtractFactsFromTextMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqExtractFacts }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqExtractFacts }, + TContext +> => { + const mutationKey = ["extractFactsFromText"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getExtractFactsFromTextMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqExtractFacts}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqExtractFacts}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqExtractFacts } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['extractFactsFromText']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return extractFactsFromText(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type ExtractFactsFromTextMutationResult = NonNullable< + Awaited> +>; +export type ExtractFactsFromTextMutationBody = ReqExtractFacts; +export type ExtractFactsFromTextMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqExtractFacts}> = (props) => { - const {placeId,data} = props ?? {}; - - return extractFactsFromText(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type ExtractFactsFromTextMutationResult = NonNullable>> - export type ExtractFactsFromTextMutationBody = ReqExtractFacts - export type ExtractFactsFromTextMutationError = void | HTTPValidationError - - /** +/** * @summary 붙여넣은 텍스트에서 fact 추출 */ -export const useExtractFactsFromText = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqExtractFacts}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqExtractFacts}, - TContext - > => { +export const useExtractFactsFromText = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqExtractFacts }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqExtractFacts }, + TContext +> => { + const mutationOptions = getExtractFactsFromTextMutationOptions(options); - const mutationOptions = getExtractFactsFromTextMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - \ No newline at end of file + return useMutation(mutationOptions, queryClient); +}; diff --git a/solution/frontend/src/api/generated/faq/faq.ts b/solution/frontend/src/api/generated/faq/faq.ts index 9b6b4a1..32e490d 100644 --- a/solution/frontend/src/api/generated/faq/faq.ts +++ b/solution/frontend/src/api/generated/faq/faq.ts @@ -4,10 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import { - useMutation, - useQuery -} from '@tanstack/react-query'; +import { useMutation, useQuery } from "@tanstack/react-query"; import type { DataTag, DefinedInitialDataOptions, @@ -20,8 +17,8 @@ import type { UseMutationOptions, UseMutationResult, UseQueryOptions, - UseQueryResult -} from '@tanstack/react-query'; + UseQueryResult, +} from "@tanstack/react-query"; import type { HTTPValidationError, @@ -29,248 +26,356 @@ import type { ReqCreateFaq, ReqTransitionFaq, ResFaq, - ResFaqList -} from '.././model'; - -import { customFetch } from '../../mutator/custom-fetch'; + ResFaqList, +} from ".././model"; +import { customFetch } from "../../mutator/custom-fetch"; type SecondParameter unknown> = Parameters[1]; - - /** * publishable_only=true 면 ★ 사이트에 나갈 수 있는 것(VERIFIED·CORRECTED)만. 기본은 승인 대기(UNVERIFIED)까지 함께 준다 — 승인할 대상을 봐야 하기 때문이다. * @summary FAQ 목록 */ export const listFaqs = ( - placeId: string, - params?: ListFaqsParams, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + params?: ListFaqsParams, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/faq/list`, method: 'GET', - params, signal - }, - options); - } - + return customFetch( + { url: `/v1/place/${placeId}/faq/list`, method: "GET", params, signal }, + options, + ); +}; - - -export const getListFaqsQueryKey = (placeId?: string, - params?: ListFaqsParams,) => { - return [ - `/v1/place/${placeId}/faq/list`, ...(params ? [params]: []) - ] as const; - } - - -export const getListFaqsQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, - params?: ListFaqsParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getListFaqsQueryKey = ( + placeId?: string, + params?: ListFaqsParams, ) => { + return [ + `/v1/place/${placeId}/faq/list`, + ...(params ? [params] : []), + ] as const; +}; -const {query: queryOptions, request: requestOptions} = options ?? {}; +export const getListFaqsQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListFaqsParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; - const queryKey = queryOptions?.queryKey ?? getListFaqsQueryKey(placeId,params); + const queryKey = + queryOptions?.queryKey ?? getListFaqsQueryKey(placeId, params); - + const queryFn: QueryFunction>> = ({ + signal, + }) => listFaqs(placeId, params, requestOptions, signal); - const queryFn: QueryFunction>> = ({ signal }) => listFaqs(placeId,params, requestOptions, signal); + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: DataTag; + }; +}; - +export type ListFaqsQueryResult = NonNullable< + Awaited> +>; +export type ListFaqsQueryError = void | HTTPValidationError; - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type ListFaqsQueryResult = NonNullable>> -export type ListFaqsQueryError = void | HTTPValidationError - - -export function useListFaqs>, TError = void | HTTPValidationError>( - placeId: string, - params: undefined | ListFaqsParams, options: { query:Partial>, TError, TData>> & Pick< +export function useListFaqs< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params: undefined | ListFaqsParams, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useListFaqs>, TError = void | HTTPValidationError>( - placeId: string, - params?: ListFaqsParams, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useListFaqs< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListFaqsParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useListFaqs>, TError = void | HTTPValidationError>( - placeId: string, - params?: ListFaqsParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useListFaqs< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListFaqsParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary FAQ 목록 */ -export function useListFaqs>, TError = void | HTTPValidationError>( - placeId: string, - params?: ListFaqsParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useListFaqs< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListFaqsParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getListFaqsQueryOptions(placeId, params, options); - const queryOptions = getListFaqsQueryOptions(placeId,params,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * 사장님이 직접 쓴 FAQ. fact 의 owner 입력과 같이 바로 노출값(VERIFIED)이 된다 — 사람이 곧 출처다. 출처(generated_by)는 서버가 OWNER 로 고정한다. * @summary FAQ 직접 추가 */ export const createFaq = ( - placeId: string, - reqCreateFaq: ReqCreateFaq, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqCreateFaq: ReqCreateFaq, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/faq`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqCreateFaq, signal + return customFetch( + { + url: `/v1/place/${placeId}/faq`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqCreateFaq, + signal, }, - options); - } - + options, + ); +}; +export const getCreateFaqMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqCreateFaq }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqCreateFaq }, + TContext +> => { + const mutationKey = ["createFaq"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getCreateFaqMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqCreateFaq}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqCreateFaq}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqCreateFaq } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['createFaq']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return createFaq(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type CreateFaqMutationResult = NonNullable< + Awaited> +>; +export type CreateFaqMutationBody = ReqCreateFaq; +export type CreateFaqMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqCreateFaq}> = (props) => { - const {placeId,data} = props ?? {}; - - return createFaq(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type CreateFaqMutationResult = NonNullable>> - export type CreateFaqMutationBody = ReqCreateFaq - export type CreateFaqMutationError = void | HTTPValidationError - - /** +/** * @summary FAQ 직접 추가 */ -export const useCreateFaq = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqCreateFaq}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqCreateFaq}, - TContext - > => { +export const useCreateFaq = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqCreateFaq }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqCreateFaq }, + TContext +> => { + const mutationOptions = getCreateFaqMutationOptions(options); - const mutationOptions = getCreateFaqMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * 허용 전이만 통과한다(FACT_STATUS_TRANSITIONS — fact 와 같은 표). CORRECTED 로 갈 때는 고친 question/answer 중 하나 이상이 필요하고, 그 뒤로는 ★ 잠긴 상태라 재생성이 덮어쓰지 못한다. * @summary 검증 상태 전이(승인·정정·반려) */ export const transitionFaq = ( - placeId: string, - faqId: string, - reqTransitionFaq: ReqTransitionFaq, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + faqId: string, + reqTransitionFaq: ReqTransitionFaq, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/faq/${faqId}/transition`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqTransitionFaq, signal + return customFetch( + { + url: `/v1/place/${placeId}/faq/${faqId}/transition`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqTransitionFaq, + signal, }, - options); - } - + options, + ); +}; +export const getTransitionFaqMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; faqId: string; data: ReqTransitionFaq }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; faqId: string; data: ReqTransitionFaq }, + TContext +> => { + const mutationKey = ["transitionFaq"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getTransitionFaqMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;faqId: string;data: ReqTransitionFaq}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;faqId: string;data: ReqTransitionFaq}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; faqId: string; data: ReqTransitionFaq } + > = (props) => { + const { placeId, faqId, data } = props ?? {}; -const mutationKey = ['transitionFaq']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return transitionFaq(placeId, faqId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type TransitionFaqMutationResult = NonNullable< + Awaited> +>; +export type TransitionFaqMutationBody = ReqTransitionFaq; +export type TransitionFaqMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;faqId: string;data: ReqTransitionFaq}> = (props) => { - const {placeId,faqId,data} = props ?? {}; - - return transitionFaq(placeId,faqId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type TransitionFaqMutationResult = NonNullable>> - export type TransitionFaqMutationBody = ReqTransitionFaq - export type TransitionFaqMutationError = void | HTTPValidationError - - /** +/** * @summary 검증 상태 전이(승인·정정·반려) */ -export const useTransitionFaq = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;faqId: string;data: ReqTransitionFaq}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;faqId: string;data: ReqTransitionFaq}, - TContext - > => { +export const useTransitionFaq = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; faqId: string; data: ReqTransitionFaq }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; faqId: string; data: ReqTransitionFaq }, + TContext +> => { + const mutationOptions = getTransitionFaqMutationOptions(options); - const mutationOptions = getTransitionFaqMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - \ No newline at end of file + return useMutation(mutationOptions, queryClient); +}; diff --git a/solution/frontend/src/api/generated/image/image.ts b/solution/frontend/src/api/generated/image/image.ts new file mode 100644 index 0000000..fc48663 --- /dev/null +++ b/solution/frontend/src/api/generated/image/image.ts @@ -0,0 +1,166 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ +import { useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import type { HTTPValidationError, RelayParams } from ".././model"; + +import { customFetch } from "../../mutator/custom-fetch"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * 인증을 요구하지 않는다. 발행본은 로그인 없이 열리는 정적 페이지이고, 여기서 나가는 +것은 **그 페이지가 이미 화면에 싣고 있는 사진**뿐이다(allowlist 가 그걸 보장한다). + * @summary 사진 중계 — 캔버스 오염을 피하려고 같은 오리진으로 흘려보낸다 + */ +export const relay = ( + params: RelayParams, + options?: SecondParameter, + signal?: AbortSignal, +) => { + return customFetch( + { url: `/v1/image/relay`, method: "GET", params, signal }, + options, + ); +}; + +export const getRelayQueryKey = (params?: RelayParams) => { + return [`/v1/image/relay`, ...(params ? [params] : [])] as const; +}; + +export const getRelayQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + params: RelayParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getRelayQueryKey(params); + + const queryFn: QueryFunction>> = ({ + signal, + }) => relay(params, requestOptions, signal); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type RelayQueryResult = NonNullable>>; +export type RelayQueryError = HTTPValidationError; + +export function useRelay< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: RelayParams, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useRelay< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: RelayParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useRelay< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: RelayParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary 사진 중계 — 캔버스 오염을 피하려고 같은 오리진으로 흘려보낸다 + */ + +export function useRelay< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: RelayParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getRelayQueryOptions(params, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} diff --git a/solution/frontend/src/api/generated/job/job.ts b/solution/frontend/src/api/generated/job/job.ts index 50eeca5..1d18b88 100644 --- a/solution/frontend/src/api/generated/job/job.ts +++ b/solution/frontend/src/api/generated/job/job.ts @@ -4,10 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import { - useMutation, - useQuery -} from '@tanstack/react-query'; +import { useMutation, useQuery } from "@tanstack/react-query"; import type { DataTag, DefinedInitialDataOptions, @@ -20,268 +17,374 @@ import type { UseMutationOptions, UseMutationResult, UseQueryOptions, - UseQueryResult -} from '@tanstack/react-query'; + UseQueryResult, +} from "@tanstack/react-query"; -import type { - HTTPValidationError, - ResJob, - ResJobOps -} from '.././model'; - -import { customFetch } from '../../mutator/custom-fetch'; +import type { HTTPValidationError, ResJob, ResJobOps } from ".././model"; +import { customFetch } from "../../mutator/custom-fetch"; type SecondParameter unknown> = Parameters[1]; - - /** * 상태별 카운트 + 큐 지연 + 최근 1시간 DEAD + 좀비(stuck). 내부 운영 전용. * @summary 큐 운영 스냅샷 */ export const jobOps = ( - - options?: SecondParameter,signal?: AbortSignal + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/job/ops`, method: 'GET', signal - }, - options); - } - - - + return customFetch( + { url: `/v1/job/ops`, method: "GET", signal }, + options, + ); +}; export const getJobOpsQueryKey = () => { - return [ - `/v1/job/ops` - ] as const; - } + return [`/v1/job/ops`] as const; +}; - -export const getJobOpsQueryOptions = >, TError = void>( options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} -) => { +export const getJobOpsQueryOptions = < + TData = Awaited>, + TError = void, +>(options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getJobOpsQueryKey(); - const queryKey = queryOptions?.queryKey ?? getJobOpsQueryKey(); + const queryFn: QueryFunction>> = ({ + signal, + }) => jobOps(requestOptions, signal); - + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; - const queryFn: QueryFunction>> = ({ signal }) => jobOps(requestOptions, signal); +export type JobOpsQueryResult = NonNullable>>; +export type JobOpsQueryError = void; - - - - - return { queryKey, queryFn, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type JobOpsQueryResult = NonNullable>> -export type JobOpsQueryError = void - - -export function useJobOps>, TError = void>( - options: { query:Partial>, TError, TData>> & Pick< +export function useJobOps< + TData = Awaited>, + TError = void, +>( + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useJobOps>, TError = void>( - options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useJobOps< + TData = Awaited>, + TError = void, +>( + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useJobOps>, TError = void>( - options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useJobOps< + TData = Awaited>, + TError = void, +>( + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 큐 운영 스냅샷 */ -export function useJobOps>, TError = void>( - options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useJobOps< + TData = Awaited>, + TError = void, +>( + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getJobOpsQueryOptions(options); - const queryOptions = getJobOpsQueryOptions(options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * @summary 잡 상태 조회(폴링) */ export const getJob = ( - jobId: string, - options?: SecondParameter,signal?: AbortSignal + jobId: string, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/job/${jobId}`, method: 'GET', signal - }, - options); - } - + return customFetch( + { url: `/v1/job/${jobId}`, method: "GET", signal }, + options, + ); +}; +export const getGetJobQueryKey = (jobId?: string) => { + return [`/v1/job/${jobId}`] as const; +}; - -export const getGetJobQueryKey = (jobId?: string,) => { - return [ - `/v1/job/${jobId}` - ] as const; - } - - -export const getGetJobQueryOptions = >, TError = void | HTTPValidationError>(jobId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getGetJobQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + jobId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, ) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getGetJobQueryKey(jobId); - const queryKey = queryOptions?.queryKey ?? getGetJobQueryKey(jobId); + const queryFn: QueryFunction>> = ({ + signal, + }) => getJob(jobId, requestOptions, signal); - + return { + queryKey, + queryFn, + enabled: !!jobId, + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: DataTag; + }; +}; - const queryFn: QueryFunction>> = ({ signal }) => getJob(jobId, requestOptions, signal); +export type GetJobQueryResult = NonNullable>>; +export type GetJobQueryError = void | HTTPValidationError; - - - - - return { queryKey, queryFn, enabled: !!(jobId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type GetJobQueryResult = NonNullable>> -export type GetJobQueryError = void | HTTPValidationError - - -export function useGetJob>, TError = void | HTTPValidationError>( - jobId: string, options: { query:Partial>, TError, TData>> & Pick< +export function useGetJob< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + jobId: string, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useGetJob>, TError = void | HTTPValidationError>( - jobId: string, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetJob< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + jobId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useGetJob>, TError = void | HTTPValidationError>( - jobId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetJob< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + jobId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 잡 상태 조회(폴링) */ -export function useGetJob>, TError = void | HTTPValidationError>( - jobId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useGetJob< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + jobId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetJobQueryOptions(jobId, options); - const queryOptions = getGetJobQueryOptions(jobId,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * max_attempts 를 소진해 dead-letter 로 간 잡을 다시 대기로 돌린다. 내부 운영 전용. * @summary DEAD 잡 재큐 */ export const requeueJob = ( - jobId: string, - options?: SecondParameter,signal?: AbortSignal + jobId: string, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/job/${jobId}/requeue`, method: 'POST', signal - }, - options); - } - + return customFetch( + { url: `/v1/job/${jobId}/requeue`, method: "POST", signal }, + options, + ); +}; +export const getRequeueJobMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { jobId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { jobId: string }, + TContext +> => { + const mutationKey = ["requeueJob"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getRequeueJobMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{jobId: string}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{jobId: string}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { jobId: string } + > = (props) => { + const { jobId } = props ?? {}; -const mutationKey = ['requeueJob']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return requeueJob(jobId, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type RequeueJobMutationResult = NonNullable< + Awaited> +>; - const mutationFn: MutationFunction>, {jobId: string}> = (props) => { - const {jobId} = props ?? {}; +export type RequeueJobMutationError = void | HTTPValidationError; - return requeueJob(jobId,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type RequeueJobMutationResult = NonNullable>> - - export type RequeueJobMutationError = void | HTTPValidationError - - /** +/** * @summary DEAD 잡 재큐 */ -export const useRequeueJob = (options?: { mutation?:UseMutationOptions>, TError,{jobId: string}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {jobId: string}, - TContext - > => { +export const useRequeueJob = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { jobId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { jobId: string }, + TContext +> => { + const mutationOptions = getRequeueJobMutationOptions(options); - const mutationOptions = getRequeueJobMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - \ No newline at end of file + return useMutation(mutationOptions, queryClient); +}; diff --git a/solution/frontend/src/api/generated/local-content/local-content.ts b/solution/frontend/src/api/generated/local-content/local-content.ts index 60bfdc2..0ed973c 100644 --- a/solution/frontend/src/api/generated/local-content/local-content.ts +++ b/solution/frontend/src/api/generated/local-content/local-content.ts @@ -4,10 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import { - useMutation, - useQuery -} from '@tanstack/react-query'; +import { useMutation, useQuery } from "@tanstack/react-query"; import type { DataTag, DefinedInitialDataOptions, @@ -20,465 +17,1059 @@ import type { UseMutationOptions, UseMutationResult, UseQueryOptions, - UseQueryResult -} from '@tanstack/react-query'; + UseQueryResult, +} from "@tanstack/react-query"; import type { + GetGuideParams, GetWeatherParams, HTTPValidationError, ListContentsParams, + ReqHidePlaceContent, ReqPublishLocalContent, - ReqSyncFestivals, ReqUpdateLocalContent, ResLocalContentList, - ResSyncFestivals, - ResWeather -} from '.././model'; - -import { customFetch } from '../../mutator/custom-fetch'; + ResLocalGuide, + ResPlaceContentList, + ResSyncPlace, + ResWeather, +} from ".././model"; +import { customFetch } from "../../mutator/custom-fetch"; type SecondParameter unknown> = Parameters[1]; +/** + * @summary 업장 주변정보 목록 (숨김 포함) + */ +export const listPlaceContents = ( + placeId: string, + options?: SecondParameter, + signal?: AbortSignal, +) => { + return customFetch( + { url: `/v1/admin/local-content/place/${placeId}`, method: "GET", signal }, + options, + ); +}; +export const getListPlaceContentsQueryKey = (placeId?: string) => { + return [`/v1/admin/local-content/place/${placeId}`] as const; +}; +export const getListPlaceContentsQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getListPlaceContentsQueryKey(placeId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => listPlaceContents(placeId, requestOptions, signal); + + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type ListPlaceContentsQueryResult = NonNullable< + Awaited> +>; +export type ListPlaceContentsQueryError = HTTPValidationError; + +export function useListPlaceContents< + TData = Awaited>, + TError = HTTPValidationError, +>( + placeId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useListPlaceContents< + TData = Awaited>, + TError = HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useListPlaceContents< + TData = Awaited>, + TError = HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary 업장 주변정보 목록 (숨김 포함) + */ + +export function useListPlaceContents< + TData = Awaited>, + TError = HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getListPlaceContentsQueryOptions(placeId, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * 빌드가 매번 하는 것과 같은 수집을 운영자가 직접 누른다. 무인 갱신(스케줄러)은 아직 없다. + * @summary 업장 주변정보 재수집 (TourAPI 반경) + */ +export const syncPlace = ( + placeId: string, + options?: SecondParameter, + signal?: AbortSignal, +) => { + return customFetch( + { + url: `/v1/admin/local-content/place/${placeId}/sync`, + method: "POST", + signal, + }, + options, + ); +}; + +export const getSyncPlaceMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string }, + TContext +> => { + const mutationKey = ["syncPlace"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { placeId: string } + > = (props) => { + const { placeId } = props ?? {}; + + return syncPlace(placeId, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type SyncPlaceMutationResult = NonNullable< + Awaited> +>; + +export type SyncPlaceMutationError = HTTPValidationError; + +/** + * @summary 업장 주변정보 재수집 (TourAPI 반경) + */ +export const useSyncPlace = ( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string }, + TContext +> => { + const mutationOptions = getSyncPlaceMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * 숨긴 항목은 재수집이 되살리지 않는다. 다음 빌드부터 발행본에서 빠진다. + * @summary 주변정보 숨김/해제 + */ +export const hidePlaceContent = ( + placeContentId: string, + reqHidePlaceContent: ReqHidePlaceContent, + options?: SecondParameter, + signal?: AbortSignal, +) => { + return customFetch( + { + url: `/v1/admin/local-content/place-content/${placeContentId}/hide`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqHidePlaceContent, + signal, + }, + options, + ); +}; + +export const getHidePlaceContentMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeContentId: string; data: ReqHidePlaceContent }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeContentId: string; data: ReqHidePlaceContent }, + TContext +> => { + const mutationKey = ["hidePlaceContent"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { placeContentId: string; data: ReqHidePlaceContent } + > = (props) => { + const { placeContentId, data } = props ?? {}; + + return hidePlaceContent(placeContentId, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type HidePlaceContentMutationResult = NonNullable< + Awaited> +>; +export type HidePlaceContentMutationBody = ReqHidePlaceContent; +export type HidePlaceContentMutationError = HTTPValidationError; + +/** + * @summary 주변정보 숨김/해제 + */ +export const useHidePlaceContent = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeContentId: string; data: ReqHidePlaceContent }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeContentId: string; data: ReqHidePlaceContent }, + TContext +> => { + const mutationOptions = getHidePlaceContentMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; /** * @summary List Contents */ export const listContents = ( - params?: ListContentsParams, - options?: SecondParameter,signal?: AbortSignal + params?: ListContentsParams, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/admin/local-content`, method: 'GET', - params, signal - }, - options); - } - + return customFetch( + { url: `/v1/admin/local-content`, method: "GET", params, signal }, + options, + ); +}; +export const getListContentsQueryKey = (params?: ListContentsParams) => { + return [`/v1/admin/local-content`, ...(params ? [params] : [])] as const; +}; - -export const getListContentsQueryKey = (params?: ListContentsParams,) => { - return [ - `/v1/admin/local-content`, ...(params ? [params]: []) - ] as const; - } - - -export const getListContentsQueryOptions = >, TError = HTTPValidationError>(params?: ListContentsParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getListContentsQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: ListContentsParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, ) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getListContentsQueryKey(params); - const queryKey = queryOptions?.queryKey ?? getListContentsQueryKey(params); + const queryFn: QueryFunction>> = ({ + signal, + }) => listContents(params, requestOptions, signal); - + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; - const queryFn: QueryFunction>> = ({ signal }) => listContents(params, requestOptions, signal); +export type ListContentsQueryResult = NonNullable< + Awaited> +>; +export type ListContentsQueryError = HTTPValidationError; - - - - - return { queryKey, queryFn, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type ListContentsQueryResult = NonNullable>> -export type ListContentsQueryError = HTTPValidationError - - -export function useListContents>, TError = HTTPValidationError>( - params: undefined | ListContentsParams, options: { query:Partial>, TError, TData>> & Pick< +export function useListContents< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: undefined | ListContentsParams, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useListContents>, TError = HTTPValidationError>( - params?: ListContentsParams, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useListContents< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: ListContentsParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useListContents>, TError = HTTPValidationError>( - params?: ListContentsParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useListContents< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: ListContentsParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary List Contents */ -export function useListContents>, TError = HTTPValidationError>( - params?: ListContentsParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useListContents< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: ListContentsParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getListContentsQueryOptions(params, options); - const queryOptions = getListContentsQueryOptions(params,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** - * @summary Sync Festivals - */ -export const syncFestivals = ( - reqSyncFestivals: ReqSyncFestivals, - options?: SecondParameter,signal?: AbortSignal -) => { - - - return customFetch( - {url: `/v1/admin/local-content/sync-festivals`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqSyncFestivals, signal - }, - options); - } - - - -export const getSyncFestivalsMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqSyncFestivals}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{data: ReqSyncFestivals}, TContext> => { - -const mutationKey = ['syncFestivals']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; - - - - - const mutationFn: MutationFunction>, {data: ReqSyncFestivals}> = (props) => { - const {data} = props ?? {}; - - return syncFestivals(data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type SyncFestivalsMutationResult = NonNullable>> - export type SyncFestivalsMutationBody = ReqSyncFestivals - export type SyncFestivalsMutationError = HTTPValidationError - - /** - * @summary Sync Festivals - */ -export const useSyncFestivals = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqSyncFestivals}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {data: ReqSyncFestivals}, - TContext - > => { - - const mutationOptions = getSyncFestivalsMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** * @summary Publish */ export const publish = ( - reqPublishLocalContent: ReqPublishLocalContent, - options?: SecondParameter,signal?: AbortSignal + reqPublishLocalContent: ReqPublishLocalContent, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/admin/local-content/publish`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqPublishLocalContent, signal + return customFetch( + { + url: `/v1/admin/local-content/publish`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqPublishLocalContent, + signal, }, - options); - } - + options, + ); +}; +export const getPublishMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ReqPublishLocalContent }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: ReqPublishLocalContent }, + TContext +> => { + const mutationKey = ["publish"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getPublishMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqPublishLocalContent}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{data: ReqPublishLocalContent}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { data: ReqPublishLocalContent } + > = (props) => { + const { data } = props ?? {}; -const mutationKey = ['publish']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return publish(data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type PublishMutationResult = NonNullable< + Awaited> +>; +export type PublishMutationBody = ReqPublishLocalContent; +export type PublishMutationError = HTTPValidationError; - const mutationFn: MutationFunction>, {data: ReqPublishLocalContent}> = (props) => { - const {data} = props ?? {}; - - return publish(data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type PublishMutationResult = NonNullable>> - export type PublishMutationBody = ReqPublishLocalContent - export type PublishMutationError = HTTPValidationError - - /** +/** * @summary Publish */ -export const usePublish = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqPublishLocalContent}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {data: ReqPublishLocalContent}, - TContext - > => { +export const usePublish = ( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ReqPublishLocalContent }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: ReqPublishLocalContent }, + TContext +> => { + const mutationOptions = getPublishMutationOptions(options); - const mutationOptions = getPublishMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * @summary Update Content */ export const updateContent = ( - contentId: string, - reqUpdateLocalContent: ReqUpdateLocalContent, - options?: SecondParameter,) => { - - - return customFetch( - {url: `/v1/admin/local-content/${contentId}`, method: 'PATCH', - headers: {'Content-Type': 'application/json', }, - data: reqUpdateLocalContent + contentId: string, + reqUpdateLocalContent: ReqUpdateLocalContent, + options?: SecondParameter, +) => { + return customFetch( + { + url: `/v1/admin/local-content/${contentId}`, + method: "PATCH", + headers: { "Content-Type": "application/json" }, + data: reqUpdateLocalContent, }, - options); - } - + options, + ); +}; +export const getUpdateContentMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { contentId: string; data: ReqUpdateLocalContent }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { contentId: string; data: ReqUpdateLocalContent }, + TContext +> => { + const mutationKey = ["updateContent"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getUpdateContentMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{contentId: string;data: ReqUpdateLocalContent}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{contentId: string;data: ReqUpdateLocalContent}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { contentId: string; data: ReqUpdateLocalContent } + > = (props) => { + const { contentId, data } = props ?? {}; -const mutationKey = ['updateContent']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return updateContent(contentId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type UpdateContentMutationResult = NonNullable< + Awaited> +>; +export type UpdateContentMutationBody = ReqUpdateLocalContent; +export type UpdateContentMutationError = HTTPValidationError; - const mutationFn: MutationFunction>, {contentId: string;data: ReqUpdateLocalContent}> = (props) => { - const {contentId,data} = props ?? {}; - - return updateContent(contentId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type UpdateContentMutationResult = NonNullable>> - export type UpdateContentMutationBody = ReqUpdateLocalContent - export type UpdateContentMutationError = HTTPValidationError - - /** +/** * @summary Update Content */ -export const useUpdateContent = (options?: { mutation?:UseMutationOptions>, TError,{contentId: string;data: ReqUpdateLocalContent}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {contentId: string;data: ReqUpdateLocalContent}, - TContext - > => { +export const useUpdateContent = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { contentId: string; data: ReqUpdateLocalContent }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { contentId: string; data: ReqUpdateLocalContent }, + TContext +> => { + const mutationOptions = getUpdateContentMutationOptions(options); - const mutationOptions = getUpdateContentMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * @summary End Content */ export const endContent = ( - contentId: string, - options?: SecondParameter,signal?: AbortSignal + contentId: string, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/admin/local-content/${contentId}/end`, method: 'POST', signal - }, - options); - } - + return customFetch( + { url: `/v1/admin/local-content/${contentId}/end`, method: "POST", signal }, + options, + ); +}; +export const getEndContentMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { contentId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { contentId: string }, + TContext +> => { + const mutationKey = ["endContent"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getEndContentMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{contentId: string}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{contentId: string}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { contentId: string } + > = (props) => { + const { contentId } = props ?? {}; -const mutationKey = ['endContent']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return endContent(contentId, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type EndContentMutationResult = NonNullable< + Awaited> +>; - const mutationFn: MutationFunction>, {contentId: string}> = (props) => { - const {contentId} = props ?? {}; +export type EndContentMutationError = HTTPValidationError; - return endContent(contentId,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type EndContentMutationResult = NonNullable>> - - export type EndContentMutationError = HTTPValidationError - - /** +/** * @summary End Content */ -export const useEndContent = (options?: { mutation?:UseMutationOptions>, TError,{contentId: string}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {contentId: string}, - TContext - > => { +export const useEndContent = ( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { contentId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { contentId: string }, + TContext +> => { + const mutationOptions = getEndContentMutationOptions(options); - const mutationOptions = getEndContentMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * @summary 현재 날씨 조회 (cache-aside) */ export const getWeather = ( - params: GetWeatherParams, - options?: SecondParameter,signal?: AbortSignal + params: GetWeatherParams, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/local/weather`, method: 'GET', - params, signal - }, - options); - } - + return customFetch( + { url: `/v1/local/weather`, method: "GET", params, signal }, + options, + ); +}; +export const getGetWeatherQueryKey = (params?: GetWeatherParams) => { + return [`/v1/local/weather`, ...(params ? [params] : [])] as const; +}; - -export const getGetWeatherQueryKey = (params?: GetWeatherParams,) => { - return [ - `/v1/local/weather`, ...(params ? [params]: []) - ] as const; - } - - -export const getGetWeatherQueryOptions = >, TError = HTTPValidationError>(params: GetWeatherParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getGetWeatherQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetWeatherParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, ) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getGetWeatherQueryKey(params); - const queryKey = queryOptions?.queryKey ?? getGetWeatherQueryKey(params); + const queryFn: QueryFunction>> = ({ + signal, + }) => getWeather(params, requestOptions, signal); - + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; - const queryFn: QueryFunction>> = ({ signal }) => getWeather(params, requestOptions, signal); +export type GetWeatherQueryResult = NonNullable< + Awaited> +>; +export type GetWeatherQueryError = HTTPValidationError; - - - - - return { queryKey, queryFn, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type GetWeatherQueryResult = NonNullable>> -export type GetWeatherQueryError = HTTPValidationError - - -export function useGetWeather>, TError = HTTPValidationError>( - params: GetWeatherParams, options: { query:Partial>, TError, TData>> & Pick< +export function useGetWeather< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetWeatherParams, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useGetWeather>, TError = HTTPValidationError>( - params: GetWeatherParams, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetWeather< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetWeatherParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useGetWeather>, TError = HTTPValidationError>( - params: GetWeatherParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetWeather< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetWeatherParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 현재 날씨 조회 (cache-aside) */ -export function useGetWeather>, TError = HTTPValidationError>( - params: GetWeatherParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useGetWeather< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetWeatherParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetWeatherQueryOptions(params, options); - const queryOptions = getGetWeatherQueryOptions(params,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } +/** + * 날씨와 같은 공개 조회다 — 운영자가 숨기지 않은 공공데이터만 나가므로 인증을 요구하지 않는다. + * @summary 업장 주변 가이드(맛집·명소·축제·코스) — 에디터 캔버스용 + */ +export const getGuide = ( + params: GetGuideParams, + options?: SecondParameter, + signal?: AbortSignal, +) => { + return customFetch( + { url: `/v1/local/guide`, method: "GET", params, signal }, + options, + ); +}; +export const getGetGuideQueryKey = (params?: GetGuideParams) => { + return [`/v1/local/guide`, ...(params ? [params] : [])] as const; +}; +export const getGetGuideQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetGuideParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getGetGuideQueryKey(params); + + const queryFn: QueryFunction>> = ({ + signal, + }) => getGuide(params, requestOptions, signal); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetGuideQueryResult = NonNullable< + Awaited> +>; +export type GetGuideQueryError = HTTPValidationError; + +export function useGetGuide< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetGuideParams, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetGuide< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetGuideParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetGuide< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetGuideParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary 업장 주변 가이드(맛집·명소·축제·코스) — 에디터 캔버스용 + */ + +export function useGetGuide< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetGuideParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetGuideQueryOptions(params, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} diff --git a/solution/frontend/src/api/generated/media/media.ts b/solution/frontend/src/api/generated/media/media.ts index b9aa6d9..87e5077 100644 --- a/solution/frontend/src/api/generated/media/media.ts +++ b/solution/frontend/src/api/generated/media/media.ts @@ -4,9 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import { - useQuery -} from '@tanstack/react-query'; +import { useQuery } from "@tanstack/react-query"; import type { DataTag, DefinedInitialDataOptions, @@ -16,120 +14,174 @@ import type { QueryKey, UndefinedInitialDataOptions, UseQueryOptions, - UseQueryResult -} from '@tanstack/react-query'; + UseQueryResult, +} from "@tanstack/react-query"; import type { HTTPValidationError, ListMediaParams, - ResMediaList -} from '.././model'; - -import { customFetch } from '../../mutator/custom-fetch'; + ResMediaList, +} from ".././model"; +import { customFetch } from "../../mutator/custom-fetch"; type SecondParameter unknown> = Parameters[1]; - - /** * publishable_only=true 면 ★ 사이트에 실제로 나갈 수 있는 것(승인 + alt 있음)만 — 빌드 스냅샷과 같은 기준이다. unit_id 를 주면 그 객실·메뉴 사진만. ★ 응답에는 항상 source_type(owner/crawl)과 origin_url 이 실린다 — 크롤링 이미지 재게시 권리가 미결이라(docs/DECISIONS.md 1-2) 결론에 따라 CRAWL 을 통째로 걸러내야 하고, 화면이 출처를 모르면 그 판단을 못 한다. * @summary 사진 목록 */ export const listMedia = ( - placeId: string, - params?: ListMediaParams, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + params?: ListMediaParams, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/media/list`, method: 'GET', - params, signal - }, - options); - } - + return customFetch( + { url: `/v1/place/${placeId}/media/list`, method: "GET", params, signal }, + options, + ); +}; - - -export const getListMediaQueryKey = (placeId?: string, - params?: ListMediaParams,) => { - return [ - `/v1/place/${placeId}/media/list`, ...(params ? [params]: []) - ] as const; - } - - -export const getListMediaQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, - params?: ListMediaParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getListMediaQueryKey = ( + placeId?: string, + params?: ListMediaParams, ) => { + return [ + `/v1/place/${placeId}/media/list`, + ...(params ? [params] : []), + ] as const; +}; -const {query: queryOptions, request: requestOptions} = options ?? {}; +export const getListMediaQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListMediaParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; - const queryKey = queryOptions?.queryKey ?? getListMediaQueryKey(placeId,params); + const queryKey = + queryOptions?.queryKey ?? getListMediaQueryKey(placeId, params); - + const queryFn: QueryFunction>> = ({ + signal, + }) => listMedia(placeId, params, requestOptions, signal); - const queryFn: QueryFunction>> = ({ signal }) => listMedia(placeId,params, requestOptions, signal); + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: DataTag; + }; +}; - +export type ListMediaQueryResult = NonNullable< + Awaited> +>; +export type ListMediaQueryError = void | HTTPValidationError; - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type ListMediaQueryResult = NonNullable>> -export type ListMediaQueryError = void | HTTPValidationError - - -export function useListMedia>, TError = void | HTTPValidationError>( - placeId: string, - params: undefined | ListMediaParams, options: { query:Partial>, TError, TData>> & Pick< +export function useListMedia< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params: undefined | ListMediaParams, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useListMedia>, TError = void | HTTPValidationError>( - placeId: string, - params?: ListMediaParams, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useListMedia< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListMediaParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useListMedia>, TError = void | HTTPValidationError>( - placeId: string, - params?: ListMediaParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useListMedia< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListMediaParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 사진 목록 */ -export function useListMedia>, TError = void | HTTPValidationError>( - placeId: string, - params?: ListMediaParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useListMedia< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListMediaParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getListMediaQueryOptions(placeId, params, options); - const queryOptions = getListMediaQueryOptions(placeId,params,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - - diff --git a/solution/frontend/src/api/generated/model/auditCheckData.ts b/solution/frontend/src/api/generated/model/auditCheckData.ts index 7e9def8..8deaec1 100644 --- a/solution/frontend/src/api/generated/model/auditCheckData.ts +++ b/solution/frontend/src/api/generated/model/auditCheckData.ts @@ -4,7 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { AuditCheckDataRecommendation } from './auditCheckDataRecommendation'; +import type { AuditCheckDataRecommendation } from "./auditCheckDataRecommendation"; export interface AuditCheckData { id: string; diff --git a/solution/frontend/src/api/generated/model/authProvider.ts b/solution/frontend/src/api/generated/model/authProvider.ts index da7067b..5f73a5b 100644 --- a/solution/frontend/src/api/generated/model/authProvider.ts +++ b/solution/frontend/src/api/generated/model/authProvider.ts @@ -12,8 +12,7 @@ 이으려면 "먼저 가입한 쪽의 소유"를 증명받아야 하는데, 그 증명 없이 이메일만 보고 이으면 남이 먼저 만들어 둔 계정에 내 구글 로그인이 들어간다(계정 선점). 보류 사유는 DECISIONS.md 1절. */ -export type AuthProvider = typeof AuthProvider[keyof typeof AuthProvider]; - +export type AuthProvider = (typeof AuthProvider)[keyof typeof AuthProvider]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const AuthProvider = { diff --git a/solution/frontend/src/api/generated/model/buildStatus.ts b/solution/frontend/src/api/generated/model/buildStatus.ts index f04faeb..1d296cb 100644 --- a/solution/frontend/src/api/generated/model/buildStatus.ts +++ b/solution/frontend/src/api/generated/model/buildStatus.ts @@ -8,8 +8,7 @@ /** * site_versions.build_status 코드값. 정적 빌드는 개별 재빌드 단위로 돈다. */ -export type BuildStatus = typeof BuildStatus[keyof typeof BuildStatus]; - +export type BuildStatus = (typeof BuildStatus)[keyof typeof BuildStatus]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const BuildStatus = { diff --git a/solution/frontend/src/api/generated/model/checkSlugParams.ts b/solution/frontend/src/api/generated/model/checkSlugParams.ts index 119448b..3b1f10c 100644 --- a/solution/frontend/src/api/generated/model/checkSlugParams.ts +++ b/solution/frontend/src/api/generated/model/checkSlugParams.ts @@ -6,8 +6,8 @@ */ export type CheckSlugParams = { -/** - * 쓰고 싶은 주소(예: doflo) - */ -slug: string; + /** + * 쓰고 싶은 주소(예: doflo) + */ + slug: string; }; diff --git a/solution/frontend/src/api/generated/model/errorInfo.ts b/solution/frontend/src/api/generated/model/errorInfo.ts index 5db7ee8..ae2ca0a 100644 --- a/solution/frontend/src/api/generated/model/errorInfo.ts +++ b/solution/frontend/src/api/generated/model/errorInfo.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfoSuccess } from './errorInfoSuccess'; -import type { ErrorInfoCode } from './errorInfoCode'; -import type { ErrorInfoDesc } from './errorInfoDesc'; +import type { ErrorInfoSuccess } from "./errorInfoSuccess"; +import type { ErrorInfoCode } from "./errorInfoCode"; +import type { ErrorInfoDesc } from "./errorInfoDesc"; /** * 모든 응답에 공통으로 실리는 결과 정보. result.success / code / desc 로 내려간다. diff --git a/solution/frontend/src/api/generated/model/externalPlaceSource.ts b/solution/frontend/src/api/generated/model/externalPlaceSource.ts index 756cc7d..bbf6c1a 100644 --- a/solution/frontend/src/api/generated/model/externalPlaceSource.ts +++ b/solution/frontend/src/api/generated/model/externalPlaceSource.ts @@ -11,8 +11,8 @@ 카카오는 안정적인 고유 place id 를 준다 → 그걸로 중복 등록을 막는다. 네이버는 고유 id 가 없다(응답의 link 는 업체 홈페이지다) → 상호명+도로명주소로 막는다. */ -export type ExternalPlaceSource = typeof ExternalPlaceSource[keyof typeof ExternalPlaceSource]; - +export type ExternalPlaceSource = + (typeof ExternalPlaceSource)[keyof typeof ExternalPlaceSource]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const ExternalPlaceSource = { diff --git a/solution/frontend/src/api/generated/model/factData.ts b/solution/frontend/src/api/generated/model/factData.ts index 8400570..d51db8e 100644 --- a/solution/frontend/src/api/generated/model/factData.ts +++ b/solution/frontend/src/api/generated/model/factData.ts @@ -4,17 +4,17 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { FactDataUnitId } from './factDataUnitId'; -import type { FactDataValue } from './factDataValue'; -import type { FactDataSummary } from './factDataSummary'; -import type { FactDataUnit } from './factDataUnit'; -import type { SourceType } from './sourceType'; -import type { FactDataSourceUrl } from './factDataSourceUrl'; -import type { FactDataCollectedAt } from './factDataCollectedAt'; -import type { FactDataVerifiedBy } from './factDataVerifiedBy'; -import type { FactDataVerifiedAt } from './factDataVerifiedAt'; -import type { FactStatus } from './factStatus'; -import type { FactDataExpiresAt } from './factDataExpiresAt'; +import type { FactDataUnitId } from "./factDataUnitId"; +import type { FactDataValue } from "./factDataValue"; +import type { FactDataSummary } from "./factDataSummary"; +import type { FactDataUnit } from "./factDataUnit"; +import type { SourceType } from "./sourceType"; +import type { FactDataSourceUrl } from "./factDataSourceUrl"; +import type { FactDataCollectedAt } from "./factDataCollectedAt"; +import type { FactDataVerifiedBy } from "./factDataVerifiedBy"; +import type { FactDataVerifiedAt } from "./factDataVerifiedAt"; +import type { FactStatus } from "./factStatus"; +import type { FactDataExpiresAt } from "./factDataExpiresAt"; export interface FactData { fact_id: string; diff --git a/solution/frontend/src/api/generated/model/factStatus.ts b/solution/frontend/src/api/generated/model/factStatus.ts index 0c78010..9e9d937 100644 --- a/solution/frontend/src/api/generated/model/factStatus.ts +++ b/solution/frontend/src/api/generated/model/factStatus.ts @@ -9,8 +9,7 @@ * facts.status / faqs.status / routes.status 공용 검증 상태. ★ VERIFIED 와 CORRECTED 만 사이트에 노출한다(PUBLISHABLE_FACT_STATUSES). */ -export type FactStatus = typeof FactStatus[keyof typeof FactStatus]; - +export type FactStatus = (typeof FactStatus)[keyof typeof FactStatus]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const FactStatus = { diff --git a/solution/frontend/src/api/generated/model/factWriteOutcome.ts b/solution/frontend/src/api/generated/model/factWriteOutcome.ts index d1791cb..2a19d1a 100644 --- a/solution/frontend/src/api/generated/model/factWriteOutcome.ts +++ b/solution/frontend/src/api/generated/model/factWriteOutcome.ts @@ -9,8 +9,8 @@ * fact 기록 결과. 재수집(업데이트)이 무엇을 했는지 호출측이 알아야 한다 — 특히 사이트 재빌드가 필요한 경우(PUBLISHED_REPLACED)를 구분해야 한다. */ -export type FactWriteOutcome = typeof FactWriteOutcome[keyof typeof FactWriteOutcome]; - +export type FactWriteOutcome = + (typeof FactWriteOutcome)[keyof typeof FactWriteOutcome]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const FactWriteOutcome = { diff --git a/solution/frontend/src/api/generated/model/faqData.ts b/solution/frontend/src/api/generated/model/faqData.ts index f196959..5cf723a 100644 --- a/solution/frontend/src/api/generated/model/faqData.ts +++ b/solution/frontend/src/api/generated/model/faqData.ts @@ -4,11 +4,11 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { FaqDataSourceFactIds } from './faqDataSourceFactIds'; -import type { SourceType } from './sourceType'; -import type { FactStatus } from './factStatus'; -import type { FaqDataCreatedAt } from './faqDataCreatedAt'; -import type { FaqDataUpdatedAt } from './faqDataUpdatedAt'; +import type { FaqDataSourceFactIds } from "./faqDataSourceFactIds"; +import type { SourceType } from "./sourceType"; +import type { FactStatus } from "./factStatus"; +import type { FaqDataCreatedAt } from "./faqDataCreatedAt"; +import type { FaqDataUpdatedAt } from "./faqDataUpdatedAt"; export interface FaqData { faq_id: string; diff --git a/solution/frontend/src/api/generated/model/fieldSpecData.ts b/solution/frontend/src/api/generated/model/fieldSpecData.ts index 9b6109c..a3f8295 100644 --- a/solution/frontend/src/api/generated/model/fieldSpecData.ts +++ b/solution/frontend/src/api/generated/model/fieldSpecData.ts @@ -4,7 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { FieldSpecDataUnit } from './fieldSpecDataUnit'; +import type { FieldSpecDataUnit } from "./fieldSpecDataUnit"; /** * 업종 스키마의 필드 정의 — 관리 화면이 입력 폼을 그리는 데 쓴다. diff --git a/solution/frontend/src/api/generated/model/reqSyncFestivalsAreaCode.ts b/solution/frontend/src/api/generated/model/getGuideParams.ts similarity index 67% rename from solution/frontend/src/api/generated/model/reqSyncFestivalsAreaCode.ts rename to solution/frontend/src/api/generated/model/getGuideParams.ts index aa1d905..cd9e1a6 100644 --- a/solution/frontend/src/api/generated/model/reqSyncFestivalsAreaCode.ts +++ b/solution/frontend/src/api/generated/model/getGuideParams.ts @@ -5,4 +5,6 @@ * OpenAPI spec version: 0.1.0 */ -export type ReqSyncFestivalsAreaCode = string | null; +export type GetGuideParams = { + place_id: string; +}; diff --git a/solution/frontend/src/api/generated/model/getWeatherParams.ts b/solution/frontend/src/api/generated/model/getWeatherParams.ts index 022c056..cdd239d 100644 --- a/solution/frontend/src/api/generated/model/getWeatherParams.ts +++ b/solution/frontend/src/api/generated/model/getWeatherParams.ts @@ -6,19 +6,19 @@ */ export type GetWeatherParams = { -/** - * @minLength 1 - * @maxLength 10 - */ -region_code: string; -/** - * @minimum -90 - * @maximum 90 - */ -latitude: number; -/** - * @minimum -180 - * @maximum 180 - */ -longitude: number; + /** + * @minLength 1 + * @maxLength 10 + */ + region_code: string; + /** + * @minimum -90 + * @maximum 90 + */ + latitude: number; + /** + * @minimum -180 + * @maximum 180 + */ + longitude: number; }; diff --git a/solution/frontend/src/api/generated/model/hTTPValidationError.ts b/solution/frontend/src/api/generated/model/hTTPValidationError.ts index 37970db..b979e83 100644 --- a/solution/frontend/src/api/generated/model/hTTPValidationError.ts +++ b/solution/frontend/src/api/generated/model/hTTPValidationError.ts @@ -4,7 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ValidationError } from './validationError'; +import type { ValidationError } from "./validationError"; export interface HTTPValidationError { detail?: ValidationError[]; diff --git a/solution/frontend/src/api/generated/model/index.ts b/solution/frontend/src/api/generated/model/index.ts index 2be8f8a..c0dc0d0 100644 --- a/solution/frontend/src/api/generated/model/index.ts +++ b/solution/frontend/src/api/generated/model/index.ts @@ -5,318 +5,333 @@ * OpenAPI spec version: 0.1.0 */ -export * from './auditCheckData'; -export * from './auditCheckDataRecommendation'; -export * from './authProvider'; -export * from './buildStatus'; -export * from './checkSlugParams'; -export * from './errorInfo'; -export * from './errorInfoCode'; -export * from './errorInfoDesc'; -export * from './errorInfoSuccess'; -export * from './externalPlaceSource'; -export * from './factData'; -export * from './factDataCollectedAt'; -export * from './factDataExpiresAt'; -export * from './factDataSourceUrl'; -export * from './factDataSummary'; -export * from './factDataUnit'; -export * from './factDataUnitId'; -export * from './factDataValue'; -export * from './factDataVerifiedAt'; -export * from './factDataVerifiedBy'; -export * from './factStatus'; -export * from './factWriteOutcome'; -export * from './faqData'; -export * from './faqDataCreatedAt'; -export * from './faqDataSourceFactIds'; -export * from './faqDataUpdatedAt'; -export * from './fieldSpecData'; -export * from './fieldSpecDataUnit'; -export * from './getWeatherParams'; -export * from './hTTPValidationError'; -export * from './jobData'; -export * from './jobDataCreatedAt'; -export * from './jobDataLastError'; -export * from './jobDataResult'; -export * from './jobDataRunAfter'; -export * from './jobDataRunStartedAt'; -export * from './jobDataUpdatedAt'; -export * from './jobStatus'; -export * from './jobType'; -export * from './linkChannel'; -export * from './linkData'; -export * from './linkDataConfirmedAt'; -export * from './linkDataDiscoveredAt'; -export * from './linkDataTitle'; -export * from './listContentsParams'; -export * from './listFactsParams'; -export * from './listFaqsParams'; -export * from './listLinksParams'; -export * from './listMediaParams'; -export * from './listMySitesParams'; -export * from './listPlacesParams'; -export * from './listShowcaseParams'; -export * from './localContentData'; -export * from './localContentDataBody'; -export * from './localContentDataCollectedAt'; -export * from './localContentDataDisplayEndAt'; -export * from './localContentDataDisplayStartAt'; -export * from './localContentDataExternalId'; -export * from './localContentDataPublishedAt'; -export * from './localContentDataTitle'; -export * from './localContentStatus'; -export * from './localContentType'; -export * from './localSource'; -export * from './mediaData'; -export * from './mediaDataAltText'; -export * from './mediaDataCreatedAt'; -export * from './mediaDataHeight'; -export * from './mediaDataLabel'; -export * from './mediaDataOriginUrl'; -export * from './mediaDataSourceUrl'; -export * from './mediaDataUnitId'; -export * from './mediaDataVisionConfidence'; -export * from './mediaDataWidth'; -export * from './mediaStatus'; -export * from './mySiteData'; -export * from './mySiteDataCreatedAt'; -export * from './mySiteDataDomain'; -export * from './mySiteDataPublishedAt'; -export * from './mySiteDataThumbnailUrl'; -export * from './mySiteDataRoadAddress'; -export * from './mySiteDataSiteId'; -export * from './mySiteDataStatus'; -export * from './mySiteDataTemplateId'; -export * from './placeCandidate'; -export * from './placeCandidateAddress'; -export * from './placeCandidateCategoryName'; -export * from './placeCandidateExternalPlaceId'; -export * from './placeCandidateLatitude'; -export * from './placeCandidateLongitude'; -export * from './placeCandidateNaverPlaceUrl'; -export * from './placeCandidatePhone'; -export * from './placeCandidatePlaceUrl'; -export * from './placeCandidateRoadAddress'; -export * from './placeCategory'; -export * from './placeData'; -export * from './placeDataAddress'; -export * from './placeDataContentUpdatedAt'; -export * from './placeDataCreatedAt'; -export * from './placeDataExternalPlaceId'; -export * from './placeDataExternalSource'; -export * from './placeDataLatitude'; -export * from './placeDataLongitude'; -export * from './placeDataOwnerUserId'; -export * from './placeDataPhone'; -export * from './placeDataRegionCode'; -export * from './placeDataRoadAddress'; -export * from './placeDataVerifiedAt'; -export * from './placeSearchItem'; -export * from './placeSearchItemCategory'; -export * from './placeSearchItemCategoryName'; -export * from './placeSearchItemRoadAddress'; -export * from './placeStatus'; -export * from './publishAction'; -export * from './publishLogData'; -export * from './publishLogDataCreatedAt'; -export * from './publishLogDataDetail'; -export * from './publishLogDataRejectReason'; -export * from './publishRejectReason'; -export * from './publishResult'; -export * from './renderStatusData'; -export * from './renderStatusDataError'; -export * from './renderStatusDataRenderedAt'; -export * from './renderStatusDataRenderedVersion'; -export * from './reqCreateFaq'; -export * from './reqCreateFaqSortOrder'; -export * from './reqCreateLink'; -export * from './reqCreateLinkTitle'; -export * from './reqCreatePlace'; -export * from './reqCreatePlaceOwnerUserId'; -export * from './reqCreateUnit'; -export * from './reqExtractFacts'; -export * from './reqGoogleLogin'; -export * from './reqLogin'; -export * from './reqPublishLocalContent'; -export * from './reqSignup'; -export * from './reqSignupName'; -export * from './reqSiteSlug'; -export * from './reqSiteStatus'; -export * from './reqSiteTemplate'; -export * from './reqSiteTheme'; -export * from './reqSiteThemeTheme'; -export * from './reqStartBuild'; -export * from './reqStartCollect'; -export * from './reqStartCopy'; -export * from './reqStartVision'; -export * from './reqSyncFestivals'; -export * from './reqSyncFestivalsAreaCode'; -export * from './reqSyncFestivalsStartDate'; -export * from './reqTransitionFact'; -export * from './reqTransitionFactValue'; -export * from './reqTransitionFaq'; -export * from './reqTransitionFaqAnswer'; -export * from './reqTransitionFaqQuestion'; -export * from './reqUpdateLocalContent'; -export * from './reqUpdateLocalContentBody'; -export * from './reqUpdateLocalContentBodyAnyOf'; -export * from './reqUpdateLocalContentDisplayEndAt'; -export * from './reqUpdateLocalContentDisplayStartAt'; -export * from './reqUpdateLocalContentTitle'; -export * from './reqUpdateMe'; -export * from './reqUpdateMeContactNumber'; -export * from './reqUpdateMeEmail'; -export * from './reqUpdateMeName'; -export * from './reqUpdateMePassword'; -export * from './reqUpdatePlace'; -export * from './reqUpdatePlaceName'; -export * from './reqUpdatePlaceOwnerUserId'; -export * from './reqUpdatePlaceStatus'; -export * from './reqUpsertFact'; -export * from './reqUpsertFactExpiresAt'; -export * from './reqUpsertFactSourceUrl'; -export * from './reqUpsertFactUnitId'; -export * from './reqUpsertFactValue'; -export * from './reqVerifyPlace'; -export * from './reqVerifyPlaceAddress'; -export * from './reqVerifyPlaceByUrl'; -export * from './reqVerifyPlaceLatitude'; -export * from './reqVerifyPlaceLongitude'; -export * from './reqVerifyPlacePhone'; -export * from './reqVerifyPlacePlaceUrl'; -export * from './reqVerifyPlaceRegionCode'; -export * from './reqVerifyPlaceRoadAddress'; -export * from './resCategorySchema'; -export * from './resCategorySchemaCategory'; -export * from './resCategorySchemaLabel'; -export * from './resCategorySchemaMsg'; -export * from './resExtractFacts'; -export * from './resExtractFactsMsg'; -export * from './resExtractedFact'; -export * from './resExtractedFactReason'; -export * from './resExtractedFactUnitName'; -export * from './resFact'; -export * from './resFactFact'; -export * from './resFactList'; -export * from './resFactListMsg'; -export * from './resFactMsg'; -export * from './resFactOutcome'; -export * from './resFaq'; -export * from './resFaqFaq'; -export * from './resFaqList'; -export * from './resFaqListMsg'; -export * from './resFaqMsg'; -export * from './resJob'; -export * from './resJobJob'; -export * from './resJobMsg'; -export * from './resJobOps'; -export * from './resJobOpsMsg'; -export * from './resLink'; -export * from './resLinkLink'; -export * from './resLinkList'; -export * from './resLinkListMsg'; -export * from './resLinkMsg'; -export * from './resLocalContentList'; -export * from './resLocalContentListMsg'; -export * from './resLogin'; -export * from './resLoginMsg'; -export * from './resMe'; -export * from './resMeContactNumber'; -export * from './resMeEmail'; -export * from './resMeMsg'; -export * from './resMeName'; -export * from './resMediaList'; -export * from './resMediaListMsg'; -export * from './resMySites'; -export * from './resMySitesMsg'; -export * from './resPlace'; -export * from './resPlaceList'; -export * from './resPlaceListMsg'; -export * from './resPlaceMsg'; -export * from './resPlacePlace'; -export * from './resPlaceSearch'; -export * from './resPlaceSearchMsg'; -export * from './resPlaceSearchSource'; -export * from './resPublishLogs'; -export * from './resPublishLogsMsg'; -export * from './resRefreshToken'; -export * from './resRefreshTokenMsg'; -export * from './resSeoAudit'; -export * from './resSeoAuditMsg'; -export * from './resSeoAuditSummary'; -export * from './resSeoAuditVisibility'; -export * from './resShowcase'; -export * from './resShowcaseMsg'; -export * from './resSite'; -export * from './resSiteCurrentVersion'; -export * from './resSiteMsg'; -export * from './resSiteSite'; -export * from './resSiteSlug'; -export * from './resSiteSlugMsg'; -export * from './resSiteSlugReason'; -export * from './resSiteSlugSite'; -export * from './resSiteSlugSuggestion'; -export * from './resSiteVersions'; -export * from './resSiteVersionsMsg'; -export * from './resSlugCheck'; -export * from './resSlugCheckMsg'; -export * from './resSlugCheckReason'; -export * from './resSlugCheckSuggestion'; -export * from './resStartBuild'; -export * from './resStartBuildJobId'; -export * from './resStartBuildMsg'; -export * from './resStartBuildStatus'; -export * from './resStartCollect'; -export * from './resStartCollectJobId'; -export * from './resStartCollectMsg'; -export * from './resStartCollectStatus'; -export * from './resStartCopy'; -export * from './resStartCopyJobId'; -export * from './resStartCopyMsg'; -export * from './resStartCopyStatus'; -export * from './resStartVision'; -export * from './resStartVisionJobId'; -export * from './resStartVisionMsg'; -export * from './resStartVisionStatus'; -export * from './resSyncFestivals'; -export * from './resSyncFestivalsMsg'; -export * from './resUnit'; -export * from './resUnitList'; -export * from './resUnitListMsg'; -export * from './resUnitMsg'; -export * from './resUnitUnit'; -export * from './resVerifyCandidates'; -export * from './resVerifyCandidatesMsg'; -export * from './resVerifyCandidatesReason'; -export * from './resVerifyCandidatesSource'; -export * from './resWeather'; -export * from './resWeatherMsg'; -export * from './resWeatherWeather'; -export * from './searchPlacesPublicParams'; -export * from './showcaseItem'; -export * from './showcaseItemRegion'; -export * from './showcaseItemThumbnailUrl'; -export * from './siteData'; -export * from './siteDataCurrentVersionId'; -export * from './siteDataDomain'; -export * from './siteDataPublishedAt'; -export * from './siteDataTemplateId'; -export * from './siteDataTheme'; -export * from './siteDataThemeAnyOf'; -export * from './siteDataThumbnailUrl'; -export * from './siteStatus'; -export * from './siteVersionData'; -export * from './siteVersionDataBuildError'; -export * from './siteVersionDataBuiltAt'; -export * from './siteVersionDataCreatedAt'; -export * from './sourceType'; -export * from './unitData'; -export * from './userRole'; -export * from './validationError'; -export * from './validationErrorCtx'; -export * from './validationErrorLocItem'; -export * from './verifyCandidatesParams'; -export * from './weatherData'; -export * from './weatherDataTimezone'; -export * from './weatherDataWindSpeed'; \ No newline at end of file +export * from "./auditCheckData"; +export * from "./auditCheckDataRecommendation"; +export * from "./authProvider"; +export * from "./buildStatus"; +export * from "./checkSlugParams"; +export * from "./errorInfo"; +export * from "./errorInfoCode"; +export * from "./errorInfoDesc"; +export * from "./errorInfoSuccess"; +export * from "./externalPlaceSource"; +export * from "./factData"; +export * from "./factDataCollectedAt"; +export * from "./factDataExpiresAt"; +export * from "./factDataSourceUrl"; +export * from "./factDataSummary"; +export * from "./factDataUnit"; +export * from "./factDataUnitId"; +export * from "./factDataValue"; +export * from "./factDataVerifiedAt"; +export * from "./factDataVerifiedBy"; +export * from "./factStatus"; +export * from "./factWriteOutcome"; +export * from "./faqData"; +export * from "./faqDataCreatedAt"; +export * from "./faqDataSourceFactIds"; +export * from "./faqDataUpdatedAt"; +export * from "./fieldSpecData"; +export * from "./fieldSpecDataUnit"; +export * from "./getGuideParams"; +export * from "./getWeatherParams"; +export * from "./hTTPValidationError"; +export * from "./jobData"; +export * from "./jobDataCreatedAt"; +export * from "./jobDataLastError"; +export * from "./jobDataResult"; +export * from "./jobDataRunAfter"; +export * from "./jobDataRunStartedAt"; +export * from "./jobDataUpdatedAt"; +export * from "./jobStatus"; +export * from "./jobType"; +export * from "./linkChannel"; +export * from "./linkData"; +export * from "./linkDataConfirmedAt"; +export * from "./linkDataDiscoveredAt"; +export * from "./linkDataTitle"; +export * from "./listContentsParams"; +export * from "./listFactsParams"; +export * from "./listFaqsParams"; +export * from "./listLinksParams"; +export * from "./listMediaParams"; +export * from "./listMySitesParams"; +export * from "./listPlacesParams"; +export * from "./listShowcaseParams"; +export * from "./localContentData"; +export * from "./localContentDataBody"; +export * from "./localContentDataCollectedAt"; +export * from "./localContentDataDisplayEndAt"; +export * from "./localContentDataDisplayStartAt"; +export * from "./localContentDataExternalId"; +export * from "./localContentDataPublishedAt"; +export * from "./localContentDataTitle"; +export * from "./localContentStatus"; +export * from "./localContentType"; +export * from "./localSource"; +export * from "./mediaData"; +export * from "./mediaDataAltText"; +export * from "./mediaDataCreatedAt"; +export * from "./mediaDataHeight"; +export * from "./mediaDataLabel"; +export * from "./mediaDataOriginUrl"; +export * from "./mediaDataSourceUrl"; +export * from "./mediaDataUnitId"; +export * from "./mediaDataVisionConfidence"; +export * from "./mediaDataWidth"; +export * from "./mediaStatus"; +export * from "./mySiteData"; +export * from "./mySiteDataCreatedAt"; +export * from "./mySiteDataDomain"; +export * from "./mySiteDataPublishedAt"; +export * from "./mySiteDataRoadAddress"; +export * from "./mySiteDataSiteId"; +export * from "./mySiteDataStatus"; +export * from "./mySiteDataTemplateId"; +export * from "./mySiteDataThumbnailUrl"; +export * from "./placeCandidate"; +export * from "./placeCandidateAddress"; +export * from "./placeCandidateCategoryName"; +export * from "./placeCandidateExternalPlaceId"; +export * from "./placeCandidateLatitude"; +export * from "./placeCandidateLongitude"; +export * from "./placeCandidateNaverPlaceUrl"; +export * from "./placeCandidatePhone"; +export * from "./placeCandidatePlaceUrl"; +export * from "./placeCandidateRoadAddress"; +export * from "./placeCategory"; +export * from "./placeContentData"; +export * from "./placeContentDataBody"; +export * from "./placeContentDataCollectedAt"; +export * from "./placeContentDataDisplayEndAt"; +export * from "./placeData"; +export * from "./placeDataAddress"; +export * from "./placeDataContentUpdatedAt"; +export * from "./placeDataCreatedAt"; +export * from "./placeDataExternalPlaceId"; +export * from "./placeDataExternalSource"; +export * from "./placeDataLatitude"; +export * from "./placeDataLongitude"; +export * from "./placeDataOwnerUserId"; +export * from "./placeDataPhone"; +export * from "./placeDataRegionCode"; +export * from "./placeDataRoadAddress"; +export * from "./placeDataVerifiedAt"; +export * from "./placeSearchItem"; +export * from "./placeSearchItemCategory"; +export * from "./placeSearchItemCategoryName"; +export * from "./placeSearchItemNaverPlaceUrl"; +export * from "./placeSearchItemRoadAddress"; +export * from "./placeStatus"; +export * from "./publishAction"; +export * from "./publishLogData"; +export * from "./publishLogDataCreatedAt"; +export * from "./publishLogDataDetail"; +export * from "./publishLogDataRejectReason"; +export * from "./publishRejectReason"; +export * from "./publishResult"; +export * from "./relayParams"; +export * from "./renderStatusData"; +export * from "./renderStatusDataError"; +export * from "./renderStatusDataRenderedAt"; +export * from "./renderStatusDataRenderedVersion"; +export * from "./reqCreateFaq"; +export * from "./reqCreateFaqSortOrder"; +export * from "./reqCreateLink"; +export * from "./reqCreateLinkTitle"; +export * from "./reqCreatePlace"; +export * from "./reqCreateUnit"; +export * from "./reqExtractFacts"; +export * from "./reqGoogleLogin"; +export * from "./reqHidePlaceContent"; +export * from "./reqLogin"; +export * from "./reqPublishLocalContent"; +export * from "./reqRollback"; +export * from "./reqSignup"; +export * from "./reqSignupName"; +export * from "./reqSiteSlug"; +export * from "./reqSiteStatus"; +export * from "./reqSiteTemplate"; +export * from "./reqSiteTheme"; +export * from "./reqSiteThemeTheme"; +export * from "./reqStartBuild"; +export * from "./reqStartCollect"; +export * from "./reqStartCopy"; +export * from "./reqStartVision"; +export * from "./reqTransitionFact"; +export * from "./reqTransitionFactValue"; +export * from "./reqTransitionFaq"; +export * from "./reqTransitionFaqAnswer"; +export * from "./reqTransitionFaqQuestion"; +export * from "./reqUpdateLocalContent"; +export * from "./reqUpdateLocalContentBody"; +export * from "./reqUpdateLocalContentBodyAnyOf"; +export * from "./reqUpdateLocalContentDisplayEndAt"; +export * from "./reqUpdateLocalContentDisplayStartAt"; +export * from "./reqUpdateLocalContentTitle"; +export * from "./reqUpdateMe"; +export * from "./reqUpdateMeContactNumber"; +export * from "./reqUpdateMeEmail"; +export * from "./reqUpdateMeName"; +export * from "./reqUpdateMePassword"; +export * from "./reqUpdatePlace"; +export * from "./reqUpdatePlaceName"; +export * from "./reqUpdatePlaceStatus"; +export * from "./reqUpsertFact"; +export * from "./reqUpsertFactExpiresAt"; +export * from "./reqUpsertFactSourceUrl"; +export * from "./reqUpsertFactUnitId"; +export * from "./reqUpsertFactValue"; +export * from "./reqVerifyPlace"; +export * from "./reqVerifyPlaceAddress"; +export * from "./reqVerifyPlaceByUrl"; +export * from "./reqVerifyPlaceCategoryName"; +export * from "./reqVerifyPlaceLatitude"; +export * from "./reqVerifyPlaceLongitude"; +export * from "./reqVerifyPlacePhone"; +export * from "./reqVerifyPlacePlaceUrl"; +export * from "./reqVerifyPlaceRegionCode"; +export * from "./reqVerifyPlaceRoadAddress"; +export * from "./resCategorySchema"; +export * from "./resCategorySchemaCategory"; +export * from "./resCategorySchemaLabel"; +export * from "./resCategorySchemaMsg"; +export * from "./resExtractFacts"; +export * from "./resExtractFactsMsg"; +export * from "./resExtractedFact"; +export * from "./resExtractedFactReason"; +export * from "./resExtractedFactUnitName"; +export * from "./resFact"; +export * from "./resFactFact"; +export * from "./resFactList"; +export * from "./resFactListMsg"; +export * from "./resFactMsg"; +export * from "./resFactOutcome"; +export * from "./resFaq"; +export * from "./resFaqFaq"; +export * from "./resFaqList"; +export * from "./resFaqListMsg"; +export * from "./resFaqMsg"; +export * from "./resJob"; +export * from "./resJobJob"; +export * from "./resJobMsg"; +export * from "./resJobOps"; +export * from "./resJobOpsMsg"; +export * from "./resLink"; +export * from "./resLinkLink"; +export * from "./resLinkList"; +export * from "./resLinkListMsg"; +export * from "./resLinkMsg"; +export * from "./resLocalContentList"; +export * from "./resLocalContentListMsg"; +export * from "./resLocalGuide"; +export * from "./resLocalGuideAttractionsItem"; +export * from "./resLocalGuideCoursesItem"; +export * from "./resLocalGuideFestivalsItem"; +export * from "./resLocalGuideItinerariesItem"; +export * from "./resLocalGuideMsg"; +export * from "./resLocalGuideRestaurantsItem"; +export * from "./resLocalGuideSyncedAt"; +export * from "./resLogin"; +export * from "./resLoginMsg"; +export * from "./resMe"; +export * from "./resMeContactNumber"; +export * from "./resMeEmail"; +export * from "./resMeMsg"; +export * from "./resMeName"; +export * from "./resMediaList"; +export * from "./resMediaListMsg"; +export * from "./resMySites"; +export * from "./resMySitesMsg"; +export * from "./resPlace"; +export * from "./resPlaceContentList"; +export * from "./resPlaceContentListMsg"; +export * from "./resPlaceList"; +export * from "./resPlaceListMsg"; +export * from "./resPlaceMsg"; +export * from "./resPlacePlace"; +export * from "./resPlaceSearch"; +export * from "./resPlaceSearchMsg"; +export * from "./resPlaceSearchSource"; +export * from "./resPublishLogs"; +export * from "./resPublishLogsMsg"; +export * from "./resRefreshToken"; +export * from "./resRefreshTokenMsg"; +export * from "./resSeoAudit"; +export * from "./resSeoAuditMsg"; +export * from "./resSeoAuditSummary"; +export * from "./resSeoAuditVisibility"; +export * from "./resShowcase"; +export * from "./resShowcaseMsg"; +export * from "./resSite"; +export * from "./resSiteCurrentVersion"; +export * from "./resSiteMsg"; +export * from "./resSiteSite"; +export * from "./resSiteSlug"; +export * from "./resSiteSlugMsg"; +export * from "./resSiteSlugReason"; +export * from "./resSiteSlugSite"; +export * from "./resSiteSlugSuggestion"; +export * from "./resSiteVersions"; +export * from "./resSiteVersionsMsg"; +export * from "./resSlugCheck"; +export * from "./resSlugCheckMsg"; +export * from "./resSlugCheckReason"; +export * from "./resSlugCheckSuggestion"; +export * from "./resStartBuild"; +export * from "./resStartBuildJobId"; +export * from "./resStartBuildMsg"; +export * from "./resStartBuildStatus"; +export * from "./resStartCollect"; +export * from "./resStartCollectJobId"; +export * from "./resStartCollectMsg"; +export * from "./resStartCollectStatus"; +export * from "./resStartCopy"; +export * from "./resStartCopyJobId"; +export * from "./resStartCopyMsg"; +export * from "./resStartCopyStatus"; +export * from "./resStartVision"; +export * from "./resStartVisionJobId"; +export * from "./resStartVisionMsg"; +export * from "./resStartVisionStatus"; +export * from "./resSyncPlace"; +export * from "./resSyncPlaceMsg"; +export * from "./resUnit"; +export * from "./resUnitList"; +export * from "./resUnitListMsg"; +export * from "./resUnitMsg"; +export * from "./resUnitUnit"; +export * from "./resVerifyCandidates"; +export * from "./resVerifyCandidatesMsg"; +export * from "./resVerifyCandidatesReason"; +export * from "./resVerifyCandidatesSource"; +export * from "./resWeather"; +export * from "./resWeatherMsg"; +export * from "./resWeatherWeather"; +export * from "./searchPlacesPublicParams"; +export * from "./showcaseItem"; +export * from "./showcaseItemRegion"; +export * from "./showcaseItemThumbnailUrl"; +export * from "./siteData"; +export * from "./siteDataCurrentVersionId"; +export * from "./siteDataDomain"; +export * from "./siteDataPublishedAt"; +export * from "./siteDataTemplateId"; +export * from "./siteDataTheme"; +export * from "./siteDataThemeAnyOf"; +export * from "./siteDataThumbnailUrl"; +export * from "./siteStatus"; +export * from "./siteVersionData"; +export * from "./siteVersionDataBuildError"; +export * from "./siteVersionDataBuiltAt"; +export * from "./siteVersionDataCreatedAt"; +export * from "./sourceType"; +export * from "./unitData"; +export * from "./userRole"; +export * from "./validationError"; +export * from "./validationErrorCtx"; +export * from "./validationErrorLocItem"; +export * from "./verifyCandidatesParams"; +export * from "./weatherData"; +export * from "./weatherDataTimezone"; +export * from "./weatherDataWindSpeed"; diff --git a/solution/frontend/src/api/generated/model/jobData.ts b/solution/frontend/src/api/generated/model/jobData.ts index f7914a7..8731db0 100644 --- a/solution/frontend/src/api/generated/model/jobData.ts +++ b/solution/frontend/src/api/generated/model/jobData.ts @@ -4,14 +4,14 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { JobType } from './jobType'; -import type { JobStatus } from './jobStatus'; -import type { JobDataResult } from './jobDataResult'; -import type { JobDataLastError } from './jobDataLastError'; -import type { JobDataRunAfter } from './jobDataRunAfter'; -import type { JobDataRunStartedAt } from './jobDataRunStartedAt'; -import type { JobDataCreatedAt } from './jobDataCreatedAt'; -import type { JobDataUpdatedAt } from './jobDataUpdatedAt'; +import type { JobType } from "./jobType"; +import type { JobStatus } from "./jobStatus"; +import type { JobDataResult } from "./jobDataResult"; +import type { JobDataLastError } from "./jobDataLastError"; +import type { JobDataRunAfter } from "./jobDataRunAfter"; +import type { JobDataRunStartedAt } from "./jobDataRunStartedAt"; +import type { JobDataCreatedAt } from "./jobDataCreatedAt"; +import type { JobDataUpdatedAt } from "./jobDataUpdatedAt"; export interface JobData { job_id: string; diff --git a/solution/frontend/src/api/generated/model/jobStatus.ts b/solution/frontend/src/api/generated/model/jobStatus.ts index 98296b3..0a1b1a3 100644 --- a/solution/frontend/src/api/generated/model/jobStatus.ts +++ b/solution/frontend/src/api/generated/model/jobStatus.ts @@ -11,8 +11,7 @@ 전이는 전부 조건부 원자 UPDATE(CAS)로만 한다. 실패는 재시도 가능하면 PENDING(run_after=백오프)으로 되돌리고, 소진되면 DEAD(dead-letter). */ -export type JobStatus = typeof JobStatus[keyof typeof JobStatus]; - +export type JobStatus = (typeof JobStatus)[keyof typeof JobStatus]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const JobStatus = { diff --git a/solution/frontend/src/api/generated/model/jobType.ts b/solution/frontend/src/api/generated/model/jobType.ts index be8484e..76e87bd 100644 --- a/solution/frontend/src/api/generated/model/jobType.ts +++ b/solution/frontend/src/api/generated/model/jobType.ts @@ -11,8 +11,7 @@ 무거운 잡(브라우저 필요)과 가벼운 잡(HTTP API 만)을 코드로 갈라 둔다 — 크롤링 법무 결론이 나면 무거운 잡만 별도 워커 이미지로 분리한다. */ -export type JobType = typeof JobType[keyof typeof JobType]; - +export type JobType = (typeof JobType)[keyof typeof JobType]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const JobType = { @@ -22,4 +21,6 @@ export const JobType = { BUILD: 4, LOCAL_SYNC: 5, AI_CHECK: 6, + SONG: 7, + ROLLBACK: 8, } as const; diff --git a/solution/frontend/src/api/generated/model/linkChannel.ts b/solution/frontend/src/api/generated/model/linkChannel.ts index 6ae5e7d..9587b58 100644 --- a/solution/frontend/src/api/generated/model/linkChannel.ts +++ b/solution/frontend/src/api/generated/model/linkChannel.ts @@ -6,10 +6,9 @@ */ /** - * place_links.channel 코드값. Perplexity 가 발견하는 채널 종류. + * place_channels.channel 코드값. Perplexity 가 발견하는 채널 종류. */ -export type LinkChannel = typeof LinkChannel[keyof typeof LinkChannel]; - +export type LinkChannel = (typeof LinkChannel)[keyof typeof LinkChannel]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const LinkChannel = { diff --git a/solution/frontend/src/api/generated/model/linkData.ts b/solution/frontend/src/api/generated/model/linkData.ts index 4054e12..de15e47 100644 --- a/solution/frontend/src/api/generated/model/linkData.ts +++ b/solution/frontend/src/api/generated/model/linkData.ts @@ -4,11 +4,11 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { LinkChannel } from './linkChannel'; -import type { LinkDataTitle } from './linkDataTitle'; -import type { SourceType } from './sourceType'; -import type { LinkDataDiscoveredAt } from './linkDataDiscoveredAt'; -import type { LinkDataConfirmedAt } from './linkDataConfirmedAt'; +import type { LinkChannel } from "./linkChannel"; +import type { LinkDataTitle } from "./linkDataTitle"; +import type { SourceType } from "./sourceType"; +import type { LinkDataDiscoveredAt } from "./linkDataDiscoveredAt"; +import type { LinkDataConfirmedAt } from "./linkDataConfirmedAt"; export interface LinkData { link_id: string; diff --git a/solution/frontend/src/api/generated/model/listContentsParams.ts b/solution/frontend/src/api/generated/model/listContentsParams.ts index 0ee2437..7da0f9b 100644 --- a/solution/frontend/src/api/generated/model/listContentsParams.ts +++ b/solution/frontend/src/api/generated/model/listContentsParams.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { LocalContentStatus } from './localContentStatus'; +import type { LocalContentStatus } from "./localContentStatus"; export type ListContentsParams = { -status?: LocalContentStatus | null; -region_code?: string | null; + status?: LocalContentStatus | null; + region_code?: string | null; }; diff --git a/solution/frontend/src/api/generated/model/listFactsParams.ts b/solution/frontend/src/api/generated/model/listFactsParams.ts index c3fa47b..c522001 100644 --- a/solution/frontend/src/api/generated/model/listFactsParams.ts +++ b/solution/frontend/src/api/generated/model/listFactsParams.ts @@ -6,9 +6,9 @@ */ export type ListFactsParams = { -/** - * 객실·메뉴·프로그램 단위 fact 만 - */ -unit_id?: string | null; -publishable_only?: boolean; + /** + * 객실·메뉴·프로그램 단위 fact 만 + */ + unit_id?: string | null; + publishable_only?: boolean; }; diff --git a/solution/frontend/src/api/generated/model/listFaqsParams.ts b/solution/frontend/src/api/generated/model/listFaqsParams.ts index d306229..96d22d1 100644 --- a/solution/frontend/src/api/generated/model/listFaqsParams.ts +++ b/solution/frontend/src/api/generated/model/listFaqsParams.ts @@ -6,5 +6,5 @@ */ export type ListFaqsParams = { -publishable_only?: boolean; + publishable_only?: boolean; }; diff --git a/solution/frontend/src/api/generated/model/listLinksParams.ts b/solution/frontend/src/api/generated/model/listLinksParams.ts index 5a33484..43e45f7 100644 --- a/solution/frontend/src/api/generated/model/listLinksParams.ts +++ b/solution/frontend/src/api/generated/model/listLinksParams.ts @@ -6,5 +6,5 @@ */ export type ListLinksParams = { -confirmed_only?: boolean; + confirmed_only?: boolean; }; diff --git a/solution/frontend/src/api/generated/model/listMediaParams.ts b/solution/frontend/src/api/generated/model/listMediaParams.ts index a7cf96f..ce86aea 100644 --- a/solution/frontend/src/api/generated/model/listMediaParams.ts +++ b/solution/frontend/src/api/generated/model/listMediaParams.ts @@ -6,9 +6,9 @@ */ export type ListMediaParams = { -/** - * 객실·메뉴 단위 사진만 - */ -unit_id?: string | null; -publishable_only?: boolean; + /** + * 객실·메뉴 단위 사진만 + */ + unit_id?: string | null; + publishable_only?: boolean; }; diff --git a/solution/frontend/src/api/generated/model/listMySitesParams.ts b/solution/frontend/src/api/generated/model/listMySitesParams.ts index 9e04322..de59272 100644 --- a/solution/frontend/src/api/generated/model/listMySitesParams.ts +++ b/solution/frontend/src/api/generated/model/listMySitesParams.ts @@ -6,13 +6,13 @@ */ export type ListMySitesParams = { -/** - * @minimum 1 - */ -page?: number; -/** - * @minimum 1 - * @maximum 100 - */ -size?: number; + /** + * @minimum 1 + */ + page?: number; + /** + * @minimum 1 + * @maximum 100 + */ + size?: number; }; diff --git a/solution/frontend/src/api/generated/model/listPlacesParams.ts b/solution/frontend/src/api/generated/model/listPlacesParams.ts index 37d7f34..5d26b46 100644 --- a/solution/frontend/src/api/generated/model/listPlacesParams.ts +++ b/solution/frontend/src/api/generated/model/listPlacesParams.ts @@ -4,29 +4,29 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { PlaceCategory } from './placeCategory'; -import type { PlaceStatus } from './placeStatus'; +import type { PlaceCategory } from "./placeCategory"; +import type { PlaceStatus } from "./placeStatus"; export type ListPlacesParams = { -/** - * 상호명·주소 부분일치 - */ -search?: string | null; -/** - * 업종 - */ -category?: PlaceCategory | null; -/** - * 상태 - */ -status?: PlaceStatus | null; -/** - * @minimum 1 - */ -page?: number; -/** - * @minimum 1 - * @maximum 100 - */ -size?: number; + /** + * 상호명·주소 부분일치 + */ + search?: string | null; + /** + * 업종 + */ + category?: PlaceCategory | null; + /** + * 상태 + */ + status?: PlaceStatus | null; + /** + * @minimum 1 + */ + page?: number; + /** + * @minimum 1 + * @maximum 100 + */ + size?: number; }; diff --git a/solution/frontend/src/api/generated/model/listShowcaseParams.ts b/solution/frontend/src/api/generated/model/listShowcaseParams.ts index 303f4de..f743693 100644 --- a/solution/frontend/src/api/generated/model/listShowcaseParams.ts +++ b/solution/frontend/src/api/generated/model/listShowcaseParams.ts @@ -6,10 +6,10 @@ */ export type ListShowcaseParams = { -/** - * 가져올 개수 - * @minimum 1 - * @maximum 48 - */ -limit?: number; + /** + * 가져올 개수 + * @minimum 1 + * @maximum 48 + */ + limit?: number; }; diff --git a/solution/frontend/src/api/generated/model/localContentData.ts b/solution/frontend/src/api/generated/model/localContentData.ts index 51d2f37..3e67387 100644 --- a/solution/frontend/src/api/generated/model/localContentData.ts +++ b/solution/frontend/src/api/generated/model/localContentData.ts @@ -4,16 +4,16 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { LocalContentType } from './localContentType'; -import type { LocalSource } from './localSource'; -import type { LocalContentDataExternalId } from './localContentDataExternalId'; -import type { LocalContentDataTitle } from './localContentDataTitle'; -import type { LocalContentDataBody } from './localContentDataBody'; -import type { LocalContentStatus } from './localContentStatus'; -import type { LocalContentDataCollectedAt } from './localContentDataCollectedAt'; -import type { LocalContentDataPublishedAt } from './localContentDataPublishedAt'; -import type { LocalContentDataDisplayStartAt } from './localContentDataDisplayStartAt'; -import type { LocalContentDataDisplayEndAt } from './localContentDataDisplayEndAt'; +import type { LocalContentType } from "./localContentType"; +import type { LocalSource } from "./localSource"; +import type { LocalContentDataExternalId } from "./localContentDataExternalId"; +import type { LocalContentDataTitle } from "./localContentDataTitle"; +import type { LocalContentDataBody } from "./localContentDataBody"; +import type { LocalContentStatus } from "./localContentStatus"; +import type { LocalContentDataCollectedAt } from "./localContentDataCollectedAt"; +import type { LocalContentDataPublishedAt } from "./localContentDataPublishedAt"; +import type { LocalContentDataDisplayStartAt } from "./localContentDataDisplayStartAt"; +import type { LocalContentDataDisplayEndAt } from "./localContentDataDisplayEndAt"; export interface LocalContentData { local_content_id: string; diff --git a/solution/frontend/src/api/generated/model/localContentStatus.ts b/solution/frontend/src/api/generated/model/localContentStatus.ts index 51092d9..a8747c9 100644 --- a/solution/frontend/src/api/generated/model/localContentStatus.ts +++ b/solution/frontend/src/api/generated/model/localContentStatus.ts @@ -8,8 +8,8 @@ /** * 운영 관리자가 검수하는 지역 콘텐츠 발행 상태. */ -export type LocalContentStatus = typeof LocalContentStatus[keyof typeof LocalContentStatus]; - +export type LocalContentStatus = + (typeof LocalContentStatus)[keyof typeof LocalContentStatus]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const LocalContentStatus = { diff --git a/solution/frontend/src/api/generated/model/localContentType.ts b/solution/frontend/src/api/generated/model/localContentType.ts index 36d49de..753d981 100644 --- a/solution/frontend/src/api/generated/model/localContentType.ts +++ b/solution/frontend/src/api/generated/model/localContentType.ts @@ -8,8 +8,8 @@ /** * local_contents.content_type 코드값. 행정구역 코드 단위로 캐싱되는 지역 정보 종류. */ -export type LocalContentType = typeof LocalContentType[keyof typeof LocalContentType]; - +export type LocalContentType = + (typeof LocalContentType)[keyof typeof LocalContentType]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const LocalContentType = { @@ -17,4 +17,6 @@ export const LocalContentType = { FESTIVAL: 2, ATTRACTION: 3, RESTAURANT: 4, + COURSE: 5, + STORY: 6, } as const; diff --git a/solution/frontend/src/api/generated/model/localSource.ts b/solution/frontend/src/api/generated/model/localSource.ts index 163adc6..99c6133 100644 --- a/solution/frontend/src/api/generated/model/localSource.ts +++ b/solution/frontend/src/api/generated/model/localSource.ts @@ -8,8 +8,7 @@ /** * local_contents.source 코드값. 어느 외부 API 에서 왔는지. */ -export type LocalSource = typeof LocalSource[keyof typeof LocalSource]; - +export type LocalSource = (typeof LocalSource)[keyof typeof LocalSource]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const LocalSource = { @@ -17,4 +16,6 @@ export const LocalSource = { TOUR_API: 2, KAKAO_LOCAL: 3, OFFICIAL_WEB: 4, + LLM: 5, + NAVER_CRAWL: 6, } as const; diff --git a/solution/frontend/src/api/generated/model/mediaData.ts b/solution/frontend/src/api/generated/model/mediaData.ts index 1539854..3d56d07 100644 --- a/solution/frontend/src/api/generated/model/mediaData.ts +++ b/solution/frontend/src/api/generated/model/mediaData.ts @@ -4,17 +4,17 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { MediaDataUnitId } from './mediaDataUnitId'; -import type { MediaDataOriginUrl } from './mediaDataOriginUrl'; -import type { SourceType } from './sourceType'; -import type { MediaDataSourceUrl } from './mediaDataSourceUrl'; -import type { MediaDataLabel } from './mediaDataLabel'; -import type { MediaDataAltText } from './mediaDataAltText'; -import type { MediaDataVisionConfidence } from './mediaDataVisionConfidence'; -import type { MediaStatus } from './mediaStatus'; -import type { MediaDataWidth } from './mediaDataWidth'; -import type { MediaDataHeight } from './mediaDataHeight'; -import type { MediaDataCreatedAt } from './mediaDataCreatedAt'; +import type { MediaDataUnitId } from "./mediaDataUnitId"; +import type { MediaDataOriginUrl } from "./mediaDataOriginUrl"; +import type { SourceType } from "./sourceType"; +import type { MediaDataSourceUrl } from "./mediaDataSourceUrl"; +import type { MediaDataLabel } from "./mediaDataLabel"; +import type { MediaDataAltText } from "./mediaDataAltText"; +import type { MediaDataVisionConfidence } from "./mediaDataVisionConfidence"; +import type { MediaStatus } from "./mediaStatus"; +import type { MediaDataWidth } from "./mediaDataWidth"; +import type { MediaDataHeight } from "./mediaDataHeight"; +import type { MediaDataCreatedAt } from "./mediaDataCreatedAt"; /** * 사진 1건. diff --git a/solution/frontend/src/api/generated/model/mediaStatus.ts b/solution/frontend/src/api/generated/model/mediaStatus.ts index f8541c1..0b997c5 100644 --- a/solution/frontend/src/api/generated/model/mediaStatus.ts +++ b/solution/frontend/src/api/generated/model/mediaStatus.ts @@ -8,8 +8,7 @@ /** * media.status 코드값. 비전 결과 신뢰도가 낮으면 자동 반영하지 않고 PENDING_REVIEW 로 둔다. */ -export type MediaStatus = typeof MediaStatus[keyof typeof MediaStatus]; - +export type MediaStatus = (typeof MediaStatus)[keyof typeof MediaStatus]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const MediaStatus = { diff --git a/solution/frontend/src/api/generated/model/mySiteData.ts b/solution/frontend/src/api/generated/model/mySiteData.ts index b3ef1d2..407fefe 100644 --- a/solution/frontend/src/api/generated/model/mySiteData.ts +++ b/solution/frontend/src/api/generated/model/mySiteData.ts @@ -4,16 +4,16 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { PlaceCategory } from './placeCategory'; -import type { PlaceStatus } from './placeStatus'; -import type { MySiteDataRoadAddress } from './mySiteDataRoadAddress'; -import type { MySiteDataCreatedAt } from './mySiteDataCreatedAt'; -import type { MySiteDataSiteId } from './mySiteDataSiteId'; -import type { MySiteDataStatus } from './mySiteDataStatus'; -import type { MySiteDataDomain } from './mySiteDataDomain'; -import type { MySiteDataTemplateId } from './mySiteDataTemplateId'; -import type { MySiteDataPublishedAt } from './mySiteDataPublishedAt'; -import type { MySiteDataThumbnailUrl } from './mySiteDataThumbnailUrl'; +import type { PlaceCategory } from "./placeCategory"; +import type { PlaceStatus } from "./placeStatus"; +import type { MySiteDataRoadAddress } from "./mySiteDataRoadAddress"; +import type { MySiteDataCreatedAt } from "./mySiteDataCreatedAt"; +import type { MySiteDataSiteId } from "./mySiteDataSiteId"; +import type { MySiteDataStatus } from "./mySiteDataStatus"; +import type { MySiteDataDomain } from "./mySiteDataDomain"; +import type { MySiteDataTemplateId } from "./mySiteDataTemplateId"; +import type { MySiteDataPublishedAt } from "./mySiteDataPublishedAt"; +import type { MySiteDataThumbnailUrl } from "./mySiteDataThumbnailUrl"; /** * 내 사이트 목록의 한 줄 — 사업장(place) + 사이트(site). @@ -33,7 +33,6 @@ export interface MySiteData { domain?: MySiteDataDomain; template_id?: MySiteDataTemplateId; published_at?: MySiteDataPublishedAt; - /** 목록 카드의 그림. 발행에 성공해야 채워지고, 발행마다 `?v=` 가 바뀐다(site_thumbnail.public_url). */ thumbnail_url?: MySiteDataThumbnailUrl; needs_rebuild?: boolean; } diff --git a/solution/frontend/src/api/generated/model/mySiteDataStatus.ts b/solution/frontend/src/api/generated/model/mySiteDataStatus.ts index 66b4a78..be84a5a 100644 --- a/solution/frontend/src/api/generated/model/mySiteDataStatus.ts +++ b/solution/frontend/src/api/generated/model/mySiteDataStatus.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { SiteStatus } from './siteStatus'; +import type { SiteStatus } from "./siteStatus"; export type MySiteDataStatus = SiteStatus | null; diff --git a/solution/frontend/src/api/generated/model/placeCandidate.ts b/solution/frontend/src/api/generated/model/placeCandidate.ts index 0cd639a..d48b187 100644 --- a/solution/frontend/src/api/generated/model/placeCandidate.ts +++ b/solution/frontend/src/api/generated/model/placeCandidate.ts @@ -4,15 +4,15 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { PlaceCandidateExternalPlaceId } from './placeCandidateExternalPlaceId'; -import type { PlaceCandidateRoadAddress } from './placeCandidateRoadAddress'; -import type { PlaceCandidateAddress } from './placeCandidateAddress'; -import type { PlaceCandidatePhone } from './placeCandidatePhone'; -import type { PlaceCandidateLatitude } from './placeCandidateLatitude'; -import type { PlaceCandidateLongitude } from './placeCandidateLongitude'; -import type { PlaceCandidateCategoryName } from './placeCandidateCategoryName'; -import type { PlaceCandidatePlaceUrl } from './placeCandidatePlaceUrl'; -import type { PlaceCandidateNaverPlaceUrl } from './placeCandidateNaverPlaceUrl'; +import type { PlaceCandidateExternalPlaceId } from "./placeCandidateExternalPlaceId"; +import type { PlaceCandidateRoadAddress } from "./placeCandidateRoadAddress"; +import type { PlaceCandidateAddress } from "./placeCandidateAddress"; +import type { PlaceCandidatePhone } from "./placeCandidatePhone"; +import type { PlaceCandidateLatitude } from "./placeCandidateLatitude"; +import type { PlaceCandidateLongitude } from "./placeCandidateLongitude"; +import type { PlaceCandidateCategoryName } from "./placeCandidateCategoryName"; +import type { PlaceCandidatePlaceUrl } from "./placeCandidatePlaceUrl"; +import type { PlaceCandidateNaverPlaceUrl } from "./placeCandidateNaverPlaceUrl"; /** * 외부 장소 DB 에서 찾은 후보 1건. UI 가 이걸 카드로 그려 사람이 고른다. diff --git a/solution/frontend/src/api/generated/model/placeCategory.ts b/solution/frontend/src/api/generated/model/placeCategory.ts index f890cb2..27a66b8 100644 --- a/solution/frontend/src/api/generated/model/placeCategory.ts +++ b/solution/frontend/src/api/generated/model/placeCategory.ts @@ -9,8 +9,7 @@ * places.category 코드값. 업종 — 스키마 파일(common/category_schema/resources/*.json)과 1:1. 업종 추가 = 여기에 코드 추가 + 스키마 파일 1개 추가. */ -export type PlaceCategory = typeof PlaceCategory[keyof typeof PlaceCategory]; - +export type PlaceCategory = (typeof PlaceCategory)[keyof typeof PlaceCategory]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const PlaceCategory = { diff --git a/solution/frontend/src/api/generated/model/placeContentData.ts b/solution/frontend/src/api/generated/model/placeContentData.ts new file mode 100644 index 0000000..33969c8 --- /dev/null +++ b/solution/frontend/src/api/generated/model/placeContentData.ts @@ -0,0 +1,27 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ +import type { LocalContentType } from "./localContentType"; +import type { PlaceContentDataBody } from "./placeContentDataBody"; +import type { PlaceContentDataDisplayEndAt } from "./placeContentDataDisplayEndAt"; +import type { PlaceContentDataCollectedAt } from "./placeContentDataCollectedAt"; + +/** + * 업장 반경 주변정보 1건(admin 목록용). + */ +export interface PlaceContentData { + place_content_id: string; + place_id: string; + content_type: LocalContentType; + external_id: string; + title: string; + body: PlaceContentDataBody; + distance_m: number; + has_image: boolean; + hidden: boolean; + display_end_at?: PlaceContentDataDisplayEndAt; + collected_at?: PlaceContentDataCollectedAt; +} diff --git a/solution/frontend/src/api/generated/model/placeContentDataBody.ts b/solution/frontend/src/api/generated/model/placeContentDataBody.ts new file mode 100644 index 0000000..1186260 --- /dev/null +++ b/solution/frontend/src/api/generated/model/placeContentDataBody.ts @@ -0,0 +1,8 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ + +export type PlaceContentDataBody = { [key: string]: unknown }; diff --git a/solution/frontend/src/api/generated/model/placeContentDataCollectedAt.ts b/solution/frontend/src/api/generated/model/placeContentDataCollectedAt.ts new file mode 100644 index 0000000..c76d886 --- /dev/null +++ b/solution/frontend/src/api/generated/model/placeContentDataCollectedAt.ts @@ -0,0 +1,8 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ + +export type PlaceContentDataCollectedAt = string | null; diff --git a/solution/frontend/src/api/generated/model/placeContentDataDisplayEndAt.ts b/solution/frontend/src/api/generated/model/placeContentDataDisplayEndAt.ts new file mode 100644 index 0000000..d46531c --- /dev/null +++ b/solution/frontend/src/api/generated/model/placeContentDataDisplayEndAt.ts @@ -0,0 +1,8 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ + +export type PlaceContentDataDisplayEndAt = string | null; diff --git a/solution/frontend/src/api/generated/model/placeData.ts b/solution/frontend/src/api/generated/model/placeData.ts index 7fbfef8..ca5cad6 100644 --- a/solution/frontend/src/api/generated/model/placeData.ts +++ b/solution/frontend/src/api/generated/model/placeData.ts @@ -4,20 +4,20 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { PlaceCategory } from './placeCategory'; -import type { PlaceStatus } from './placeStatus'; -import type { PlaceDataOwnerUserId } from './placeDataOwnerUserId'; -import type { PlaceDataExternalSource } from './placeDataExternalSource'; -import type { PlaceDataExternalPlaceId } from './placeDataExternalPlaceId'; -import type { PlaceDataRoadAddress } from './placeDataRoadAddress'; -import type { PlaceDataAddress } from './placeDataAddress'; -import type { PlaceDataPhone } from './placeDataPhone'; -import type { PlaceDataLatitude } from './placeDataLatitude'; -import type { PlaceDataLongitude } from './placeDataLongitude'; -import type { PlaceDataRegionCode } from './placeDataRegionCode'; -import type { PlaceDataVerifiedAt } from './placeDataVerifiedAt'; -import type { PlaceDataContentUpdatedAt } from './placeDataContentUpdatedAt'; -import type { PlaceDataCreatedAt } from './placeDataCreatedAt'; +import type { PlaceCategory } from "./placeCategory"; +import type { PlaceStatus } from "./placeStatus"; +import type { PlaceDataOwnerUserId } from "./placeDataOwnerUserId"; +import type { PlaceDataExternalSource } from "./placeDataExternalSource"; +import type { PlaceDataExternalPlaceId } from "./placeDataExternalPlaceId"; +import type { PlaceDataRoadAddress } from "./placeDataRoadAddress"; +import type { PlaceDataAddress } from "./placeDataAddress"; +import type { PlaceDataPhone } from "./placeDataPhone"; +import type { PlaceDataLatitude } from "./placeDataLatitude"; +import type { PlaceDataLongitude } from "./placeDataLongitude"; +import type { PlaceDataRegionCode } from "./placeDataRegionCode"; +import type { PlaceDataVerifiedAt } from "./placeDataVerifiedAt"; +import type { PlaceDataContentUpdatedAt } from "./placeDataContentUpdatedAt"; +import type { PlaceDataCreatedAt } from "./placeDataCreatedAt"; export interface PlaceData { place_id: string; diff --git a/solution/frontend/src/api/generated/model/placeDataExternalSource.ts b/solution/frontend/src/api/generated/model/placeDataExternalSource.ts index 03c0749..7f9febf 100644 --- a/solution/frontend/src/api/generated/model/placeDataExternalSource.ts +++ b/solution/frontend/src/api/generated/model/placeDataExternalSource.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ExternalPlaceSource } from './externalPlaceSource'; +import type { ExternalPlaceSource } from "./externalPlaceSource"; export type PlaceDataExternalSource = ExternalPlaceSource | null; diff --git a/solution/frontend/src/api/generated/model/placeSearchItem.ts b/solution/frontend/src/api/generated/model/placeSearchItem.ts index ee94833..5007240 100644 --- a/solution/frontend/src/api/generated/model/placeSearchItem.ts +++ b/solution/frontend/src/api/generated/model/placeSearchItem.ts @@ -4,9 +4,10 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { PlaceSearchItemRoadAddress } from './placeSearchItemRoadAddress'; -import type { PlaceSearchItemCategoryName } from './placeSearchItemCategoryName'; -import type { PlaceSearchItemCategory } from './placeSearchItemCategory'; +import type { PlaceSearchItemRoadAddress } from "./placeSearchItemRoadAddress"; +import type { PlaceSearchItemCategoryName } from "./placeSearchItemCategoryName"; +import type { PlaceSearchItemCategory } from "./placeSearchItemCategory"; +import type { PlaceSearchItemNaverPlaceUrl } from "./placeSearchItemNaverPlaceUrl"; /** * 공개 검색 결과 1건. @@ -21,6 +22,5 @@ export interface PlaceSearchItem { road_address?: PlaceSearchItemRoadAddress; category_name?: PlaceSearchItemCategoryName; category?: PlaceSearchItemCategory; - /** @nullable */ - naver_place_url?: string | null; + naver_place_url?: PlaceSearchItemNaverPlaceUrl; } diff --git a/solution/frontend/src/api/generated/model/placeSearchItemCategory.ts b/solution/frontend/src/api/generated/model/placeSearchItemCategory.ts index 4c4e071..7f2be7d 100644 --- a/solution/frontend/src/api/generated/model/placeSearchItemCategory.ts +++ b/solution/frontend/src/api/generated/model/placeSearchItemCategory.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { PlaceCategory } from './placeCategory'; +import type { PlaceCategory } from "./placeCategory"; export type PlaceSearchItemCategory = PlaceCategory | null; diff --git a/solution/frontend/src/api/generated/model/placeStatus.ts b/solution/frontend/src/api/generated/model/placeStatus.ts index 5693a00..960273d 100644 --- a/solution/frontend/src/api/generated/model/placeStatus.ts +++ b/solution/frontend/src/api/generated/model/placeStatus.ts @@ -9,8 +9,7 @@ * places.status 코드값. 사업장 생애주기. 해지는 삭제가 아니라 SUSPENDED 로의 상태 전이다(색인된 페이지를 갑자기 404 로 만들지 않는다). */ -export type PlaceStatus = typeof PlaceStatus[keyof typeof PlaceStatus]; - +export type PlaceStatus = (typeof PlaceStatus)[keyof typeof PlaceStatus]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const PlaceStatus = { diff --git a/solution/frontend/src/api/generated/model/publishAction.ts b/solution/frontend/src/api/generated/model/publishAction.ts index 0d56e92..16d1acd 100644 --- a/solution/frontend/src/api/generated/model/publishAction.ts +++ b/solution/frontend/src/api/generated/model/publishAction.ts @@ -8,8 +8,7 @@ /** * publish_logs.action 코드값. */ -export type PublishAction = typeof PublishAction[keyof typeof PublishAction]; - +export type PublishAction = (typeof PublishAction)[keyof typeof PublishAction]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const PublishAction = { @@ -18,4 +17,5 @@ export const PublishAction = { REBUILD: 3, SUSPEND: 4, RESUME: 5, + ROLLBACK: 6, } as const; diff --git a/solution/frontend/src/api/generated/model/publishLogData.ts b/solution/frontend/src/api/generated/model/publishLogData.ts index 2e0ed79..b2714b6 100644 --- a/solution/frontend/src/api/generated/model/publishLogData.ts +++ b/solution/frontend/src/api/generated/model/publishLogData.ts @@ -4,11 +4,11 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { PublishAction } from './publishAction'; -import type { PublishResult } from './publishResult'; -import type { PublishLogDataRejectReason } from './publishLogDataRejectReason'; -import type { PublishLogDataDetail } from './publishLogDataDetail'; -import type { PublishLogDataCreatedAt } from './publishLogDataCreatedAt'; +import type { PublishAction } from "./publishAction"; +import type { PublishResult } from "./publishResult"; +import type { PublishLogDataRejectReason } from "./publishLogDataRejectReason"; +import type { PublishLogDataDetail } from "./publishLogDataDetail"; +import type { PublishLogDataCreatedAt } from "./publishLogDataCreatedAt"; export interface PublishLogData { publish_log_id: string; diff --git a/solution/frontend/src/api/generated/model/publishLogDataRejectReason.ts b/solution/frontend/src/api/generated/model/publishLogDataRejectReason.ts index db9fb8f..a1294b6 100644 --- a/solution/frontend/src/api/generated/model/publishLogDataRejectReason.ts +++ b/solution/frontend/src/api/generated/model/publishLogDataRejectReason.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { PublishRejectReason } from './publishRejectReason'; +import type { PublishRejectReason } from "./publishRejectReason"; export type PublishLogDataRejectReason = PublishRejectReason | null; diff --git a/solution/frontend/src/api/generated/model/publishRejectReason.ts b/solution/frontend/src/api/generated/model/publishRejectReason.ts index 6fcb170..cbd0992 100644 --- a/solution/frontend/src/api/generated/model/publishRejectReason.ts +++ b/solution/frontend/src/api/generated/model/publishRejectReason.ts @@ -8,8 +8,8 @@ /** * publish_logs.reject_reason 코드값. 검수 게이트가 발행을 막은 이유(절대규칙 1~3). */ -export type PublishRejectReason = typeof PublishRejectReason[keyof typeof PublishRejectReason]; - +export type PublishRejectReason = + (typeof PublishRejectReason)[keyof typeof PublishRejectReason]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const PublishRejectReason = { diff --git a/solution/frontend/src/api/generated/model/publishResult.ts b/solution/frontend/src/api/generated/model/publishResult.ts index 1c2d2a4..8f68766 100644 --- a/solution/frontend/src/api/generated/model/publishResult.ts +++ b/solution/frontend/src/api/generated/model/publishResult.ts @@ -8,8 +8,7 @@ /** * publish_logs.result 코드값. */ -export type PublishResult = typeof PublishResult[keyof typeof PublishResult]; - +export type PublishResult = (typeof PublishResult)[keyof typeof PublishResult]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const PublishResult = { diff --git a/solution/frontend/src/api/generated/model/relayParams.ts b/solution/frontend/src/api/generated/model/relayParams.ts new file mode 100644 index 0000000..fd195c6 --- /dev/null +++ b/solution/frontend/src/api/generated/model/relayParams.ts @@ -0,0 +1,14 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ + +export type RelayParams = { + /** + * @minLength 8 + * @maxLength 2000 + */ + url: string; +}; diff --git a/solution/frontend/src/api/generated/model/renderStatusData.ts b/solution/frontend/src/api/generated/model/renderStatusData.ts index 697aa96..4f20757 100644 --- a/solution/frontend/src/api/generated/model/renderStatusData.ts +++ b/solution/frontend/src/api/generated/model/renderStatusData.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { RenderStatusDataRenderedAt } from './renderStatusDataRenderedAt'; -import type { RenderStatusDataRenderedVersion } from './renderStatusDataRenderedVersion'; -import type { RenderStatusDataError } from './renderStatusDataError'; +import type { RenderStatusDataRenderedAt } from "./renderStatusDataRenderedAt"; +import type { RenderStatusDataRenderedVersion } from "./renderStatusDataRenderedVersion"; +import type { RenderStatusDataError } from "./renderStatusDataError"; /** * 정적 페이지가 실제로 구워졌는지. 프리렌더가 남긴 보고서를 그대로 옮긴다. diff --git a/solution/frontend/src/api/generated/model/reqCreateFaq.ts b/solution/frontend/src/api/generated/model/reqCreateFaq.ts index 7e5c5f6..45f96b8 100644 --- a/solution/frontend/src/api/generated/model/reqCreateFaq.ts +++ b/solution/frontend/src/api/generated/model/reqCreateFaq.ts @@ -4,7 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ReqCreateFaqSortOrder } from './reqCreateFaqSortOrder'; +import type { ReqCreateFaqSortOrder } from "./reqCreateFaqSortOrder"; /** * 사장님이 직접 쓴 FAQ. diff --git a/solution/frontend/src/api/generated/model/reqCreateLink.ts b/solution/frontend/src/api/generated/model/reqCreateLink.ts index df1e00b..6767e5c 100644 --- a/solution/frontend/src/api/generated/model/reqCreateLink.ts +++ b/solution/frontend/src/api/generated/model/reqCreateLink.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { LinkChannel } from './linkChannel'; -import type { ReqCreateLinkTitle } from './reqCreateLinkTitle'; -import type { SourceType } from './sourceType'; +import type { LinkChannel } from "./linkChannel"; +import type { ReqCreateLinkTitle } from "./reqCreateLinkTitle"; +import type { SourceType } from "./sourceType"; export interface ReqCreateLink { channel?: LinkChannel; diff --git a/solution/frontend/src/api/generated/model/reqCreatePlace.ts b/solution/frontend/src/api/generated/model/reqCreatePlace.ts index d117aab..5b53147 100644 --- a/solution/frontend/src/api/generated/model/reqCreatePlace.ts +++ b/solution/frontend/src/api/generated/model/reqCreatePlace.ts @@ -4,11 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { PlaceCategory } from './placeCategory'; -import type { ReqCreatePlaceOwnerUserId } from './reqCreatePlaceOwnerUserId'; +import type { PlaceCategory } from "./placeCategory"; export interface ReqCreatePlace { name?: string; category?: PlaceCategory; - owner_user_id?: ReqCreatePlaceOwnerUserId; } diff --git a/solution/frontend/src/api/generated/model/reqHidePlaceContent.ts b/solution/frontend/src/api/generated/model/reqHidePlaceContent.ts new file mode 100644 index 0000000..d42f34c --- /dev/null +++ b/solution/frontend/src/api/generated/model/reqHidePlaceContent.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ + +export interface ReqHidePlaceContent { + hidden: boolean; +} diff --git a/solution/frontend/src/api/generated/model/reqRollback.ts b/solution/frontend/src/api/generated/model/reqRollback.ts new file mode 100644 index 0000000..1b8f5e4 --- /dev/null +++ b/solution/frontend/src/api/generated/model/reqRollback.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ + +/** + * 예전 버전으로 공개 주소를 되돌린다. + +★ 재굽기가 아니다 — 대상 버전이 디스크에 아직 있으면 심볼릭 링크만 돌린다. 지워졌으면 + (보관 정책, prerender.ts pruneOldVersions) site_versions.snapshot 으로 다시 굽고 나서 + 돌린다. 어느 경우든 게이트를 다시 통과해야 한다(사장님이 이미 확인한 값이라 대개는 + 그대로 통과한다). + */ +export interface ReqRollback { + target_version: number; +} diff --git a/solution/frontend/src/api/generated/model/reqSignup.ts b/solution/frontend/src/api/generated/model/reqSignup.ts index ca5de09..7ca2aa0 100644 --- a/solution/frontend/src/api/generated/model/reqSignup.ts +++ b/solution/frontend/src/api/generated/model/reqSignup.ts @@ -4,7 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ReqSignupName } from './reqSignupName'; +import type { ReqSignupName } from "./reqSignupName"; /** * id/pw 가입. 가입 = 계정 1개다. diff --git a/solution/frontend/src/api/generated/model/reqSiteStatus.ts b/solution/frontend/src/api/generated/model/reqSiteStatus.ts index d1037bd..aaa369f 100644 --- a/solution/frontend/src/api/generated/model/reqSiteStatus.ts +++ b/solution/frontend/src/api/generated/model/reqSiteStatus.ts @@ -4,7 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { PublishAction } from './publishAction'; +import type { PublishAction } from "./publishAction"; /** * 발행 상태 전이. ★ 해지는 삭제가 아니라 상태 전이다 — diff --git a/solution/frontend/src/api/generated/model/reqSiteTheme.ts b/solution/frontend/src/api/generated/model/reqSiteTheme.ts index eff465b..ebaee6a 100644 --- a/solution/frontend/src/api/generated/model/reqSiteTheme.ts +++ b/solution/frontend/src/api/generated/model/reqSiteTheme.ts @@ -4,7 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ReqSiteThemeTheme } from './reqSiteThemeTheme'; +import type { ReqSiteThemeTheme } from "./reqSiteThemeTheme"; /** * 디자인(색·서체·섹션) 저장. 에디터 좌측 패널과 [디자인] 탭이 만든 결과 그대로 온다. diff --git a/solution/frontend/src/api/generated/model/reqStartCopy.ts b/solution/frontend/src/api/generated/model/reqStartCopy.ts index 65d37ca..151e6d8 100644 --- a/solution/frontend/src/api/generated/model/reqStartCopy.ts +++ b/solution/frontend/src/api/generated/model/reqStartCopy.ts @@ -10,4 +10,6 @@ ★ 확인된 fact 만 근거로 쓴다. 근거가 없으면 생성하지 않는다(유료 호출조차 안 한다). */ -export interface ReqStartCopy { [key: string]: unknown } +export interface ReqStartCopy { + [key: string]: unknown; +} diff --git a/solution/frontend/src/api/generated/model/reqSyncFestivals.ts b/solution/frontend/src/api/generated/model/reqSyncFestivals.ts deleted file mode 100644 index 5dbf5e9..0000000 --- a/solution/frontend/src/api/generated/model/reqSyncFestivals.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Generated by orval v7.21.0 🍺 - * Do not edit manually. - * Web4Ai API - * OpenAPI spec version: 0.1.0 - */ -import type { ReqSyncFestivalsAreaCode } from './reqSyncFestivalsAreaCode'; -import type { ReqSyncFestivalsStartDate } from './reqSyncFestivalsStartDate'; - -export interface ReqSyncFestivals { - /** - * @minLength 1 - * @maxLength 10 - */ - region_code: string; - area_code?: ReqSyncFestivalsAreaCode; - start_date?: ReqSyncFestivalsStartDate; -} diff --git a/solution/frontend/src/api/generated/model/reqTransitionFact.ts b/solution/frontend/src/api/generated/model/reqTransitionFact.ts index 2cdbcfe..c607356 100644 --- a/solution/frontend/src/api/generated/model/reqTransitionFact.ts +++ b/solution/frontend/src/api/generated/model/reqTransitionFact.ts @@ -4,8 +4,8 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { FactStatus } from './factStatus'; -import type { ReqTransitionFactValue } from './reqTransitionFactValue'; +import type { FactStatus } from "./factStatus"; +import type { ReqTransitionFactValue } from "./reqTransitionFactValue"; /** * 검증 상태 전이. 허용 전이는 FACT_STATUS_TRANSITIONS 가 유일한 소스다. diff --git a/solution/frontend/src/api/generated/model/reqTransitionFaq.ts b/solution/frontend/src/api/generated/model/reqTransitionFaq.ts index a83b581..571b384 100644 --- a/solution/frontend/src/api/generated/model/reqTransitionFaq.ts +++ b/solution/frontend/src/api/generated/model/reqTransitionFaq.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { FactStatus } from './factStatus'; -import type { ReqTransitionFaqQuestion } from './reqTransitionFaqQuestion'; -import type { ReqTransitionFaqAnswer } from './reqTransitionFaqAnswer'; +import type { FactStatus } from "./factStatus"; +import type { ReqTransitionFaqQuestion } from "./reqTransitionFaqQuestion"; +import type { ReqTransitionFaqAnswer } from "./reqTransitionFaqAnswer"; /** * 검증 상태 전이. 허용 전이는 fact 와 같은 표(FACT_STATUS_TRANSITIONS)가 유일한 소스다. diff --git a/solution/frontend/src/api/generated/model/reqUpdateLocalContent.ts b/solution/frontend/src/api/generated/model/reqUpdateLocalContent.ts index 1d868f0..5cfcdff 100644 --- a/solution/frontend/src/api/generated/model/reqUpdateLocalContent.ts +++ b/solution/frontend/src/api/generated/model/reqUpdateLocalContent.ts @@ -4,10 +4,10 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ReqUpdateLocalContentTitle } from './reqUpdateLocalContentTitle'; -import type { ReqUpdateLocalContentBody } from './reqUpdateLocalContentBody'; -import type { ReqUpdateLocalContentDisplayStartAt } from './reqUpdateLocalContentDisplayStartAt'; -import type { ReqUpdateLocalContentDisplayEndAt } from './reqUpdateLocalContentDisplayEndAt'; +import type { ReqUpdateLocalContentTitle } from "./reqUpdateLocalContentTitle"; +import type { ReqUpdateLocalContentBody } from "./reqUpdateLocalContentBody"; +import type { ReqUpdateLocalContentDisplayStartAt } from "./reqUpdateLocalContentDisplayStartAt"; +import type { ReqUpdateLocalContentDisplayEndAt } from "./reqUpdateLocalContentDisplayEndAt"; export interface ReqUpdateLocalContent { title?: ReqUpdateLocalContentTitle; diff --git a/solution/frontend/src/api/generated/model/reqUpdateLocalContentBody.ts b/solution/frontend/src/api/generated/model/reqUpdateLocalContentBody.ts index be3d4ce..54cbd1b 100644 --- a/solution/frontend/src/api/generated/model/reqUpdateLocalContentBody.ts +++ b/solution/frontend/src/api/generated/model/reqUpdateLocalContentBody.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ReqUpdateLocalContentBodyAnyOf } from './reqUpdateLocalContentBodyAnyOf'; +import type { ReqUpdateLocalContentBodyAnyOf } from "./reqUpdateLocalContentBodyAnyOf"; export type ReqUpdateLocalContentBody = ReqUpdateLocalContentBodyAnyOf | null; diff --git a/solution/frontend/src/api/generated/model/reqUpdateMe.ts b/solution/frontend/src/api/generated/model/reqUpdateMe.ts index 288ebaa..cb0360c 100644 --- a/solution/frontend/src/api/generated/model/reqUpdateMe.ts +++ b/solution/frontend/src/api/generated/model/reqUpdateMe.ts @@ -4,10 +4,10 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ReqUpdateMeName } from './reqUpdateMeName'; -import type { ReqUpdateMeEmail } from './reqUpdateMeEmail'; -import type { ReqUpdateMeContactNumber } from './reqUpdateMeContactNumber'; -import type { ReqUpdateMePassword } from './reqUpdateMePassword'; +import type { ReqUpdateMeName } from "./reqUpdateMeName"; +import type { ReqUpdateMeEmail } from "./reqUpdateMeEmail"; +import type { ReqUpdateMeContactNumber } from "./reqUpdateMeContactNumber"; +import type { ReqUpdateMePassword } from "./reqUpdateMePassword"; export interface ReqUpdateMe { name?: ReqUpdateMeName; diff --git a/solution/frontend/src/api/generated/model/reqUpdatePlace.ts b/solution/frontend/src/api/generated/model/reqUpdatePlace.ts index 86f7688..5b7a7a0 100644 --- a/solution/frontend/src/api/generated/model/reqUpdatePlace.ts +++ b/solution/frontend/src/api/generated/model/reqUpdatePlace.ts @@ -4,12 +4,10 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ReqUpdatePlaceName } from './reqUpdatePlaceName'; -import type { ReqUpdatePlaceOwnerUserId } from './reqUpdatePlaceOwnerUserId'; -import type { ReqUpdatePlaceStatus } from './reqUpdatePlaceStatus'; +import type { ReqUpdatePlaceName } from "./reqUpdatePlaceName"; +import type { ReqUpdatePlaceStatus } from "./reqUpdatePlaceStatus"; export interface ReqUpdatePlace { name?: ReqUpdatePlaceName; - owner_user_id?: ReqUpdatePlaceOwnerUserId; status?: ReqUpdatePlaceStatus; } diff --git a/solution/frontend/src/api/generated/model/reqUpdatePlaceStatus.ts b/solution/frontend/src/api/generated/model/reqUpdatePlaceStatus.ts index a1da292..14bb823 100644 --- a/solution/frontend/src/api/generated/model/reqUpdatePlaceStatus.ts +++ b/solution/frontend/src/api/generated/model/reqUpdatePlaceStatus.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { PlaceStatus } from './placeStatus'; +import type { PlaceStatus } from "./placeStatus"; export type ReqUpdatePlaceStatus = PlaceStatus | null; diff --git a/solution/frontend/src/api/generated/model/reqUpsertFact.ts b/solution/frontend/src/api/generated/model/reqUpsertFact.ts index 24c2e6e..057576f 100644 --- a/solution/frontend/src/api/generated/model/reqUpsertFact.ts +++ b/solution/frontend/src/api/generated/model/reqUpsertFact.ts @@ -4,11 +4,11 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ReqUpsertFactValue } from './reqUpsertFactValue'; -import type { ReqUpsertFactUnitId } from './reqUpsertFactUnitId'; -import type { SourceType } from './sourceType'; -import type { ReqUpsertFactSourceUrl } from './reqUpsertFactSourceUrl'; -import type { ReqUpsertFactExpiresAt } from './reqUpsertFactExpiresAt'; +import type { ReqUpsertFactValue } from "./reqUpsertFactValue"; +import type { ReqUpsertFactUnitId } from "./reqUpsertFactUnitId"; +import type { SourceType } from "./sourceType"; +import type { ReqUpsertFactSourceUrl } from "./reqUpsertFactSourceUrl"; +import type { ReqUpsertFactExpiresAt } from "./reqUpsertFactExpiresAt"; /** * fact 기록. key 는 사업장 업종의 스키마에 있는 것만 허용한다. diff --git a/solution/frontend/src/api/generated/model/reqVerifyPlace.ts b/solution/frontend/src/api/generated/model/reqVerifyPlace.ts index 30de6c0..bee8de0 100644 --- a/solution/frontend/src/api/generated/model/reqVerifyPlace.ts +++ b/solution/frontend/src/api/generated/model/reqVerifyPlace.ts @@ -4,14 +4,15 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ExternalPlaceSource } from './externalPlaceSource'; -import type { ReqVerifyPlacePlaceUrl } from './reqVerifyPlacePlaceUrl'; -import type { ReqVerifyPlaceRoadAddress } from './reqVerifyPlaceRoadAddress'; -import type { ReqVerifyPlaceAddress } from './reqVerifyPlaceAddress'; -import type { ReqVerifyPlacePhone } from './reqVerifyPlacePhone'; -import type { ReqVerifyPlaceLatitude } from './reqVerifyPlaceLatitude'; -import type { ReqVerifyPlaceLongitude } from './reqVerifyPlaceLongitude'; -import type { ReqVerifyPlaceRegionCode } from './reqVerifyPlaceRegionCode'; +import type { ExternalPlaceSource } from "./externalPlaceSource"; +import type { ReqVerifyPlacePlaceUrl } from "./reqVerifyPlacePlaceUrl"; +import type { ReqVerifyPlaceRoadAddress } from "./reqVerifyPlaceRoadAddress"; +import type { ReqVerifyPlaceAddress } from "./reqVerifyPlaceAddress"; +import type { ReqVerifyPlacePhone } from "./reqVerifyPlacePhone"; +import type { ReqVerifyPlaceLatitude } from "./reqVerifyPlaceLatitude"; +import type { ReqVerifyPlaceLongitude } from "./reqVerifyPlaceLongitude"; +import type { ReqVerifyPlaceRegionCode } from "./reqVerifyPlaceRegionCode"; +import type { ReqVerifyPlaceCategoryName } from "./reqVerifyPlaceCategoryName"; /** * 카카오 로컬 조회 결과를 사업장에 박제한다(동일 업소 확정). @@ -30,4 +31,5 @@ export interface ReqVerifyPlace { latitude?: ReqVerifyPlaceLatitude; longitude?: ReqVerifyPlaceLongitude; region_code?: ReqVerifyPlaceRegionCode; + category_name?: ReqVerifyPlaceCategoryName; } diff --git a/solution/frontend/src/api/generated/model/reqCreatePlaceOwnerUserId.ts b/solution/frontend/src/api/generated/model/reqVerifyPlaceCategoryName.ts similarity index 67% rename from solution/frontend/src/api/generated/model/reqCreatePlaceOwnerUserId.ts rename to solution/frontend/src/api/generated/model/reqVerifyPlaceCategoryName.ts index 26da46e..862cdea 100644 --- a/solution/frontend/src/api/generated/model/reqCreatePlaceOwnerUserId.ts +++ b/solution/frontend/src/api/generated/model/reqVerifyPlaceCategoryName.ts @@ -5,4 +5,4 @@ * OpenAPI spec version: 0.1.0 */ -export type ReqCreatePlaceOwnerUserId = string | null; +export type ReqVerifyPlaceCategoryName = string | null; diff --git a/solution/frontend/src/api/generated/model/resCategorySchema.ts b/solution/frontend/src/api/generated/model/resCategorySchema.ts index bf4a647..e6cfada 100644 --- a/solution/frontend/src/api/generated/model/resCategorySchema.ts +++ b/solution/frontend/src/api/generated/model/resCategorySchema.ts @@ -4,11 +4,11 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResCategorySchemaMsg } from './resCategorySchemaMsg'; -import type { ResCategorySchemaCategory } from './resCategorySchemaCategory'; -import type { ResCategorySchemaLabel } from './resCategorySchemaLabel'; -import type { FieldSpecData } from './fieldSpecData'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResCategorySchemaMsg } from "./resCategorySchemaMsg"; +import type { ResCategorySchemaCategory } from "./resCategorySchemaCategory"; +import type { ResCategorySchemaLabel } from "./resCategorySchemaLabel"; +import type { FieldSpecData } from "./fieldSpecData"; export interface ResCategorySchema { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resExtractFacts.ts b/solution/frontend/src/api/generated/model/resExtractFacts.ts index 0c0c9ed..9cdfa1c 100644 --- a/solution/frontend/src/api/generated/model/resExtractFacts.ts +++ b/solution/frontend/src/api/generated/model/resExtractFacts.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResExtractFactsMsg } from './resExtractFactsMsg'; -import type { ResExtractedFact } from './resExtractedFact'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResExtractFactsMsg } from "./resExtractFactsMsg"; +import type { ResExtractedFact } from "./resExtractedFact"; /** * 뽑힌 것과 버려진 것을 **둘 다** 돌려준다. diff --git a/solution/frontend/src/api/generated/model/resExtractedFact.ts b/solution/frontend/src/api/generated/model/resExtractedFact.ts index b468695..64ebddf 100644 --- a/solution/frontend/src/api/generated/model/resExtractedFact.ts +++ b/solution/frontend/src/api/generated/model/resExtractedFact.ts @@ -4,8 +4,8 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ResExtractedFactUnitName } from './resExtractedFactUnitName'; -import type { ResExtractedFactReason } from './resExtractedFactReason'; +import type { ResExtractedFactUnitName } from "./resExtractedFactUnitName"; +import type { ResExtractedFactReason } from "./resExtractedFactReason"; export interface ResExtractedFact { key?: string; diff --git a/solution/frontend/src/api/generated/model/resFact.ts b/solution/frontend/src/api/generated/model/resFact.ts index 6e0f80c..9a430e2 100644 --- a/solution/frontend/src/api/generated/model/resFact.ts +++ b/solution/frontend/src/api/generated/model/resFact.ts @@ -4,10 +4,10 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResFactMsg } from './resFactMsg'; -import type { ResFactFact } from './resFactFact'; -import type { ResFactOutcome } from './resFactOutcome'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResFactMsg } from "./resFactMsg"; +import type { ResFactFact } from "./resFactFact"; +import type { ResFactOutcome } from "./resFactOutcome"; export interface ResFact { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resFactFact.ts b/solution/frontend/src/api/generated/model/resFactFact.ts index e09c9f5..55a63aa 100644 --- a/solution/frontend/src/api/generated/model/resFactFact.ts +++ b/solution/frontend/src/api/generated/model/resFactFact.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { FactData } from './factData'; +import type { FactData } from "./factData"; export type ResFactFact = FactData | null; diff --git a/solution/frontend/src/api/generated/model/resFactList.ts b/solution/frontend/src/api/generated/model/resFactList.ts index ce221be..2c4059f 100644 --- a/solution/frontend/src/api/generated/model/resFactList.ts +++ b/solution/frontend/src/api/generated/model/resFactList.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResFactListMsg } from './resFactListMsg'; -import type { FactData } from './factData'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResFactListMsg } from "./resFactListMsg"; +import type { FactData } from "./factData"; export interface ResFactList { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resFactOutcome.ts b/solution/frontend/src/api/generated/model/resFactOutcome.ts index 53f6ea7..608d6a7 100644 --- a/solution/frontend/src/api/generated/model/resFactOutcome.ts +++ b/solution/frontend/src/api/generated/model/resFactOutcome.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { FactWriteOutcome } from './factWriteOutcome'; +import type { FactWriteOutcome } from "./factWriteOutcome"; export type ResFactOutcome = FactWriteOutcome | null; diff --git a/solution/frontend/src/api/generated/model/resFaq.ts b/solution/frontend/src/api/generated/model/resFaq.ts index 5502505..49ec104 100644 --- a/solution/frontend/src/api/generated/model/resFaq.ts +++ b/solution/frontend/src/api/generated/model/resFaq.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResFaqMsg } from './resFaqMsg'; -import type { ResFaqFaq } from './resFaqFaq'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResFaqMsg } from "./resFaqMsg"; +import type { ResFaqFaq } from "./resFaqFaq"; export interface ResFaq { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resFaqFaq.ts b/solution/frontend/src/api/generated/model/resFaqFaq.ts index 76fc798..6585b0d 100644 --- a/solution/frontend/src/api/generated/model/resFaqFaq.ts +++ b/solution/frontend/src/api/generated/model/resFaqFaq.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { FaqData } from './faqData'; +import type { FaqData } from "./faqData"; export type ResFaqFaq = FaqData | null; diff --git a/solution/frontend/src/api/generated/model/resFaqList.ts b/solution/frontend/src/api/generated/model/resFaqList.ts index 765d1d0..4e07f10 100644 --- a/solution/frontend/src/api/generated/model/resFaqList.ts +++ b/solution/frontend/src/api/generated/model/resFaqList.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResFaqListMsg } from './resFaqListMsg'; -import type { FaqData } from './faqData'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResFaqListMsg } from "./resFaqListMsg"; +import type { FaqData } from "./faqData"; export interface ResFaqList { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resJob.ts b/solution/frontend/src/api/generated/model/resJob.ts index 2310d4b..b7b0434 100644 --- a/solution/frontend/src/api/generated/model/resJob.ts +++ b/solution/frontend/src/api/generated/model/resJob.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResJobMsg } from './resJobMsg'; -import type { ResJobJob } from './resJobJob'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResJobMsg } from "./resJobMsg"; +import type { ResJobJob } from "./resJobJob"; /** * 잡 상태 폴링 응답. 수집·빌드는 몇 분 걸리므로 클라이언트가 이 엔드포인트를 폴링한다. diff --git a/solution/frontend/src/api/generated/model/resJobJob.ts b/solution/frontend/src/api/generated/model/resJobJob.ts index 138e47a..688ea3c 100644 --- a/solution/frontend/src/api/generated/model/resJobJob.ts +++ b/solution/frontend/src/api/generated/model/resJobJob.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { JobData } from './jobData'; +import type { JobData } from "./jobData"; export type ResJobJob = JobData | null; diff --git a/solution/frontend/src/api/generated/model/resJobOps.ts b/solution/frontend/src/api/generated/model/resJobOps.ts index c561af5..b0d81b9 100644 --- a/solution/frontend/src/api/generated/model/resJobOps.ts +++ b/solution/frontend/src/api/generated/model/resJobOps.ts @@ -4,8 +4,8 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResJobOpsMsg } from './resJobOpsMsg'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResJobOpsMsg } from "./resJobOpsMsg"; /** * 운영 스냅샷 — 큐 지연·DEAD·좀비 감시용. diff --git a/solution/frontend/src/api/generated/model/resLink.ts b/solution/frontend/src/api/generated/model/resLink.ts index 766394b..68419c8 100644 --- a/solution/frontend/src/api/generated/model/resLink.ts +++ b/solution/frontend/src/api/generated/model/resLink.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResLinkMsg } from './resLinkMsg'; -import type { ResLinkLink } from './resLinkLink'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResLinkMsg } from "./resLinkMsg"; +import type { ResLinkLink } from "./resLinkLink"; export interface ResLink { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resLinkLink.ts b/solution/frontend/src/api/generated/model/resLinkLink.ts index 0fb6701..d0514af 100644 --- a/solution/frontend/src/api/generated/model/resLinkLink.ts +++ b/solution/frontend/src/api/generated/model/resLinkLink.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { LinkData } from './linkData'; +import type { LinkData } from "./linkData"; export type ResLinkLink = LinkData | null; diff --git a/solution/frontend/src/api/generated/model/resLinkList.ts b/solution/frontend/src/api/generated/model/resLinkList.ts index 18e360a..68e1e2f 100644 --- a/solution/frontend/src/api/generated/model/resLinkList.ts +++ b/solution/frontend/src/api/generated/model/resLinkList.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResLinkListMsg } from './resLinkListMsg'; -import type { LinkData } from './linkData'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResLinkListMsg } from "./resLinkListMsg"; +import type { LinkData } from "./linkData"; export interface ResLinkList { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resLocalContentList.ts b/solution/frontend/src/api/generated/model/resLocalContentList.ts index 0f05af2..ddfa218 100644 --- a/solution/frontend/src/api/generated/model/resLocalContentList.ts +++ b/solution/frontend/src/api/generated/model/resLocalContentList.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResLocalContentListMsg } from './resLocalContentListMsg'; -import type { LocalContentData } from './localContentData'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResLocalContentListMsg } from "./resLocalContentListMsg"; +import type { LocalContentData } from "./localContentData"; export interface ResLocalContentList { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resLocalGuide.ts b/solution/frontend/src/api/generated/model/resLocalGuide.ts new file mode 100644 index 0000000..53bfbd3 --- /dev/null +++ b/solution/frontend/src/api/generated/model/resLocalGuide.ts @@ -0,0 +1,29 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ +import type { ErrorInfo } from "./errorInfo"; +import type { ResLocalGuideMsg } from "./resLocalGuideMsg"; +import type { ResLocalGuideAttractionsItem } from "./resLocalGuideAttractionsItem"; +import type { ResLocalGuideRestaurantsItem } from "./resLocalGuideRestaurantsItem"; +import type { ResLocalGuideFestivalsItem } from "./resLocalGuideFestivalsItem"; +import type { ResLocalGuideCoursesItem } from "./resLocalGuideCoursesItem"; +import type { ResLocalGuideItinerariesItem } from "./resLocalGuideItinerariesItem"; +import type { ResLocalGuideSyncedAt } from "./resLocalGuideSyncedAt"; + +/** + * 에디터 캔버스가 그리는 지역 가이드. ★ 항목 모양은 발행 payload 의 LocalContents 와 **동일**하다 +(services/site_payload._local 을 그대로 거친다) — 캔버스와 발행본이 다른 목록을 보이면 안 된다. + */ +export interface ResLocalGuide { + result?: ErrorInfo; + msg?: ResLocalGuideMsg; + attractions?: ResLocalGuideAttractionsItem[]; + restaurants?: ResLocalGuideRestaurantsItem[]; + festivals?: ResLocalGuideFestivalsItem[]; + courses?: ResLocalGuideCoursesItem[]; + itineraries?: ResLocalGuideItinerariesItem[]; + synced_at?: ResLocalGuideSyncedAt; +} diff --git a/solution/frontend/src/api/generated/model/resLocalGuideAttractionsItem.ts b/solution/frontend/src/api/generated/model/resLocalGuideAttractionsItem.ts new file mode 100644 index 0000000..85da430 --- /dev/null +++ b/solution/frontend/src/api/generated/model/resLocalGuideAttractionsItem.ts @@ -0,0 +1,8 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ + +export type ResLocalGuideAttractionsItem = { [key: string]: unknown }; diff --git a/solution/frontend/src/api/generated/model/resLocalGuideCoursesItem.ts b/solution/frontend/src/api/generated/model/resLocalGuideCoursesItem.ts new file mode 100644 index 0000000..b749f83 --- /dev/null +++ b/solution/frontend/src/api/generated/model/resLocalGuideCoursesItem.ts @@ -0,0 +1,8 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ + +export type ResLocalGuideCoursesItem = { [key: string]: unknown }; diff --git a/solution/frontend/src/api/generated/model/resLocalGuideFestivalsItem.ts b/solution/frontend/src/api/generated/model/resLocalGuideFestivalsItem.ts new file mode 100644 index 0000000..42b5637 --- /dev/null +++ b/solution/frontend/src/api/generated/model/resLocalGuideFestivalsItem.ts @@ -0,0 +1,8 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ + +export type ResLocalGuideFestivalsItem = { [key: string]: unknown }; diff --git a/solution/frontend/src/api/generated/model/resLocalGuideItinerariesItem.ts b/solution/frontend/src/api/generated/model/resLocalGuideItinerariesItem.ts new file mode 100644 index 0000000..e7ff431 --- /dev/null +++ b/solution/frontend/src/api/generated/model/resLocalGuideItinerariesItem.ts @@ -0,0 +1,8 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ + +export type ResLocalGuideItinerariesItem = { [key: string]: unknown }; diff --git a/solution/frontend/src/api/generated/model/resSyncFestivalsMsg.ts b/solution/frontend/src/api/generated/model/resLocalGuideMsg.ts similarity index 69% rename from solution/frontend/src/api/generated/model/resSyncFestivalsMsg.ts rename to solution/frontend/src/api/generated/model/resLocalGuideMsg.ts index be86451..9f7e1c4 100644 --- a/solution/frontend/src/api/generated/model/resSyncFestivalsMsg.ts +++ b/solution/frontend/src/api/generated/model/resLocalGuideMsg.ts @@ -5,4 +5,4 @@ * OpenAPI spec version: 0.1.0 */ -export type ResSyncFestivalsMsg = string | null; +export type ResLocalGuideMsg = string | null; diff --git a/solution/frontend/src/api/generated/model/resLocalGuideRestaurantsItem.ts b/solution/frontend/src/api/generated/model/resLocalGuideRestaurantsItem.ts new file mode 100644 index 0000000..095059c --- /dev/null +++ b/solution/frontend/src/api/generated/model/resLocalGuideRestaurantsItem.ts @@ -0,0 +1,8 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ + +export type ResLocalGuideRestaurantsItem = { [key: string]: unknown }; diff --git a/solution/frontend/src/api/generated/model/reqUpdatePlaceOwnerUserId.ts b/solution/frontend/src/api/generated/model/resLocalGuideSyncedAt.ts similarity index 67% rename from solution/frontend/src/api/generated/model/reqUpdatePlaceOwnerUserId.ts rename to solution/frontend/src/api/generated/model/resLocalGuideSyncedAt.ts index c1b2515..ed4aaec 100644 --- a/solution/frontend/src/api/generated/model/reqUpdatePlaceOwnerUserId.ts +++ b/solution/frontend/src/api/generated/model/resLocalGuideSyncedAt.ts @@ -5,4 +5,4 @@ * OpenAPI spec version: 0.1.0 */ -export type ReqUpdatePlaceOwnerUserId = string | null; +export type ResLocalGuideSyncedAt = string | null; diff --git a/solution/frontend/src/api/generated/model/resLogin.ts b/solution/frontend/src/api/generated/model/resLogin.ts index edb1605..b2e74cb 100644 --- a/solution/frontend/src/api/generated/model/resLogin.ts +++ b/solution/frontend/src/api/generated/model/resLogin.ts @@ -4,8 +4,8 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResLoginMsg } from './resLoginMsg'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResLoginMsg } from "./resLoginMsg"; export interface ResLogin { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resMe.ts b/solution/frontend/src/api/generated/model/resMe.ts index 2441fe4..1e55fc2 100644 --- a/solution/frontend/src/api/generated/model/resMe.ts +++ b/solution/frontend/src/api/generated/model/resMe.ts @@ -4,13 +4,13 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResMeMsg } from './resMeMsg'; -import type { ResMeName } from './resMeName'; -import type { ResMeEmail } from './resMeEmail'; -import type { ResMeContactNumber } from './resMeContactNumber'; -import type { UserRole } from './userRole'; -import type { AuthProvider } from './authProvider'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResMeMsg } from "./resMeMsg"; +import type { ResMeName } from "./resMeName"; +import type { ResMeEmail } from "./resMeEmail"; +import type { ResMeContactNumber } from "./resMeContactNumber"; +import type { UserRole } from "./userRole"; +import type { AuthProvider } from "./authProvider"; export interface ResMe { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resMediaList.ts b/solution/frontend/src/api/generated/model/resMediaList.ts index 53900b8..3065836 100644 --- a/solution/frontend/src/api/generated/model/resMediaList.ts +++ b/solution/frontend/src/api/generated/model/resMediaList.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResMediaListMsg } from './resMediaListMsg'; -import type { MediaData } from './mediaData'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResMediaListMsg } from "./resMediaListMsg"; +import type { MediaData } from "./mediaData"; export interface ResMediaList { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resMySites.ts b/solution/frontend/src/api/generated/model/resMySites.ts index f8c5655..d021424 100644 --- a/solution/frontend/src/api/generated/model/resMySites.ts +++ b/solution/frontend/src/api/generated/model/resMySites.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResMySitesMsg } from './resMySitesMsg'; -import type { MySiteData } from './mySiteData'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResMySitesMsg } from "./resMySitesMsg"; +import type { MySiteData } from "./mySiteData"; export interface ResMySites { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resPlace.ts b/solution/frontend/src/api/generated/model/resPlace.ts index 8cb6581..d15a911 100644 --- a/solution/frontend/src/api/generated/model/resPlace.ts +++ b/solution/frontend/src/api/generated/model/resPlace.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResPlaceMsg } from './resPlaceMsg'; -import type { ResPlacePlace } from './resPlacePlace'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResPlaceMsg } from "./resPlaceMsg"; +import type { ResPlacePlace } from "./resPlacePlace"; export interface ResPlace { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resPlaceContentList.ts b/solution/frontend/src/api/generated/model/resPlaceContentList.ts new file mode 100644 index 0000000..9171ddd --- /dev/null +++ b/solution/frontend/src/api/generated/model/resPlaceContentList.ts @@ -0,0 +1,15 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ +import type { ErrorInfo } from "./errorInfo"; +import type { ResPlaceContentListMsg } from "./resPlaceContentListMsg"; +import type { PlaceContentData } from "./placeContentData"; + +export interface ResPlaceContentList { + result?: ErrorInfo; + msg?: ResPlaceContentListMsg; + contents?: PlaceContentData[]; +} diff --git a/solution/frontend/src/api/generated/model/reqSyncFestivalsStartDate.ts b/solution/frontend/src/api/generated/model/resPlaceContentListMsg.ts similarity index 67% rename from solution/frontend/src/api/generated/model/reqSyncFestivalsStartDate.ts rename to solution/frontend/src/api/generated/model/resPlaceContentListMsg.ts index 5d2794a..d0faed4 100644 --- a/solution/frontend/src/api/generated/model/reqSyncFestivalsStartDate.ts +++ b/solution/frontend/src/api/generated/model/resPlaceContentListMsg.ts @@ -5,4 +5,4 @@ * OpenAPI spec version: 0.1.0 */ -export type ReqSyncFestivalsStartDate = string | null; +export type ResPlaceContentListMsg = string | null; diff --git a/solution/frontend/src/api/generated/model/resPlaceList.ts b/solution/frontend/src/api/generated/model/resPlaceList.ts index 724fd05..0991627 100644 --- a/solution/frontend/src/api/generated/model/resPlaceList.ts +++ b/solution/frontend/src/api/generated/model/resPlaceList.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResPlaceListMsg } from './resPlaceListMsg'; -import type { PlaceData } from './placeData'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResPlaceListMsg } from "./resPlaceListMsg"; +import type { PlaceData } from "./placeData"; export interface ResPlaceList { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resPlacePlace.ts b/solution/frontend/src/api/generated/model/resPlacePlace.ts index d27c75f..dcb3f70 100644 --- a/solution/frontend/src/api/generated/model/resPlacePlace.ts +++ b/solution/frontend/src/api/generated/model/resPlacePlace.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { PlaceData } from './placeData'; +import type { PlaceData } from "./placeData"; export type ResPlacePlace = PlaceData | null; diff --git a/solution/frontend/src/api/generated/model/resPlaceSearch.ts b/solution/frontend/src/api/generated/model/resPlaceSearch.ts index beb75cc..0481b69 100644 --- a/solution/frontend/src/api/generated/model/resPlaceSearch.ts +++ b/solution/frontend/src/api/generated/model/resPlaceSearch.ts @@ -4,10 +4,10 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResPlaceSearchMsg } from './resPlaceSearchMsg'; -import type { ResPlaceSearchSource } from './resPlaceSearchSource'; -import type { PlaceSearchItem } from './placeSearchItem'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResPlaceSearchMsg } from "./resPlaceSearchMsg"; +import type { ResPlaceSearchSource } from "./resPlaceSearchSource"; +import type { PlaceSearchItem } from "./placeSearchItem"; /** * 상호명 공개 검색 결과. diff --git a/solution/frontend/src/api/generated/model/resPlaceSearchSource.ts b/solution/frontend/src/api/generated/model/resPlaceSearchSource.ts index c1ea375..e89c4df 100644 --- a/solution/frontend/src/api/generated/model/resPlaceSearchSource.ts +++ b/solution/frontend/src/api/generated/model/resPlaceSearchSource.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ExternalPlaceSource } from './externalPlaceSource'; +import type { ExternalPlaceSource } from "./externalPlaceSource"; export type ResPlaceSearchSource = ExternalPlaceSource | null; diff --git a/solution/frontend/src/api/generated/model/resPublishLogs.ts b/solution/frontend/src/api/generated/model/resPublishLogs.ts index 1d3462f..42694ba 100644 --- a/solution/frontend/src/api/generated/model/resPublishLogs.ts +++ b/solution/frontend/src/api/generated/model/resPublishLogs.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResPublishLogsMsg } from './resPublishLogsMsg'; -import type { PublishLogData } from './publishLogData'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResPublishLogsMsg } from "./resPublishLogsMsg"; +import type { PublishLogData } from "./publishLogData"; export interface ResPublishLogs { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resRefreshToken.ts b/solution/frontend/src/api/generated/model/resRefreshToken.ts index dd82f8d..ec10f0c 100644 --- a/solution/frontend/src/api/generated/model/resRefreshToken.ts +++ b/solution/frontend/src/api/generated/model/resRefreshToken.ts @@ -4,8 +4,8 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResRefreshTokenMsg } from './resRefreshTokenMsg'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResRefreshTokenMsg } from "./resRefreshTokenMsg"; export interface ResRefreshToken { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resSeoAudit.ts b/solution/frontend/src/api/generated/model/resSeoAudit.ts index 9122c3d..9b707bf 100644 --- a/solution/frontend/src/api/generated/model/resSeoAudit.ts +++ b/solution/frontend/src/api/generated/model/resSeoAudit.ts @@ -4,11 +4,11 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResSeoAuditMsg } from './resSeoAuditMsg'; -import type { AuditCheckData } from './auditCheckData'; -import type { ResSeoAuditSummary } from './resSeoAuditSummary'; -import type { ResSeoAuditVisibility } from './resSeoAuditVisibility'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResSeoAuditMsg } from "./resSeoAuditMsg"; +import type { AuditCheckData } from "./auditCheckData"; +import type { ResSeoAuditSummary } from "./resSeoAuditSummary"; +import type { ResSeoAuditVisibility } from "./resSeoAuditVisibility"; export interface ResSeoAudit { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resSeoAuditVisibility.ts b/solution/frontend/src/api/generated/model/resSeoAuditVisibility.ts index 5182aa4..d33aca0 100644 --- a/solution/frontend/src/api/generated/model/resSeoAuditVisibility.ts +++ b/solution/frontend/src/api/generated/model/resSeoAuditVisibility.ts @@ -5,4 +5,4 @@ * OpenAPI spec version: 0.1.0 */ -export type ResSeoAuditVisibility = {[key: string]: number}; +export type ResSeoAuditVisibility = { [key: string]: number }; diff --git a/solution/frontend/src/api/generated/model/resShowcase.ts b/solution/frontend/src/api/generated/model/resShowcase.ts index 7c01a7f..905d626 100644 --- a/solution/frontend/src/api/generated/model/resShowcase.ts +++ b/solution/frontend/src/api/generated/model/resShowcase.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResShowcaseMsg } from './resShowcaseMsg'; -import type { ShowcaseItem } from './showcaseItem'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResShowcaseMsg } from "./resShowcaseMsg"; +import type { ShowcaseItem } from "./showcaseItem"; export interface ResShowcase { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resSite.ts b/solution/frontend/src/api/generated/model/resSite.ts index 1a2a3a2..7ee71c9 100644 --- a/solution/frontend/src/api/generated/model/resSite.ts +++ b/solution/frontend/src/api/generated/model/resSite.ts @@ -4,11 +4,11 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResSiteMsg } from './resSiteMsg'; -import type { ResSiteSite } from './resSiteSite'; -import type { ResSiteCurrentVersion } from './resSiteCurrentVersion'; -import type { RenderStatusData } from './renderStatusData'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResSiteMsg } from "./resSiteMsg"; +import type { ResSiteSite } from "./resSiteSite"; +import type { ResSiteCurrentVersion } from "./resSiteCurrentVersion"; +import type { RenderStatusData } from "./renderStatusData"; export interface ResSite { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resSiteCurrentVersion.ts b/solution/frontend/src/api/generated/model/resSiteCurrentVersion.ts index a4f3a23..970301f 100644 --- a/solution/frontend/src/api/generated/model/resSiteCurrentVersion.ts +++ b/solution/frontend/src/api/generated/model/resSiteCurrentVersion.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { SiteVersionData } from './siteVersionData'; +import type { SiteVersionData } from "./siteVersionData"; export type ResSiteCurrentVersion = SiteVersionData | null; diff --git a/solution/frontend/src/api/generated/model/resSiteSite.ts b/solution/frontend/src/api/generated/model/resSiteSite.ts index 3d2e760..00107aa 100644 --- a/solution/frontend/src/api/generated/model/resSiteSite.ts +++ b/solution/frontend/src/api/generated/model/resSiteSite.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { SiteData } from './siteData'; +import type { SiteData } from "./siteData"; export type ResSiteSite = SiteData | null; diff --git a/solution/frontend/src/api/generated/model/resSiteSlug.ts b/solution/frontend/src/api/generated/model/resSiteSlug.ts index 538e0ca..45464bc 100644 --- a/solution/frontend/src/api/generated/model/resSiteSlug.ts +++ b/solution/frontend/src/api/generated/model/resSiteSlug.ts @@ -4,11 +4,11 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResSiteSlugMsg } from './resSiteSlugMsg'; -import type { ResSiteSlugSite } from './resSiteSlugSite'; -import type { ResSiteSlugReason } from './resSiteSlugReason'; -import type { ResSiteSlugSuggestion } from './resSiteSlugSuggestion'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResSiteSlugMsg } from "./resSiteSlugMsg"; +import type { ResSiteSlugSite } from "./resSiteSlugSite"; +import type { ResSiteSlugReason } from "./resSiteSlugReason"; +import type { ResSiteSlugSuggestion } from "./resSiteSlugSuggestion"; /** * 주소 저장 결과. 거부됐으면 왜/대안을 check 와 같은 코드로 돌려준다. diff --git a/solution/frontend/src/api/generated/model/resSiteSlugSite.ts b/solution/frontend/src/api/generated/model/resSiteSlugSite.ts index b954cfb..192eb96 100644 --- a/solution/frontend/src/api/generated/model/resSiteSlugSite.ts +++ b/solution/frontend/src/api/generated/model/resSiteSlugSite.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { SiteData } from './siteData'; +import type { SiteData } from "./siteData"; export type ResSiteSlugSite = SiteData | null; diff --git a/solution/frontend/src/api/generated/model/resSiteVersions.ts b/solution/frontend/src/api/generated/model/resSiteVersions.ts index d9a7237..192b8ae 100644 --- a/solution/frontend/src/api/generated/model/resSiteVersions.ts +++ b/solution/frontend/src/api/generated/model/resSiteVersions.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResSiteVersionsMsg } from './resSiteVersionsMsg'; -import type { SiteVersionData } from './siteVersionData'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResSiteVersionsMsg } from "./resSiteVersionsMsg"; +import type { SiteVersionData } from "./siteVersionData"; export interface ResSiteVersions { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resSlugCheck.ts b/solution/frontend/src/api/generated/model/resSlugCheck.ts index 6a53f5d..2b8fd5e 100644 --- a/solution/frontend/src/api/generated/model/resSlugCheck.ts +++ b/solution/frontend/src/api/generated/model/resSlugCheck.ts @@ -4,10 +4,10 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResSlugCheckMsg } from './resSlugCheckMsg'; -import type { ResSlugCheckReason } from './resSlugCheckReason'; -import type { ResSlugCheckSuggestion } from './resSlugCheckSuggestion'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResSlugCheckMsg } from "./resSlugCheckMsg"; +import type { ResSlugCheckReason } from "./resSlugCheckReason"; +import type { ResSlugCheckSuggestion } from "./resSlugCheckSuggestion"; /** * 주소 사용 가능 확인. UI 가 타이핑 중에 호출한다. diff --git a/solution/frontend/src/api/generated/model/resStartBuild.ts b/solution/frontend/src/api/generated/model/resStartBuild.ts index ff52d77..77ecd9f 100644 --- a/solution/frontend/src/api/generated/model/resStartBuild.ts +++ b/solution/frontend/src/api/generated/model/resStartBuild.ts @@ -4,10 +4,10 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResStartBuildMsg } from './resStartBuildMsg'; -import type { ResStartBuildJobId } from './resStartBuildJobId'; -import type { ResStartBuildStatus } from './resStartBuildStatus'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResStartBuildMsg } from "./resStartBuildMsg"; +import type { ResStartBuildJobId } from "./resStartBuildJobId"; +import type { ResStartBuildStatus } from "./resStartBuildStatus"; export interface ResStartBuild { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resStartBuildStatus.ts b/solution/frontend/src/api/generated/model/resStartBuildStatus.ts index 30a76ae..23ef785 100644 --- a/solution/frontend/src/api/generated/model/resStartBuildStatus.ts +++ b/solution/frontend/src/api/generated/model/resStartBuildStatus.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { JobStatus } from './jobStatus'; +import type { JobStatus } from "./jobStatus"; export type ResStartBuildStatus = JobStatus | null; diff --git a/solution/frontend/src/api/generated/model/resStartCollect.ts b/solution/frontend/src/api/generated/model/resStartCollect.ts index 9e121bb..099a4cf 100644 --- a/solution/frontend/src/api/generated/model/resStartCollect.ts +++ b/solution/frontend/src/api/generated/model/resStartCollect.ts @@ -4,10 +4,10 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResStartCollectMsg } from './resStartCollectMsg'; -import type { ResStartCollectJobId } from './resStartCollectJobId'; -import type { ResStartCollectStatus } from './resStartCollectStatus'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResStartCollectMsg } from "./resStartCollectMsg"; +import type { ResStartCollectJobId } from "./resStartCollectJobId"; +import type { ResStartCollectStatus } from "./resStartCollectStatus"; /** * 수집 잡 적재 결과. 클라이언트는 job_id 로 GET /v1/job/{job_id} 를 폴링한다. diff --git a/solution/frontend/src/api/generated/model/resStartCollectStatus.ts b/solution/frontend/src/api/generated/model/resStartCollectStatus.ts index 4490db5..29dbb67 100644 --- a/solution/frontend/src/api/generated/model/resStartCollectStatus.ts +++ b/solution/frontend/src/api/generated/model/resStartCollectStatus.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { JobStatus } from './jobStatus'; +import type { JobStatus } from "./jobStatus"; export type ResStartCollectStatus = JobStatus | null; diff --git a/solution/frontend/src/api/generated/model/resStartCopy.ts b/solution/frontend/src/api/generated/model/resStartCopy.ts index 7793a52..bc2bdf7 100644 --- a/solution/frontend/src/api/generated/model/resStartCopy.ts +++ b/solution/frontend/src/api/generated/model/resStartCopy.ts @@ -4,10 +4,10 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResStartCopyMsg } from './resStartCopyMsg'; -import type { ResStartCopyJobId } from './resStartCopyJobId'; -import type { ResStartCopyStatus } from './resStartCopyStatus'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResStartCopyMsg } from "./resStartCopyMsg"; +import type { ResStartCopyJobId } from "./resStartCopyJobId"; +import type { ResStartCopyStatus } from "./resStartCopyStatus"; export interface ResStartCopy { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resStartCopyStatus.ts b/solution/frontend/src/api/generated/model/resStartCopyStatus.ts index 426b2da..baaeda6 100644 --- a/solution/frontend/src/api/generated/model/resStartCopyStatus.ts +++ b/solution/frontend/src/api/generated/model/resStartCopyStatus.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { JobStatus } from './jobStatus'; +import type { JobStatus } from "./jobStatus"; export type ResStartCopyStatus = JobStatus | null; diff --git a/solution/frontend/src/api/generated/model/resStartVision.ts b/solution/frontend/src/api/generated/model/resStartVision.ts index 9eb9fe4..910098f 100644 --- a/solution/frontend/src/api/generated/model/resStartVision.ts +++ b/solution/frontend/src/api/generated/model/resStartVision.ts @@ -4,10 +4,10 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResStartVisionMsg } from './resStartVisionMsg'; -import type { ResStartVisionJobId } from './resStartVisionJobId'; -import type { ResStartVisionStatus } from './resStartVisionStatus'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResStartVisionMsg } from "./resStartVisionMsg"; +import type { ResStartVisionJobId } from "./resStartVisionJobId"; +import type { ResStartVisionStatus } from "./resStartVisionStatus"; export interface ResStartVision { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resStartVisionStatus.ts b/solution/frontend/src/api/generated/model/resStartVisionStatus.ts index 9d63ad8..9a96af9 100644 --- a/solution/frontend/src/api/generated/model/resStartVisionStatus.ts +++ b/solution/frontend/src/api/generated/model/resStartVisionStatus.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { JobStatus } from './jobStatus'; +import type { JobStatus } from "./jobStatus"; export type ResStartVisionStatus = JobStatus | null; diff --git a/solution/frontend/src/api/generated/model/resSyncFestivals.ts b/solution/frontend/src/api/generated/model/resSyncFestivals.ts deleted file mode 100644 index b04f96b..0000000 --- a/solution/frontend/src/api/generated/model/resSyncFestivals.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Generated by orval v7.21.0 🍺 - * Do not edit manually. - * Web4Ai API - * OpenAPI spec version: 0.1.0 - */ -import type { ErrorInfo } from './errorInfo'; -import type { ResSyncFestivalsMsg } from './resSyncFestivalsMsg'; - -export interface ResSyncFestivals { - result?: ErrorInfo; - msg?: ResSyncFestivalsMsg; - collected?: number; - skipped?: number; -} diff --git a/solution/frontend/src/api/generated/model/resSyncPlace.ts b/solution/frontend/src/api/generated/model/resSyncPlace.ts new file mode 100644 index 0000000..ed54f62 --- /dev/null +++ b/solution/frontend/src/api/generated/model/resSyncPlace.ts @@ -0,0 +1,21 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ +import type { ErrorInfo } from "./errorInfo"; +import type { ResSyncPlaceMsg } from "./resSyncPlaceMsg"; + +/** + * 업장 반경 동기화 결과 — 종류별로 **남긴** 건수(반경·기간 필터 뒤). changed 는 값이 바뀌었는지. + */ +export interface ResSyncPlace { + result?: ErrorInfo; + msg?: ResSyncPlaceMsg; + festivals?: number; + attractions?: number; + restaurants?: number; + courses?: number; + changed?: boolean; +} diff --git a/solution/frontend/src/api/generated/model/resSyncPlaceMsg.ts b/solution/frontend/src/api/generated/model/resSyncPlaceMsg.ts new file mode 100644 index 0000000..23d06b4 --- /dev/null +++ b/solution/frontend/src/api/generated/model/resSyncPlaceMsg.ts @@ -0,0 +1,8 @@ +/** + * Generated by orval v7.21.0 🍺 + * Do not edit manually. + * Web4Ai API + * OpenAPI spec version: 0.1.0 + */ + +export type ResSyncPlaceMsg = string | null; diff --git a/solution/frontend/src/api/generated/model/resUnit.ts b/solution/frontend/src/api/generated/model/resUnit.ts index d9af559..5fc26f8 100644 --- a/solution/frontend/src/api/generated/model/resUnit.ts +++ b/solution/frontend/src/api/generated/model/resUnit.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResUnitMsg } from './resUnitMsg'; -import type { ResUnitUnit } from './resUnitUnit'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResUnitMsg } from "./resUnitMsg"; +import type { ResUnitUnit } from "./resUnitUnit"; export interface ResUnit { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resUnitList.ts b/solution/frontend/src/api/generated/model/resUnitList.ts index 0999bdf..e1e9af2 100644 --- a/solution/frontend/src/api/generated/model/resUnitList.ts +++ b/solution/frontend/src/api/generated/model/resUnitList.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResUnitListMsg } from './resUnitListMsg'; -import type { UnitData } from './unitData'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResUnitListMsg } from "./resUnitListMsg"; +import type { UnitData } from "./unitData"; export interface ResUnitList { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resUnitUnit.ts b/solution/frontend/src/api/generated/model/resUnitUnit.ts index e7b3068..921a6dd 100644 --- a/solution/frontend/src/api/generated/model/resUnitUnit.ts +++ b/solution/frontend/src/api/generated/model/resUnitUnit.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { UnitData } from './unitData'; +import type { UnitData } from "./unitData"; export type ResUnitUnit = UnitData | null; diff --git a/solution/frontend/src/api/generated/model/resVerifyCandidates.ts b/solution/frontend/src/api/generated/model/resVerifyCandidates.ts index 33889e8..4d61d3c 100644 --- a/solution/frontend/src/api/generated/model/resVerifyCandidates.ts +++ b/solution/frontend/src/api/generated/model/resVerifyCandidates.ts @@ -4,11 +4,11 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResVerifyCandidatesMsg } from './resVerifyCandidatesMsg'; -import type { ResVerifyCandidatesSource } from './resVerifyCandidatesSource'; -import type { ResVerifyCandidatesReason } from './resVerifyCandidatesReason'; -import type { PlaceCandidate } from './placeCandidate'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResVerifyCandidatesMsg } from "./resVerifyCandidatesMsg"; +import type { ResVerifyCandidatesSource } from "./resVerifyCandidatesSource"; +import type { ResVerifyCandidatesReason } from "./resVerifyCandidatesReason"; +import type { PlaceCandidate } from "./placeCandidate"; /** * 동일 업소 후보 목록. diff --git a/solution/frontend/src/api/generated/model/resVerifyCandidatesSource.ts b/solution/frontend/src/api/generated/model/resVerifyCandidatesSource.ts index 7e113df..d8f25c4 100644 --- a/solution/frontend/src/api/generated/model/resVerifyCandidatesSource.ts +++ b/solution/frontend/src/api/generated/model/resVerifyCandidatesSource.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ExternalPlaceSource } from './externalPlaceSource'; +import type { ExternalPlaceSource } from "./externalPlaceSource"; export type ResVerifyCandidatesSource = ExternalPlaceSource | null; diff --git a/solution/frontend/src/api/generated/model/resWeather.ts b/solution/frontend/src/api/generated/model/resWeather.ts index 4b4719c..ee118ec 100644 --- a/solution/frontend/src/api/generated/model/resWeather.ts +++ b/solution/frontend/src/api/generated/model/resWeather.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ErrorInfo } from './errorInfo'; -import type { ResWeatherMsg } from './resWeatherMsg'; -import type { ResWeatherWeather } from './resWeatherWeather'; +import type { ErrorInfo } from "./errorInfo"; +import type { ResWeatherMsg } from "./resWeatherMsg"; +import type { ResWeatherWeather } from "./resWeatherWeather"; export interface ResWeather { result?: ErrorInfo; diff --git a/solution/frontend/src/api/generated/model/resWeatherWeather.ts b/solution/frontend/src/api/generated/model/resWeatherWeather.ts index 48d8de8..220e418 100644 --- a/solution/frontend/src/api/generated/model/resWeatherWeather.ts +++ b/solution/frontend/src/api/generated/model/resWeatherWeather.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { WeatherData } from './weatherData'; +import type { WeatherData } from "./weatherData"; export type ResWeatherWeather = WeatherData | null; diff --git a/solution/frontend/src/api/generated/model/searchPlacesPublicParams.ts b/solution/frontend/src/api/generated/model/searchPlacesPublicParams.ts index 8d965fd..281bffd 100644 --- a/solution/frontend/src/api/generated/model/searchPlacesPublicParams.ts +++ b/solution/frontend/src/api/generated/model/searchPlacesPublicParams.ts @@ -6,10 +6,10 @@ */ export type SearchPlacesPublicParams = { -/** - * 상호명 - * @minLength 2 - * @maxLength 100 - */ -q: string; + /** + * 상호명 + * @minLength 2 + * @maxLength 100 + */ + q: string; }; diff --git a/solution/frontend/src/api/generated/model/showcaseItem.ts b/solution/frontend/src/api/generated/model/showcaseItem.ts index b6374d3..7aa84ec 100644 --- a/solution/frontend/src/api/generated/model/showcaseItem.ts +++ b/solution/frontend/src/api/generated/model/showcaseItem.ts @@ -4,9 +4,9 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { PlaceCategory } from './placeCategory'; -import type { ShowcaseItemRegion } from './showcaseItemRegion'; -import type { ShowcaseItemThumbnailUrl } from './showcaseItemThumbnailUrl'; +import type { PlaceCategory } from "./placeCategory"; +import type { ShowcaseItemRegion } from "./showcaseItemRegion"; +import type { ShowcaseItemThumbnailUrl } from "./showcaseItemThumbnailUrl"; /** * 랜딩 쇼케이스 카드 한 장. **로그인 없이 나가는 값이다.** diff --git a/solution/frontend/src/api/generated/model/siteData.ts b/solution/frontend/src/api/generated/model/siteData.ts index f48299d..cacc8ba 100644 --- a/solution/frontend/src/api/generated/model/siteData.ts +++ b/solution/frontend/src/api/generated/model/siteData.ts @@ -4,13 +4,13 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { SiteStatus } from './siteStatus'; -import type { SiteDataDomain } from './siteDataDomain'; -import type { SiteDataTemplateId } from './siteDataTemplateId'; -import type { SiteDataTheme } from './siteDataTheme'; -import type { SiteDataCurrentVersionId } from './siteDataCurrentVersionId'; -import type { SiteDataPublishedAt } from './siteDataPublishedAt'; -import type { SiteDataThumbnailUrl } from './siteDataThumbnailUrl'; +import type { SiteStatus } from "./siteStatus"; +import type { SiteDataDomain } from "./siteDataDomain"; +import type { SiteDataTemplateId } from "./siteDataTemplateId"; +import type { SiteDataTheme } from "./siteDataTheme"; +import type { SiteDataCurrentVersionId } from "./siteDataCurrentVersionId"; +import type { SiteDataPublishedAt } from "./siteDataPublishedAt"; +import type { SiteDataThumbnailUrl } from "./siteDataThumbnailUrl"; export interface SiteData { site_id: string; diff --git a/solution/frontend/src/api/generated/model/siteDataTheme.ts b/solution/frontend/src/api/generated/model/siteDataTheme.ts index a06d868..f0aa229 100644 --- a/solution/frontend/src/api/generated/model/siteDataTheme.ts +++ b/solution/frontend/src/api/generated/model/siteDataTheme.ts @@ -4,6 +4,6 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { SiteDataThemeAnyOf } from './siteDataThemeAnyOf'; +import type { SiteDataThemeAnyOf } from "./siteDataThemeAnyOf"; export type SiteDataTheme = SiteDataThemeAnyOf | null; diff --git a/solution/frontend/src/api/generated/model/siteStatus.ts b/solution/frontend/src/api/generated/model/siteStatus.ts index 9716066..e86124e 100644 --- a/solution/frontend/src/api/generated/model/siteStatus.ts +++ b/solution/frontend/src/api/generated/model/siteStatus.ts @@ -8,8 +8,7 @@ /** * sites.status 코드값. ★ 해지는 물리 삭제가 아니라 상태 전이로만 처리한다. */ -export type SiteStatus = typeof SiteStatus[keyof typeof SiteStatus]; - +export type SiteStatus = (typeof SiteStatus)[keyof typeof SiteStatus]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const SiteStatus = { diff --git a/solution/frontend/src/api/generated/model/siteVersionData.ts b/solution/frontend/src/api/generated/model/siteVersionData.ts index 99a1757..e8c119f 100644 --- a/solution/frontend/src/api/generated/model/siteVersionData.ts +++ b/solution/frontend/src/api/generated/model/siteVersionData.ts @@ -4,10 +4,10 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { BuildStatus } from './buildStatus'; -import type { SiteVersionDataBuildError } from './siteVersionDataBuildError'; -import type { SiteVersionDataBuiltAt } from './siteVersionDataBuiltAt'; -import type { SiteVersionDataCreatedAt } from './siteVersionDataCreatedAt'; +import type { BuildStatus } from "./buildStatus"; +import type { SiteVersionDataBuildError } from "./siteVersionDataBuildError"; +import type { SiteVersionDataBuiltAt } from "./siteVersionDataBuiltAt"; +import type { SiteVersionDataCreatedAt } from "./siteVersionDataCreatedAt"; export interface SiteVersionData { site_version_id: string; diff --git a/solution/frontend/src/api/generated/model/sourceType.ts b/solution/frontend/src/api/generated/model/sourceType.ts index 3098e1d..7d549c9 100644 --- a/solution/frontend/src/api/generated/model/sourceType.ts +++ b/solution/frontend/src/api/generated/model/sourceType.ts @@ -9,8 +9,7 @@ * facts.source_type / media.source_type / place_aliases.source_type / place_faqs.generated_by 공용 코드값. 값이 어디서 왔는지 — 모든 사실은 출처를 갖는다. */ -export type SourceType = typeof SourceType[keyof typeof SourceType]; - +export type SourceType = (typeof SourceType)[keyof typeof SourceType]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const SourceType = { diff --git a/solution/frontend/src/api/generated/model/userRole.ts b/solution/frontend/src/api/generated/model/userRole.ts index b1500c0..83aaea5 100644 --- a/solution/frontend/src/api/generated/model/userRole.ts +++ b/solution/frontend/src/api/generated/model/userRole.ts @@ -10,8 +10,7 @@ 1=일반, 2=최고관리자(고객사 최상위), 3=개발자(우리 내부 운영 계정). 개발자 계정은 고객사에 존재를 노출하지 않는다 — 회원 목록에서 빼고 총계에도 넣지 않는다. */ -export type UserRole = typeof UserRole[keyof typeof UserRole]; - +export type UserRole = (typeof UserRole)[keyof typeof UserRole]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const UserRole = { diff --git a/solution/frontend/src/api/generated/model/validationError.ts b/solution/frontend/src/api/generated/model/validationError.ts index bfd016e..3354369 100644 --- a/solution/frontend/src/api/generated/model/validationError.ts +++ b/solution/frontend/src/api/generated/model/validationError.ts @@ -4,8 +4,8 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { ValidationErrorLocItem } from './validationErrorLocItem'; -import type { ValidationErrorCtx } from './validationErrorCtx'; +import type { ValidationErrorLocItem } from "./validationErrorLocItem"; +import type { ValidationErrorCtx } from "./validationErrorCtx"; export interface ValidationError { loc: ValidationErrorLocItem[]; diff --git a/solution/frontend/src/api/generated/model/verifyCandidatesParams.ts b/solution/frontend/src/api/generated/model/verifyCandidatesParams.ts index 52dce97..f249d2b 100644 --- a/solution/frontend/src/api/generated/model/verifyCandidatesParams.ts +++ b/solution/frontend/src/api/generated/model/verifyCandidatesParams.ts @@ -6,8 +6,8 @@ */ export type VerifyCandidatesParams = { -/** - * 검색어. 비우면 등록된 상호명 - */ -query?: string | null; + /** + * 검색어. 비우면 등록된 상호명 + */ + query?: string | null; }; diff --git a/solution/frontend/src/api/generated/model/weatherData.ts b/solution/frontend/src/api/generated/model/weatherData.ts index 7660333..b0fe0b0 100644 --- a/solution/frontend/src/api/generated/model/weatherData.ts +++ b/solution/frontend/src/api/generated/model/weatherData.ts @@ -4,8 +4,8 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import type { WeatherDataWindSpeed } from './weatherDataWindSpeed'; -import type { WeatherDataTimezone } from './weatherDataTimezone'; +import type { WeatherDataWindSpeed } from "./weatherDataWindSpeed"; +import type { WeatherDataTimezone } from "./weatherDataTimezone"; export interface WeatherData { temperature: number; diff --git a/solution/frontend/src/api/generated/place/place.ts b/solution/frontend/src/api/generated/place/place.ts index b0e67d7..95ef86b 100644 --- a/solution/frontend/src/api/generated/place/place.ts +++ b/solution/frontend/src/api/generated/place/place.ts @@ -4,10 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import { - useMutation, - useQuery -} from '@tanstack/react-query'; +import { useMutation, useQuery } from "@tanstack/react-query"; import type { DataTag, DefinedInitialDataOptions, @@ -20,8 +17,8 @@ import type { UseMutationOptions, UseMutationResult, UseQueryOptions, - UseQueryResult -} from '@tanstack/react-query'; + UseQueryResult, +} from "@tanstack/react-query"; import type { HTTPValidationError, @@ -49,1394 +46,2132 @@ import type { ResUnitList, ResVerifyCandidates, SearchPlacesPublicParams, - VerifyCandidatesParams -} from '.././model'; - -import { customFetch } from '../../mutator/custom-fetch'; + VerifyCandidatesParams, +} from ".././model"; +import { customFetch } from "../../mutator/custom-fetch"; type SecondParameter unknown> = Parameters[1]; - - /** * @summary 사업장 목록 */ export const listPlaces = ( - params?: ListPlacesParams, - options?: SecondParameter,signal?: AbortSignal + params?: ListPlacesParams, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/list`, method: 'GET', - params, signal - }, - options); - } - + return customFetch( + { url: `/v1/place/list`, method: "GET", params, signal }, + options, + ); +}; +export const getListPlacesQueryKey = (params?: ListPlacesParams) => { + return [`/v1/place/list`, ...(params ? [params] : [])] as const; +}; - -export const getListPlacesQueryKey = (params?: ListPlacesParams,) => { - return [ - `/v1/place/list`, ...(params ? [params]: []) - ] as const; - } - - -export const getListPlacesQueryOptions = >, TError = void | HTTPValidationError>(params?: ListPlacesParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getListPlacesQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params?: ListPlacesParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, ) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getListPlacesQueryKey(params); - const queryKey = queryOptions?.queryKey ?? getListPlacesQueryKey(params); + const queryFn: QueryFunction>> = ({ + signal, + }) => listPlaces(params, requestOptions, signal); - + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; - const queryFn: QueryFunction>> = ({ signal }) => listPlaces(params, requestOptions, signal); +export type ListPlacesQueryResult = NonNullable< + Awaited> +>; +export type ListPlacesQueryError = void | HTTPValidationError; - - - - - return { queryKey, queryFn, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type ListPlacesQueryResult = NonNullable>> -export type ListPlacesQueryError = void | HTTPValidationError - - -export function useListPlaces>, TError = void | HTTPValidationError>( - params: undefined | ListPlacesParams, options: { query:Partial>, TError, TData>> & Pick< +export function useListPlaces< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params: undefined | ListPlacesParams, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useListPlaces>, TError = void | HTTPValidationError>( - params?: ListPlacesParams, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useListPlaces< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params?: ListPlacesParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useListPlaces>, TError = void | HTTPValidationError>( - params?: ListPlacesParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useListPlaces< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params?: ListPlacesParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 사업장 목록 */ -export function useListPlaces>, TError = void | HTTPValidationError>( - params?: ListPlacesParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useListPlaces< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params?: ListPlacesParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getListPlacesQueryOptions(params, options); - const queryOptions = getListPlacesQueryOptions(params,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * 상호명으로 외부 장소 DB(카카오 → 없으면 네이버)를 찾아 후보를 그대로 돌려준다. ★ 인증이 없다 — 랜딩 첫 화면이 부른다. 사업장을 만들지도, 우리 DB 를 읽지도 않는다. ★ 응답의 category 는 외부 분류에서 **추정한 기본값**이다. None 이면 못 정한 것이고, 값이 있어도 확정이 아니다 — 화면은 언제나 바꿀 수 있게 둔다. * @summary 상호명 공개 검색 */ export const searchPlacesPublic = ( - params: SearchPlacesPublicParams, - options?: SecondParameter,signal?: AbortSignal + params: SearchPlacesPublicParams, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/search`, method: 'GET', - params, signal - }, - options); - } - + return customFetch( + { url: `/v1/place/search`, method: "GET", params, signal }, + options, + ); +}; - - -export const getSearchPlacesPublicQueryKey = (params?: SearchPlacesPublicParams,) => { - return [ - `/v1/place/search`, ...(params ? [params]: []) - ] as const; - } - - -export const getSearchPlacesPublicQueryOptions = >, TError = void | HTTPValidationError>(params: SearchPlacesPublicParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getSearchPlacesPublicQueryKey = ( + params?: SearchPlacesPublicParams, ) => { + return [`/v1/place/search`, ...(params ? [params] : [])] as const; +}; -const {query: queryOptions, request: requestOptions} = options ?? {}; +export const getSearchPlacesPublicQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params: SearchPlacesPublicParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; - const queryKey = queryOptions?.queryKey ?? getSearchPlacesPublicQueryKey(params); + const queryKey = + queryOptions?.queryKey ?? getSearchPlacesPublicQueryKey(params); - + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => searchPlacesPublic(params, requestOptions, signal); - const queryFn: QueryFunction>> = ({ signal }) => searchPlacesPublic(params, requestOptions, signal); + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; - +export type SearchPlacesPublicQueryResult = NonNullable< + Awaited> +>; +export type SearchPlacesPublicQueryError = void | HTTPValidationError; - - - return { queryKey, queryFn, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type SearchPlacesPublicQueryResult = NonNullable>> -export type SearchPlacesPublicQueryError = void | HTTPValidationError - - -export function useSearchPlacesPublic>, TError = void | HTTPValidationError>( - params: SearchPlacesPublicParams, options: { query:Partial>, TError, TData>> & Pick< +export function useSearchPlacesPublic< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params: SearchPlacesPublicParams, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useSearchPlacesPublic>, TError = void | HTTPValidationError>( - params: SearchPlacesPublicParams, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useSearchPlacesPublic< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params: SearchPlacesPublicParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useSearchPlacesPublic>, TError = void | HTTPValidationError>( - params: SearchPlacesPublicParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useSearchPlacesPublic< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params: SearchPlacesPublicParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 상호명 공개 검색 */ -export function useSearchPlacesPublic>, TError = void | HTTPValidationError>( - params: SearchPlacesPublicParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useSearchPlacesPublic< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params: SearchPlacesPublicParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getSearchPlacesPublicQueryOptions(params, options); - const queryOptions = getSearchPlacesPublicQueryOptions(params,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * 상호명 하나로 시작한다. 주소·좌표는 동일 업소 검증(verify)이 채운다. * @summary 사업장 등록 */ export const createPlace = ( - reqCreatePlace: ReqCreatePlace, - options?: SecondParameter,signal?: AbortSignal + reqCreatePlace: ReqCreatePlace, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqCreatePlace, signal + return customFetch( + { + url: `/v1/place`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqCreatePlace, + signal, }, - options); - } - + options, + ); +}; +export const getCreatePlaceMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ReqCreatePlace }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: ReqCreatePlace }, + TContext +> => { + const mutationKey = ["createPlace"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getCreatePlaceMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqCreatePlace}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{data: ReqCreatePlace}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { data: ReqCreatePlace } + > = (props) => { + const { data } = props ?? {}; -const mutationKey = ['createPlace']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return createPlace(data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type CreatePlaceMutationResult = NonNullable< + Awaited> +>; +export type CreatePlaceMutationBody = ReqCreatePlace; +export type CreatePlaceMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {data: ReqCreatePlace}> = (props) => { - const {data} = props ?? {}; - - return createPlace(data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type CreatePlaceMutationResult = NonNullable>> - export type CreatePlaceMutationBody = ReqCreatePlace - export type CreatePlaceMutationError = void | HTTPValidationError - - /** +/** * @summary 사업장 등록 */ -export const useCreatePlace = (options?: { mutation?:UseMutationOptions>, TError,{data: ReqCreatePlace}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {data: ReqCreatePlace}, - TContext - > => { +export const useCreatePlace = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ReqCreatePlace }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: ReqCreatePlace }, + TContext +> => { + const mutationOptions = getCreatePlaceMutationOptions(options); - const mutationOptions = getCreatePlaceMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * @summary 사업장 단건 */ export const getPlace = ( - placeId: string, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}`, method: 'GET', signal - }, - options); - } - + return customFetch( + { url: `/v1/place/${placeId}`, method: "GET", signal }, + options, + ); +}; +export const getGetPlaceQueryKey = (placeId?: string) => { + return [`/v1/place/${placeId}`] as const; +}; - -export const getGetPlaceQueryKey = (placeId?: string,) => { - return [ - `/v1/place/${placeId}` - ] as const; - } - - -export const getGetPlaceQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getGetPlaceQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, ) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getGetPlaceQueryKey(placeId); - const queryKey = queryOptions?.queryKey ?? getGetPlaceQueryKey(placeId); + const queryFn: QueryFunction>> = ({ + signal, + }) => getPlace(placeId, requestOptions, signal); - + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: DataTag; + }; +}; - const queryFn: QueryFunction>> = ({ signal }) => getPlace(placeId, requestOptions, signal); +export type GetPlaceQueryResult = NonNullable< + Awaited> +>; +export type GetPlaceQueryError = void | HTTPValidationError; - - - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type GetPlaceQueryResult = NonNullable>> -export type GetPlaceQueryError = void | HTTPValidationError - - -export function useGetPlace>, TError = void | HTTPValidationError>( - placeId: string, options: { query:Partial>, TError, TData>> & Pick< +export function useGetPlace< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useGetPlace>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetPlace< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useGetPlace>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetPlace< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 사업장 단건 */ -export function useGetPlace>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useGetPlace< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetPlaceQueryOptions(placeId, options); - const queryOptions = getGetPlaceQueryOptions(placeId,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * @summary 사업장 수정 */ export const updatePlace = ( - placeId: string, - reqUpdatePlace: ReqUpdatePlace, - options?: SecondParameter,) => { - - - return customFetch( - {url: `/v1/place/${placeId}`, method: 'PATCH', - headers: {'Content-Type': 'application/json', }, - data: reqUpdatePlace + placeId: string, + reqUpdatePlace: ReqUpdatePlace, + options?: SecondParameter, +) => { + return customFetch( + { + url: `/v1/place/${placeId}`, + method: "PATCH", + headers: { "Content-Type": "application/json" }, + data: reqUpdatePlace, }, - options); - } - + options, + ); +}; +export const getUpdatePlaceMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqUpdatePlace }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqUpdatePlace }, + TContext +> => { + const mutationKey = ["updatePlace"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getUpdatePlaceMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqUpdatePlace}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqUpdatePlace}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqUpdatePlace } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['updatePlace']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return updatePlace(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type UpdatePlaceMutationResult = NonNullable< + Awaited> +>; +export type UpdatePlaceMutationBody = ReqUpdatePlace; +export type UpdatePlaceMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqUpdatePlace}> = (props) => { - const {placeId,data} = props ?? {}; - - return updatePlace(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type UpdatePlaceMutationResult = NonNullable>> - export type UpdatePlaceMutationBody = ReqUpdatePlace - export type UpdatePlaceMutationError = void | HTTPValidationError - - /** +/** * @summary 사업장 수정 */ -export const useUpdatePlace = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqUpdatePlace}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqUpdatePlace}, - TContext - > => { +export const useUpdatePlace = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqUpdatePlace }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqUpdatePlace }, + TContext +> => { + const mutationOptions = getUpdatePlaceMutationOptions(options); - const mutationOptions = getUpdatePlaceMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * @summary 사업장 삭제 */ export const deletePlace = ( - placeId: string, - options?: SecondParameter,) => { - - - return customFetch( - {url: `/v1/place/${placeId}`, method: 'DELETE' - }, - options); - } - + placeId: string, + options?: SecondParameter, +) => { + return customFetch( + { url: `/v1/place/${placeId}`, method: "DELETE" }, + options, + ); +}; +export const getDeletePlaceMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string }, + TContext +> => { + const mutationKey = ["deletePlace"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getDeletePlaceMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string } + > = (props) => { + const { placeId } = props ?? {}; -const mutationKey = ['deletePlace']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return deletePlace(placeId, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type DeletePlaceMutationResult = NonNullable< + Awaited> +>; - const mutationFn: MutationFunction>, {placeId: string}> = (props) => { - const {placeId} = props ?? {}; +export type DeletePlaceMutationError = void | HTTPValidationError; - return deletePlace(placeId,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type DeletePlaceMutationResult = NonNullable>> - - export type DeletePlaceMutationError = void | HTTPValidationError - - /** +/** * @summary 사업장 삭제 */ -export const useDeletePlace = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string}, - TContext - > => { +export const useDeletePlace = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string }, + TContext +> => { + const mutationOptions = getDeletePlaceMutationOptions(options); - const mutationOptions = getDeletePlaceMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * @summary 진행 중인 수집 잡 */ export const getActiveCollect = ( - placeId: string, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/collect/active`, method: 'GET', signal - }, - options); - } - + return customFetch( + { url: `/v1/place/${placeId}/collect/active`, method: "GET", signal }, + options, + ); +}; +export const getGetActiveCollectQueryKey = (placeId?: string) => { + return [`/v1/place/${placeId}/collect/active`] as const; +}; - -export const getGetActiveCollectQueryKey = (placeId?: string,) => { - return [ - `/v1/place/${placeId}/collect/active` - ] as const; - } - - -export const getGetActiveCollectQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getGetActiveCollectQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, ) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = + queryOptions?.queryKey ?? getGetActiveCollectQueryKey(placeId); - const queryKey = queryOptions?.queryKey ?? getGetActiveCollectQueryKey(placeId); + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getActiveCollect(placeId, requestOptions, signal); - + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; - const queryFn: QueryFunction>> = ({ signal }) => getActiveCollect(placeId, requestOptions, signal); +export type GetActiveCollectQueryResult = NonNullable< + Awaited> +>; +export type GetActiveCollectQueryError = void | HTTPValidationError; - - - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type GetActiveCollectQueryResult = NonNullable>> -export type GetActiveCollectQueryError = void | HTTPValidationError - - -export function useGetActiveCollect>, TError = void | HTTPValidationError>( - placeId: string, options: { query:Partial>, TError, TData>> & Pick< +export function useGetActiveCollect< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useGetActiveCollect>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetActiveCollect< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useGetActiveCollect>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetActiveCollect< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 진행 중인 수집 잡 */ -export function useGetActiveCollect>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useGetActiveCollect< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetActiveCollectQueryOptions(placeId, options); - const queryOptions = getGetActiveCollectQueryOptions(placeId,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * 카카오 로컬 조회 결과를 박제해 동일 업소를 확정한다. ★ 이걸 통과해야 수집이 열린다. * @summary 동일 업소 검증 */ export const verifyPlace = ( - placeId: string, - reqVerifyPlace: ReqVerifyPlace, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqVerifyPlace: ReqVerifyPlace, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/verify`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqVerifyPlace, signal + return customFetch( + { + url: `/v1/place/${placeId}/verify`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqVerifyPlace, + signal, }, - options); - } - + options, + ); +}; +export const getVerifyPlaceMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqVerifyPlace }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqVerifyPlace }, + TContext +> => { + const mutationKey = ["verifyPlace"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getVerifyPlaceMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqVerifyPlace}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqVerifyPlace}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqVerifyPlace } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['verifyPlace']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return verifyPlace(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type VerifyPlaceMutationResult = NonNullable< + Awaited> +>; +export type VerifyPlaceMutationBody = ReqVerifyPlace; +export type VerifyPlaceMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqVerifyPlace}> = (props) => { - const {placeId,data} = props ?? {}; - - return verifyPlace(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type VerifyPlaceMutationResult = NonNullable>> - export type VerifyPlaceMutationBody = ReqVerifyPlace - export type VerifyPlaceMutationError = void | HTTPValidationError - - /** +/** * @summary 동일 업소 검증 */ -export const useVerifyPlace = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqVerifyPlace}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqVerifyPlace}, - TContext - > => { +export const useVerifyPlace = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqVerifyPlace }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqVerifyPlace }, + TContext +> => { + const mutationOptions = getVerifyPlaceMutationOptions(options); - const mutationOptions = getVerifyPlaceMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * 사장님이 붙여넣은 네이버 플레이스 주소로 상호·주소·좌표를 읽어 확정하고, 그 URL 을 수집 채널로 등록·확정한다. ★ 상호 검색이 실패하는 가게(동명·지점명 표기 차이)를 위한 확실한 경로다. * @summary 네이버 플레이스 URL 로 동일 업소 확정 */ export const verifyPlaceByUrl = ( - placeId: string, - reqVerifyPlaceByUrl: ReqVerifyPlaceByUrl, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqVerifyPlaceByUrl: ReqVerifyPlaceByUrl, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/verify/by-url`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqVerifyPlaceByUrl, signal + return customFetch( + { + url: `/v1/place/${placeId}/verify/by-url`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqVerifyPlaceByUrl, + signal, }, - options); - } - + options, + ); +}; +export const getVerifyPlaceByUrlMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqVerifyPlaceByUrl }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqVerifyPlaceByUrl }, + TContext +> => { + const mutationKey = ["verifyPlaceByUrl"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getVerifyPlaceByUrlMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqVerifyPlaceByUrl}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqVerifyPlaceByUrl}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqVerifyPlaceByUrl } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['verifyPlaceByUrl']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return verifyPlaceByUrl(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type VerifyPlaceByUrlMutationResult = NonNullable< + Awaited> +>; +export type VerifyPlaceByUrlMutationBody = ReqVerifyPlaceByUrl; +export type VerifyPlaceByUrlMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqVerifyPlaceByUrl}> = (props) => { - const {placeId,data} = props ?? {}; - - return verifyPlaceByUrl(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type VerifyPlaceByUrlMutationResult = NonNullable>> - export type VerifyPlaceByUrlMutationBody = ReqVerifyPlaceByUrl - export type VerifyPlaceByUrlMutationError = void | HTTPValidationError - - /** +/** * @summary 네이버 플레이스 URL 로 동일 업소 확정 */ -export const useVerifyPlaceByUrl = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqVerifyPlaceByUrl}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqVerifyPlaceByUrl}, - TContext - > => { +export const useVerifyPlaceByUrl = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqVerifyPlaceByUrl }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqVerifyPlaceByUrl }, + TContext +> => { + const mutationOptions = getVerifyPlaceByUrlMutationOptions(options); - const mutationOptions = getVerifyPlaceByUrlMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * @summary 하위 단위 목록(객실·메뉴·프로그램) */ export const listUnits = ( - placeId: string, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/unit/list`, method: 'GET', signal - }, - options); - } - + return customFetch( + { url: `/v1/place/${placeId}/unit/list`, method: "GET", signal }, + options, + ); +}; +export const getListUnitsQueryKey = (placeId?: string) => { + return [`/v1/place/${placeId}/unit/list`] as const; +}; - -export const getListUnitsQueryKey = (placeId?: string,) => { - return [ - `/v1/place/${placeId}/unit/list` - ] as const; - } - - -export const getListUnitsQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getListUnitsQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, ) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getListUnitsQueryKey(placeId); - const queryKey = queryOptions?.queryKey ?? getListUnitsQueryKey(placeId); + const queryFn: QueryFunction>> = ({ + signal, + }) => listUnits(placeId, requestOptions, signal); - + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: DataTag; + }; +}; - const queryFn: QueryFunction>> = ({ signal }) => listUnits(placeId, requestOptions, signal); +export type ListUnitsQueryResult = NonNullable< + Awaited> +>; +export type ListUnitsQueryError = void | HTTPValidationError; - - - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type ListUnitsQueryResult = NonNullable>> -export type ListUnitsQueryError = void | HTTPValidationError - - -export function useListUnits>, TError = void | HTTPValidationError>( - placeId: string, options: { query:Partial>, TError, TData>> & Pick< +export function useListUnits< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useListUnits>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useListUnits< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useListUnits>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useListUnits< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 하위 단위 목록(객실·메뉴·프로그램) */ -export function useListUnits>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useListUnits< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getListUnitsQueryOptions(placeId, options); - const queryOptions = getListUnitsQueryOptions(placeId,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * @summary 하위 단위 등록 */ export const createUnit = ( - placeId: string, - reqCreateUnit: ReqCreateUnit, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqCreateUnit: ReqCreateUnit, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/unit`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqCreateUnit, signal + return customFetch( + { + url: `/v1/place/${placeId}/unit`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqCreateUnit, + signal, }, - options); - } - + options, + ); +}; +export const getCreateUnitMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqCreateUnit }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqCreateUnit }, + TContext +> => { + const mutationKey = ["createUnit"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getCreateUnitMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqCreateUnit}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqCreateUnit}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqCreateUnit } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['createUnit']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return createUnit(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type CreateUnitMutationResult = NonNullable< + Awaited> +>; +export type CreateUnitMutationBody = ReqCreateUnit; +export type CreateUnitMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqCreateUnit}> = (props) => { - const {placeId,data} = props ?? {}; - - return createUnit(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type CreateUnitMutationResult = NonNullable>> - export type CreateUnitMutationBody = ReqCreateUnit - export type CreateUnitMutationError = void | HTTPValidationError - - /** +/** * @summary 하위 단위 등록 */ -export const useCreateUnit = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqCreateUnit}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqCreateUnit}, - TContext - > => { +export const useCreateUnit = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqCreateUnit }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqCreateUnit }, + TContext +> => { + const mutationOptions = getCreateUnitMutationOptions(options); - const mutationOptions = getCreateUnitMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * confirmed_only=true 면 크롤링 대상(확정된 URL)만. * @summary 채널 URL 목록 */ export const listLinks = ( - placeId: string, - params?: ListLinksParams, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + params?: ListLinksParams, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/link/list`, method: 'GET', - params, signal - }, - options); - } - + return customFetch( + { url: `/v1/place/${placeId}/link/list`, method: "GET", params, signal }, + options, + ); +}; - - -export const getListLinksQueryKey = (placeId?: string, - params?: ListLinksParams,) => { - return [ - `/v1/place/${placeId}/link/list`, ...(params ? [params]: []) - ] as const; - } - - -export const getListLinksQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, - params?: ListLinksParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getListLinksQueryKey = ( + placeId?: string, + params?: ListLinksParams, ) => { + return [ + `/v1/place/${placeId}/link/list`, + ...(params ? [params] : []), + ] as const; +}; -const {query: queryOptions, request: requestOptions} = options ?? {}; +export const getListLinksQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListLinksParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; - const queryKey = queryOptions?.queryKey ?? getListLinksQueryKey(placeId,params); + const queryKey = + queryOptions?.queryKey ?? getListLinksQueryKey(placeId, params); - + const queryFn: QueryFunction>> = ({ + signal, + }) => listLinks(placeId, params, requestOptions, signal); - const queryFn: QueryFunction>> = ({ signal }) => listLinks(placeId,params, requestOptions, signal); + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: DataTag; + }; +}; - +export type ListLinksQueryResult = NonNullable< + Awaited> +>; +export type ListLinksQueryError = void | HTTPValidationError; - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type ListLinksQueryResult = NonNullable>> -export type ListLinksQueryError = void | HTTPValidationError - - -export function useListLinks>, TError = void | HTTPValidationError>( - placeId: string, - params: undefined | ListLinksParams, options: { query:Partial>, TError, TData>> & Pick< +export function useListLinks< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params: undefined | ListLinksParams, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useListLinks>, TError = void | HTTPValidationError>( - placeId: string, - params?: ListLinksParams, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useListLinks< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListLinksParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useListLinks>, TError = void | HTTPValidationError>( - placeId: string, - params?: ListLinksParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useListLinks< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListLinksParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 채널 URL 목록 */ -export function useListLinks>, TError = void | HTTPValidationError>( - placeId: string, - params?: ListLinksParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useListLinks< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: ListLinksParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getListLinksQueryOptions(placeId, params, options); - const queryOptions = getListLinksQueryOptions(placeId,params,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * 등록만으로는 크롤링 대상이 되지 않는다 — 확정(confirm)이 따로 필요하다. * @summary 채널 URL 등록 */ export const createLink = ( - placeId: string, - reqCreateLink: ReqCreateLink, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqCreateLink: ReqCreateLink, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/link`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqCreateLink, signal + return customFetch( + { + url: `/v1/place/${placeId}/link`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqCreateLink, + signal, }, - options); - } - + options, + ); +}; +export const getCreateLinkMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqCreateLink }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqCreateLink }, + TContext +> => { + const mutationKey = ["createLink"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getCreateLinkMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqCreateLink}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqCreateLink}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqCreateLink } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['createLink']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return createLink(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type CreateLinkMutationResult = NonNullable< + Awaited> +>; +export type CreateLinkMutationBody = ReqCreateLink; +export type CreateLinkMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqCreateLink}> = (props) => { - const {placeId,data} = props ?? {}; - - return createLink(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type CreateLinkMutationResult = NonNullable>> - export type CreateLinkMutationBody = ReqCreateLink - export type CreateLinkMutationError = void | HTTPValidationError - - /** +/** * @summary 채널 URL 등록 */ -export const useCreateLink = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqCreateLink}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqCreateLink}, - TContext - > => { +export const useCreateLink = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqCreateLink }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqCreateLink }, + TContext +> => { + const mutationOptions = getCreateLinkMutationOptions(options); - const mutationOptions = getCreateLinkMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * ★ 확정된 URL 만 크롤링 대상이 된다. 사업장 검증이 끝나야 확정할 수 있다. * @summary 채널 URL 확정 */ export const confirmLink = ( - placeId: string, - linkId: string, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + linkId: string, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/link/${linkId}/confirm`, method: 'POST', signal + return customFetch( + { + url: `/v1/place/${placeId}/link/${linkId}/confirm`, + method: "POST", + signal, }, - options); - } - + options, + ); +}; +export const getConfirmLinkMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; linkId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; linkId: string }, + TContext +> => { + const mutationKey = ["confirmLink"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getConfirmLinkMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;linkId: string}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;linkId: string}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; linkId: string } + > = (props) => { + const { placeId, linkId } = props ?? {}; -const mutationKey = ['confirmLink']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return confirmLink(placeId, linkId, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type ConfirmLinkMutationResult = NonNullable< + Awaited> +>; - const mutationFn: MutationFunction>, {placeId: string;linkId: string}> = (props) => { - const {placeId,linkId} = props ?? {}; +export type ConfirmLinkMutationError = void | HTTPValidationError; - return confirmLink(placeId,linkId,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type ConfirmLinkMutationResult = NonNullable>> - - export type ConfirmLinkMutationError = void | HTTPValidationError - - /** +/** * @summary 채널 URL 확정 */ -export const useConfirmLink = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;linkId: string}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;linkId: string}, - TContext - > => { +export const useConfirmLink = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; linkId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; linkId: string }, + TContext +> => { + const mutationOptions = getConfirmLinkMutationOptions(options); - const mutationOptions = getConfirmLinkMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * 수집 파이프라인을 큐에 넣고 즉시 job_id 를 돌려준다. 한 건에 몇 분 걸리므로 GET /v1/job/{job_id} 로 진행 상태를 폴링한다. ★ 동일 업소 검증과 채널 URL 확정이 끝나야 시작할 수 있다. * @summary 수집 시작(비동기) */ export const startCollect = ( - placeId: string, - reqStartCollect: ReqStartCollect, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqStartCollect: ReqStartCollect, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/collect`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqStartCollect, signal + return customFetch( + { + url: `/v1/place/${placeId}/collect`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqStartCollect, + signal, }, - options); - } - + options, + ); +}; +export const getStartCollectMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqStartCollect }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqStartCollect }, + TContext +> => { + const mutationKey = ["startCollect"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getStartCollectMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqStartCollect}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqStartCollect}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqStartCollect } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['startCollect']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return startCollect(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type StartCollectMutationResult = NonNullable< + Awaited> +>; +export type StartCollectMutationBody = ReqStartCollect; +export type StartCollectMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqStartCollect}> = (props) => { - const {placeId,data} = props ?? {}; - - return startCollect(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type StartCollectMutationResult = NonNullable>> - export type StartCollectMutationBody = ReqStartCollect - export type StartCollectMutationError = void | HTTPValidationError - - /** +/** * @summary 수집 시작(비동기) */ -export const useStartCollect = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqStartCollect}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqStartCollect}, - TContext - > => { +export const useStartCollect = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqStartCollect }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqStartCollect }, + TContext +> => { + const mutationOptions = getStartCollectMutationOptions(options); - const mutationOptions = getStartCollectMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * Gemini Vision 으로 사진 분류 라벨과 alt 를 생성한다. 수집이 사진을 저장하면 자동으로 걸리며, 사장님이 직접 올린 사진이나 재분석(force)에 이 엔드포인트를 쓴다. ★ 신뢰도가 낮은 결과는 자동 반영되지 않고 사람 확인 큐에 남는다. * @summary 사진 분석 시작(비동기) */ export const startVision = ( - placeId: string, - reqStartVision: ReqStartVision, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqStartVision: ReqStartVision, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/vision`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqStartVision, signal + return customFetch( + { + url: `/v1/place/${placeId}/vision`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqStartVision, + signal, }, - options); - } - + options, + ); +}; +export const getStartVisionMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqStartVision }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqStartVision }, + TContext +> => { + const mutationKey = ["startVision"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getStartVisionMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqStartVision}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqStartVision}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqStartVision } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['startVision']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return startVision(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type StartVisionMutationResult = NonNullable< + Awaited> +>; +export type StartVisionMutationBody = ReqStartVision; +export type StartVisionMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqStartVision}> = (props) => { - const {placeId,data} = props ?? {}; - - return startVision(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type StartVisionMutationResult = NonNullable>> - export type StartVisionMutationBody = ReqStartVision - export type StartVisionMutationError = void | HTTPValidationError - - /** +/** * @summary 사진 분석 시작(비동기) */ -export const useStartVision = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqStartVision}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqStartVision}, - TContext - > => { +export const useStartVision = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqStartVision }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqStartVision }, + TContext +> => { + const mutationOptions = getStartVisionMutationOptions(options); - const mutationOptions = getStartVisionMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * 외부 장소 DB(카카오 키가 있으면 카카오, 없으면 네이버)에서 후보를 찾아 그대로 돌려준다. ★ 서버가 자동 확정하지 않는다 — UI 가 후보를 보여주고 사람이 고른 뒤 POST /verify 로 확정한다. auto_selectable=true 면 판정이 명확해 '이거 맞나요?' 한 번만 물어도 된다. * @summary 동일 업소 후보 조회 */ export const verifyCandidates = ( - placeId: string, - params?: VerifyCandidatesParams, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + params?: VerifyCandidatesParams, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/verify/candidates`, method: 'GET', - params, signal + return customFetch( + { + url: `/v1/place/${placeId}/verify/candidates`, + method: "GET", + params, + signal, }, - options); - } - + options, + ); +}; - - -export const getVerifyCandidatesQueryKey = (placeId?: string, - params?: VerifyCandidatesParams,) => { - return [ - `/v1/place/${placeId}/verify/candidates`, ...(params ? [params]: []) - ] as const; - } - - -export const getVerifyCandidatesQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, - params?: VerifyCandidatesParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getVerifyCandidatesQueryKey = ( + placeId?: string, + params?: VerifyCandidatesParams, ) => { + return [ + `/v1/place/${placeId}/verify/candidates`, + ...(params ? [params] : []), + ] as const; +}; -const {query: queryOptions, request: requestOptions} = options ?? {}; +export const getVerifyCandidatesQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: VerifyCandidatesParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; - const queryKey = queryOptions?.queryKey ?? getVerifyCandidatesQueryKey(placeId,params); + const queryKey = + queryOptions?.queryKey ?? getVerifyCandidatesQueryKey(placeId, params); - + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => verifyCandidates(placeId, params, requestOptions, signal); - const queryFn: QueryFunction>> = ({ signal }) => verifyCandidates(placeId,params, requestOptions, signal); + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; - +export type VerifyCandidatesQueryResult = NonNullable< + Awaited> +>; +export type VerifyCandidatesQueryError = void | HTTPValidationError; - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type VerifyCandidatesQueryResult = NonNullable>> -export type VerifyCandidatesQueryError = void | HTTPValidationError - - -export function useVerifyCandidates>, TError = void | HTTPValidationError>( - placeId: string, - params: undefined | VerifyCandidatesParams, options: { query:Partial>, TError, TData>> & Pick< +export function useVerifyCandidates< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params: undefined | VerifyCandidatesParams, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useVerifyCandidates>, TError = void | HTTPValidationError>( - placeId: string, - params?: VerifyCandidatesParams, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useVerifyCandidates< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: VerifyCandidatesParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useVerifyCandidates>, TError = void | HTTPValidationError>( - placeId: string, - params?: VerifyCandidatesParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useVerifyCandidates< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: VerifyCandidatesParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 동일 업소 후보 조회 */ -export function useVerifyCandidates>, TError = void | HTTPValidationError>( - placeId: string, - params?: VerifyCandidatesParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useVerifyCandidates< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params?: VerifyCandidatesParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getVerifyCandidatesQueryOptions( + placeId, + params, + options, + ); - const queryOptions = getVerifyCandidatesQueryOptions(placeId,params,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * 확인된 fact 만 근거로 소개문과 FAQ 를 작성한다. ★ 근거 없는 수치·시설 언급은 코드로 검증해 반려한다(LLM 은 사실을 만들지 않는다). 생성물도 미검증 상태로 들어가 사람이 승인해야 사이트에 나간다. * @summary 소개문·FAQ 생성(비동기) */ export const startCopy = ( - placeId: string, - reqStartCopy: ReqStartCopy, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqStartCopy: ReqStartCopy, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/copy`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqStartCopy, signal + return customFetch( + { + url: `/v1/place/${placeId}/copy`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqStartCopy, + signal, }, - options); - } - + options, + ); +}; +export const getStartCopyMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqStartCopy }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqStartCopy }, + TContext +> => { + const mutationKey = ["startCopy"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getStartCopyMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqStartCopy}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqStartCopy}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqStartCopy } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['startCopy']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return startCopy(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type StartCopyMutationResult = NonNullable< + Awaited> +>; +export type StartCopyMutationBody = ReqStartCopy; +export type StartCopyMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqStartCopy}> = (props) => { - const {placeId,data} = props ?? {}; - - return startCopy(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type StartCopyMutationResult = NonNullable>> - export type StartCopyMutationBody = ReqStartCopy - export type StartCopyMutationError = void | HTTPValidationError - - /** +/** * @summary 소개문·FAQ 생성(비동기) */ -export const useStartCopy = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqStartCopy}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqStartCopy}, - TContext - > => { +export const useStartCopy = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqStartCopy }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqStartCopy }, + TContext +> => { + const mutationOptions = getStartCopyMutationOptions(options); - const mutationOptions = getStartCopyMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - \ No newline at end of file + return useMutation(mutationOptions, queryClient); +}; diff --git a/solution/frontend/src/api/generated/showcase/showcase.ts b/solution/frontend/src/api/generated/showcase/showcase.ts index 2ed057a..a8a8f14 100644 --- a/solution/frontend/src/api/generated/showcase/showcase.ts +++ b/solution/frontend/src/api/generated/showcase/showcase.ts @@ -4,9 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import { - useQuery -} from '@tanstack/react-query'; +import { useQuery } from "@tanstack/react-query"; import type { DataTag, DefinedInitialDataOptions, @@ -16,113 +14,158 @@ import type { QueryKey, UndefinedInitialDataOptions, UseQueryOptions, - UseQueryResult -} from '@tanstack/react-query'; + UseQueryResult, +} from "@tanstack/react-query"; import type { HTTPValidationError, ListShowcaseParams, - ResShowcase -} from '.././model'; - -import { customFetch } from '../../mutator/custom-fetch'; + ResShowcase, +} from ".././model"; +import { customFetch } from "../../mutator/custom-fetch"; type SecondParameter unknown> = Parameters[1]; - - /** * 실제로 발행된 사이트를 최신순으로 준다. 상호명·업종·지역(시·군·구까지)·발행 주소·썸네일뿐이다 — 로그인 없이 나가므로 사업장 식별자·전화번호·상세 주소는 싣지 않는다. 썸네일은 그 사이트의 대표 사진이고, 만들지 못한 사이트는 키가 없다. * @summary 발행 사이트 쇼케이스(공개) */ export const listShowcase = ( - params?: ListShowcaseParams, - options?: SecondParameter,signal?: AbortSignal + params?: ListShowcaseParams, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/showcase`, method: 'GET', - params, signal - }, - options); - } - + return customFetch( + { url: `/v1/showcase`, method: "GET", params, signal }, + options, + ); +}; +export const getListShowcaseQueryKey = (params?: ListShowcaseParams) => { + return [`/v1/showcase`, ...(params ? [params] : [])] as const; +}; - -export const getListShowcaseQueryKey = (params?: ListShowcaseParams,) => { - return [ - `/v1/showcase`, ...(params ? [params]: []) - ] as const; - } - - -export const getListShowcaseQueryOptions = >, TError = void | HTTPValidationError>(params?: ListShowcaseParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getListShowcaseQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params?: ListShowcaseParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, ) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getListShowcaseQueryKey(params); - const queryKey = queryOptions?.queryKey ?? getListShowcaseQueryKey(params); + const queryFn: QueryFunction>> = ({ + signal, + }) => listShowcase(params, requestOptions, signal); - + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; - const queryFn: QueryFunction>> = ({ signal }) => listShowcase(params, requestOptions, signal); +export type ListShowcaseQueryResult = NonNullable< + Awaited> +>; +export type ListShowcaseQueryError = void | HTTPValidationError; - - - - - return { queryKey, queryFn, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type ListShowcaseQueryResult = NonNullable>> -export type ListShowcaseQueryError = void | HTTPValidationError - - -export function useListShowcase>, TError = void | HTTPValidationError>( - params: undefined | ListShowcaseParams, options: { query:Partial>, TError, TData>> & Pick< +export function useListShowcase< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params: undefined | ListShowcaseParams, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useListShowcase>, TError = void | HTTPValidationError>( - params?: ListShowcaseParams, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useListShowcase< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params?: ListShowcaseParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useListShowcase>, TError = void | HTTPValidationError>( - params?: ListShowcaseParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useListShowcase< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params?: ListShowcaseParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 발행 사이트 쇼케이스(공개) */ -export function useListShowcase>, TError = void | HTTPValidationError>( - params?: ListShowcaseParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useListShowcase< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params?: ListShowcaseParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getListShowcaseQueryOptions(params, options); - const queryOptions = getListShowcaseQueryOptions(params,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - - diff --git a/solution/frontend/src/api/generated/site/site.ts b/solution/frontend/src/api/generated/site/site.ts index c44b8ee..79ebd49 100644 --- a/solution/frontend/src/api/generated/site/site.ts +++ b/solution/frontend/src/api/generated/site/site.ts @@ -4,10 +4,7 @@ * Web4Ai API * OpenAPI spec version: 0.1.0 */ -import { - useMutation, - useQuery -} from '@tanstack/react-query'; +import { useMutation, useQuery } from "@tanstack/react-query"; import type { DataTag, DefinedInitialDataOptions, @@ -20,13 +17,14 @@ import type { UseMutationOptions, UseMutationResult, UseQueryOptions, - UseQueryResult -} from '@tanstack/react-query'; + UseQueryResult, +} from "@tanstack/react-query"; import type { CheckSlugParams, HTTPValidationError, ListMySitesParams, + ReqRollback, ReqSiteSlug, ReqSiteStatus, ReqSiteTemplate, @@ -39,909 +37,1609 @@ import type { ResSiteSlug, ResSiteVersions, ResSlugCheck, - ResStartBuild -} from '.././model'; - -import { customFetch } from '../../mutator/custom-fetch'; + ResStartBuild, +} from ".././model"; +import { customFetch } from "../../mutator/custom-fetch"; type SecondParameter unknown> = Parameters[1]; - - /** * 현재 발행 버전과 재빌드 필요 여부(needs_rebuild). 노출값이 바뀐 뒤 다시 빌드하지 않았으면 true — 이 사업장만 재빌드하면 된다. * @summary 사이트 상태 */ export const getSite = ( - placeId: string, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/site`, method: 'GET', signal - }, - options); - } - + return customFetch( + { url: `/v1/place/${placeId}/site`, method: "GET", signal }, + options, + ); +}; +export const getGetSiteQueryKey = (placeId?: string) => { + return [`/v1/place/${placeId}/site`] as const; +}; - -export const getGetSiteQueryKey = (placeId?: string,) => { - return [ - `/v1/place/${placeId}/site` - ] as const; - } - - -export const getGetSiteQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getGetSiteQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, ) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getGetSiteQueryKey(placeId); - const queryKey = queryOptions?.queryKey ?? getGetSiteQueryKey(placeId); + const queryFn: QueryFunction>> = ({ + signal, + }) => getSite(placeId, requestOptions, signal); - + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: DataTag; + }; +}; - const queryFn: QueryFunction>> = ({ signal }) => getSite(placeId, requestOptions, signal); +export type GetSiteQueryResult = NonNullable< + Awaited> +>; +export type GetSiteQueryError = void | HTTPValidationError; - - - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type GetSiteQueryResult = NonNullable>> -export type GetSiteQueryError = void | HTTPValidationError - - -export function useGetSite>, TError = void | HTTPValidationError>( - placeId: string, options: { query:Partial>, TError, TData>> & Pick< +export function useGetSite< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useGetSite>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetSite< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useGetSite>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetSite< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 사이트 상태 */ -export function useGetSite>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useGetSite< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetSiteQueryOptions(placeId, options); - const queryOptions = getGetSiteQueryOptions(placeId,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * 현재 확인된 콘텐츠와 최신 빌드를 SEO·AEO 각각 100점으로 채점하고 개선 항목을 반환한다. 준비도 점수이며 실제 검색 순위나 AI 인용을 보장하지 않는다. * @summary SEO/AEO 준비도 진단 */ export const getSeoAudit = ( - placeId: string, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/site/audit`, method: 'GET', signal - }, - options); - } - + return customFetch( + { url: `/v1/place/${placeId}/site/audit`, method: "GET", signal }, + options, + ); +}; +export const getGetSeoAuditQueryKey = (placeId?: string) => { + return [`/v1/place/${placeId}/site/audit`] as const; +}; - -export const getGetSeoAuditQueryKey = (placeId?: string,) => { - return [ - `/v1/place/${placeId}/site/audit` - ] as const; - } - - -export const getGetSeoAuditQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getGetSeoAuditQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, ) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getGetSeoAuditQueryKey(placeId); - const queryKey = queryOptions?.queryKey ?? getGetSeoAuditQueryKey(placeId); + const queryFn: QueryFunction>> = ({ + signal, + }) => getSeoAudit(placeId, requestOptions, signal); - + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; - const queryFn: QueryFunction>> = ({ signal }) => getSeoAudit(placeId, requestOptions, signal); +export type GetSeoAuditQueryResult = NonNullable< + Awaited> +>; +export type GetSeoAuditQueryError = void | HTTPValidationError; - - - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type GetSeoAuditQueryResult = NonNullable>> -export type GetSeoAuditQueryError = void | HTTPValidationError - - -export function useGetSeoAudit>, TError = void | HTTPValidationError>( - placeId: string, options: { query:Partial>, TError, TData>> & Pick< +export function useGetSeoAudit< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useGetSeoAudit>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetSeoAudit< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useGetSeoAudit>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetSeoAudit< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary SEO/AEO 준비도 진단 */ -export function useGetSeoAudit>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useGetSeoAudit< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetSeoAuditQueryOptions(placeId, options); - const queryOptions = getGetSeoAuditQueryOptions(placeId,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * 쓰고 싶은 주소를 미리 본다. 소문자 영문·숫자·하이픈 3~50자만 되고, 예약어와 남이 쓰는 주소는 막힌다. ★ 서버가 상호명으로 자동 확정하지 않는다 — 사람이 고른다. 못 쓰면 reason(INVALID_LENGTH·INVALID_FORMAT·RESERVED·TAKEN)과 대안 하나(suggestion)를 준다. 지금 자기가 쓰는 주소면 available=true 다. * @summary 사이트 주소 사용 가능 확인 */ export const checkSlug = ( - placeId: string, - params: CheckSlugParams, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + params: CheckSlugParams, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/site/slug/check`, method: 'GET', - params, signal + return customFetch( + { + url: `/v1/place/${placeId}/site/slug/check`, + method: "GET", + params, + signal, }, - options); - } - + options, + ); +}; - - -export const getCheckSlugQueryKey = (placeId?: string, - params?: CheckSlugParams,) => { - return [ - `/v1/place/${placeId}/site/slug/check`, ...(params ? [params]: []) - ] as const; - } - - -export const getCheckSlugQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, - params: CheckSlugParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getCheckSlugQueryKey = ( + placeId?: string, + params?: CheckSlugParams, ) => { + return [ + `/v1/place/${placeId}/site/slug/check`, + ...(params ? [params] : []), + ] as const; +}; -const {query: queryOptions, request: requestOptions} = options ?? {}; +export const getCheckSlugQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params: CheckSlugParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; - const queryKey = queryOptions?.queryKey ?? getCheckSlugQueryKey(placeId,params); + const queryKey = + queryOptions?.queryKey ?? getCheckSlugQueryKey(placeId, params); - + const queryFn: QueryFunction>> = ({ + signal, + }) => checkSlug(placeId, params, requestOptions, signal); - const queryFn: QueryFunction>> = ({ signal }) => checkSlug(placeId,params, requestOptions, signal); + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: DataTag; + }; +}; - +export type CheckSlugQueryResult = NonNullable< + Awaited> +>; +export type CheckSlugQueryError = void | HTTPValidationError; - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type CheckSlugQueryResult = NonNullable>> -export type CheckSlugQueryError = void | HTTPValidationError - - -export function useCheckSlug>, TError = void | HTTPValidationError>( - placeId: string, - params: CheckSlugParams, options: { query:Partial>, TError, TData>> & Pick< +export function useCheckSlug< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params: CheckSlugParams, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useCheckSlug>, TError = void | HTTPValidationError>( - placeId: string, - params: CheckSlugParams, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useCheckSlug< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params: CheckSlugParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useCheckSlug>, TError = void | HTTPValidationError>( - placeId: string, - params: CheckSlugParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useCheckSlug< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params: CheckSlugParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 사이트 주소 사용 가능 확인 */ -export function useCheckSlug>, TError = void | HTTPValidationError>( - placeId: string, - params: CheckSlugParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useCheckSlug< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + params: CheckSlugParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getCheckSlugQueryOptions(placeId, params, options); - const queryOptions = getCheckSlugQueryOptions(placeId,params,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * 주소를 sites.domain 에 저장한다(사이트 행이 없으면 만든다). 검증은 check 와 같은 규칙으로 서버가 다시 한다 — 클라이언트 검증을 믿지 않는다. ★ 이미 발행된 사이트의 주소는 바꿀 수 없다(SITE_SLUG_LOCKED) — 색인된 주소가 바뀌면 AI 검색이 잡아 둔 페이지가 404 가 된다. * @summary 사이트 주소 확정 */ export const setSlug = ( - placeId: string, - reqSiteSlug: ReqSiteSlug, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqSiteSlug: ReqSiteSlug, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/site/slug`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqSiteSlug, signal + return customFetch( + { + url: `/v1/place/${placeId}/site/slug`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqSiteSlug, + signal, }, - options); - } - + options, + ); +}; +export const getSetSlugMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqSiteSlug }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqSiteSlug }, + TContext +> => { + const mutationKey = ["setSlug"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getSetSlugMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqSiteSlug}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqSiteSlug}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqSiteSlug } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['setSlug']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return setSlug(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type SetSlugMutationResult = NonNullable< + Awaited> +>; +export type SetSlugMutationBody = ReqSiteSlug; +export type SetSlugMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqSiteSlug}> = (props) => { - const {placeId,data} = props ?? {}; - - return setSlug(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type SetSlugMutationResult = NonNullable>> - export type SetSlugMutationBody = ReqSiteSlug - export type SetSlugMutationError = void | HTTPValidationError - - /** +/** * @summary 사이트 주소 확정 */ -export const useSetSlug = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqSiteSlug}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqSiteSlug}, - TContext - > => { +export const useSetSlug = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqSiteSlug }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqSiteSlug }, + TContext +> => { + const mutationOptions = getSetSlugMutationOptions(options); - const mutationOptions = getSetSlugMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * 위저드에서 고른 템플릿을 sites.template_id 에 저장한다(사이트 행이 없으면 만든다). ★ 서버는 값을 검증하지 않는다 — 템플릿 목록은 프론트가 소유한다. 길이(100자)만 막는다. ★ 주소와 달리 발행 뒤에도 바꿀 수 있다: 디자인이 바뀌어도 URL 은 그대로라 색인이 깨지지 않는다. 이미 발행된 사이트면 재빌드가 필요하다는 표시로 content_updated_at 을 찍는다(needs_rebuild=true). * @summary 템플릿(디자인) 선택 저장 */ export const setTemplate = ( - placeId: string, - reqSiteTemplate: ReqSiteTemplate, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqSiteTemplate: ReqSiteTemplate, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/site/template`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqSiteTemplate, signal + return customFetch( + { + url: `/v1/place/${placeId}/site/template`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqSiteTemplate, + signal, }, - options); - } - + options, + ); +}; +export const getSetTemplateMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqSiteTemplate }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqSiteTemplate }, + TContext +> => { + const mutationKey = ["setTemplate"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getSetTemplateMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqSiteTemplate}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqSiteTemplate}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqSiteTemplate } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['setTemplate']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return setTemplate(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type SetTemplateMutationResult = NonNullable< + Awaited> +>; +export type SetTemplateMutationBody = ReqSiteTemplate; +export type SetTemplateMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqSiteTemplate}> = (props) => { - const {placeId,data} = props ?? {}; - - return setTemplate(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type SetTemplateMutationResult = NonNullable>> - export type SetTemplateMutationBody = ReqSiteTemplate - export type SetTemplateMutationError = void | HTTPValidationError - - /** +/** * @summary 템플릿(디자인) 선택 저장 */ -export const useSetTemplate = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqSiteTemplate}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqSiteTemplate}, - TContext - > => { +export const useSetTemplate = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqSiteTemplate }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqSiteTemplate }, + TContext +> => { + const mutationOptions = getSetTemplateMutationOptions(options); - const mutationOptions = getSetTemplateMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * 에디터가 정한 색·서체·섹션(순서·on/off·배리에이션)을 sites.theme 에 저장한다(사이트 행이 없으면 만든다). body 최상위 키는 theme 하나다: {"theme":{"colors":{...},"fontStyle":"...","look":{...},"colorPaletteId":"...","sections":[{"id","name","enabled","locked","variantId","body","data"}]}}. ★ sections 의 배열 순서가 곧 섹션 순서다(별도 order 필드 없음). ★ 서버는 값을 해석하지 않는다 — 섹션 목록·배리에이션 키·색 토큰은 프론트가 소유한다. 직렬화 크기(64KB)만 막는다. ★ templateId 는 여기 담지 않는다 — sites.template_id 와 POST /template 이 담당한다. ★ colorPaletteId 는 에디터 복원 전용이라 저장·반환만 하고 발행 payload 에는 싣지 않는다. ★ 빈 값({})을 보내면 NULL 로 되돌아가 업종 기본 색·서체·섹션으로 떨어진다. ★ 템플릿과 같이 발행 뒤에도 바꿀 수 있다(디자인이 바뀌어도 URL 은 그대로다). 이미 발행된 사이트면 재빌드가 필요하다는 표시로 content_updated_at 을 찍는다(needs_rebuild=true). * @summary 디자인(색·서체·섹션) 저장 */ export const setTheme = ( - placeId: string, - reqSiteTheme: ReqSiteTheme, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqSiteTheme: ReqSiteTheme, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/site/theme`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqSiteTheme, signal + return customFetch( + { + url: `/v1/place/${placeId}/site/theme`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqSiteTheme, + signal, }, - options); - } - + options, + ); +}; +export const getSetThemeMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqSiteTheme }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqSiteTheme }, + TContext +> => { + const mutationKey = ["setTheme"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getSetThemeMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqSiteTheme}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqSiteTheme}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqSiteTheme } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['setTheme']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return setTheme(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type SetThemeMutationResult = NonNullable< + Awaited> +>; +export type SetThemeMutationBody = ReqSiteTheme; +export type SetThemeMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqSiteTheme}> = (props) => { - const {placeId,data} = props ?? {}; - - return setTheme(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type SetThemeMutationResult = NonNullable>> - export type SetThemeMutationBody = ReqSiteTheme - export type SetThemeMutationError = void | HTTPValidationError - - /** +/** * @summary 디자인(색·서체·섹션) 저장 */ -export const useSetTheme = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqSiteTheme}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqSiteTheme}, - TContext - > => { +export const useSetTheme = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqSiteTheme }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqSiteTheme }, + TContext +> => { + const mutationOptions = getSetThemeMutationOptions(options); - const mutationOptions = getSetThemeMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * 스냅샷을 박제해 HTML + JSON-LD 를 만들고 발행 검수 게이트를 돌린다. publish=true 면 통과 시 바로 발행한다. ★ 게이트를 우회하는 옵션은 없다. * @summary 정적 빌드(비동기) */ export const startBuild = ( - placeId: string, - reqStartBuild: ReqStartBuild, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqStartBuild: ReqStartBuild, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/site/build`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqStartBuild, signal + return customFetch( + { + url: `/v1/place/${placeId}/site/build`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqStartBuild, + signal, }, - options); - } - + options, + ); +}; +export const getStartBuildMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqStartBuild }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqStartBuild }, + TContext +> => { + const mutationKey = ["startBuild"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getStartBuildMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqStartBuild}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqStartBuild}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqStartBuild } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['startBuild']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return startBuild(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type StartBuildMutationResult = NonNullable< + Awaited> +>; +export type StartBuildMutationBody = ReqStartBuild; +export type StartBuildMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqStartBuild}> = (props) => { - const {placeId,data} = props ?? {}; - - return startBuild(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type StartBuildMutationResult = NonNullable>> - export type StartBuildMutationBody = ReqStartBuild - export type StartBuildMutationError = void | HTTPValidationError - - /** +/** * @summary 정적 빌드(비동기) */ -export const useStartBuild = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqStartBuild}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqStartBuild}, - TContext - > => { +export const useStartBuild = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqStartBuild }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqStartBuild }, + TContext +> => { + const mutationOptions = getStartBuildMutationOptions(options); - const mutationOptions = getStartBuildMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** - * @summary 빌드 버전 목록 - */ -export const listVersions = ( - placeId: string, - options?: SecondParameter,signal?: AbortSignal -) => { - - - return customFetch( - {url: `/v1/place/${placeId}/site/version/list`, method: 'GET', signal - }, - options); - } - - - - -export const getListVersionsQueryKey = (placeId?: string,) => { - return [ - `/v1/place/${placeId}/site/version/list` - ] as const; - } - - -export const getListVersionsQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} -) => { - -const {query: queryOptions, request: requestOptions} = options ?? {}; - - const queryKey = queryOptions?.queryKey ?? getListVersionsQueryKey(placeId); - - - - const queryFn: QueryFunction>> = ({ signal }) => listVersions(placeId, requestOptions, signal); - - - - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type ListVersionsQueryResult = NonNullable>> -export type ListVersionsQueryError = void | HTTPValidationError - - -export function useListVersions>, TError = void | HTTPValidationError>( - placeId: string, options: { query:Partial>, TError, TData>> & Pick< - DefinedInitialDataOptions< - Awaited>, - TError, - Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useListVersions>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>> & Pick< - UndefinedInitialDataOptions< - Awaited>, - TError, - Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useListVersions>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + return useMutation(mutationOptions, queryClient); +}; /** - * @summary 빌드 버전 목록 + * 발행이 굽는 것과 **같은 함수**로 만든 SitePayload 를 그대로 준다. 미리보기가 이 하나만 먹으면 캔버스와 발행본이 갈릴 자리가 없다. ★ DB 도 파일도 건드리지 않는다 — 버전을 만들지 않으므로 눌러도 발행 이력이 안 쌓인다. + * @summary 에디터 미리보기 payload — 발행본과 같은 것 + */ +export const sitePreview = ( + placeId: string, + options?: SecondParameter, + signal?: AbortSignal, +) => { + return customFetch( + { url: `/v1/place/${placeId}/site/preview`, method: "GET", signal }, + options, + ); +}; + +export const getSitePreviewQueryKey = (placeId?: string) => { + return [`/v1/place/${placeId}/site/preview`] as const; +}; + +export const getSitePreviewQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getSitePreviewQueryKey(placeId); + + const queryFn: QueryFunction>> = ({ + signal, + }) => sitePreview(placeId, requestOptions, signal); + + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type SitePreviewQueryResult = NonNullable< + Awaited> +>; +export type SitePreviewQueryError = void | HTTPValidationError; + +export function useSitePreview< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useSitePreview< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useSitePreview< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary 에디터 미리보기 payload — 발행본과 같은 것 */ -export function useListVersions>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useSitePreview< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getSitePreviewQueryOptions(placeId, options); - const queryOptions = getListVersionsQueryOptions(placeId,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } +/** + * target_version 을 다시 공개 주소로 내보낸다. 재수집·재생성을 하지 않는다 — 그 버전이 발행될 때 박제된 snapshot 을 그대로 다시 굽는다. 렌더러가 그 사이 바뀌었을 수 있어 게이트는 다시 통과해야 한다. 실패하면 지금 공개된 버전은 그대로다. + * @summary 예전 버전으로 되돌리기(비동기) + */ +export const rollbackVersion = ( + placeId: string, + reqRollback: ReqRollback, + options?: SecondParameter, + signal?: AbortSignal, +) => { + return customFetch( + { + url: `/v1/place/${placeId}/site/version/rollback`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqRollback, + signal, + }, + options, + ); +}; +export const getRollbackVersionMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqRollback }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqRollback }, + TContext +> => { + const mutationKey = ["rollbackVersion"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqRollback } + > = (props) => { + const { placeId, data } = props ?? {}; + + return rollbackVersion(placeId, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type RollbackVersionMutationResult = NonNullable< + Awaited> +>; +export type RollbackVersionMutationBody = ReqRollback; +export type RollbackVersionMutationError = void | HTTPValidationError; + +/** + * @summary 예전 버전으로 되돌리기(비동기) + */ +export const useRollbackVersion = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqRollback }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqRollback }, + TContext +> => { + const mutationOptions = getRollbackVersionMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary 빌드 버전 목록 + */ +export const listVersions = ( + placeId: string, + options?: SecondParameter, + signal?: AbortSignal, +) => { + return customFetch( + { url: `/v1/place/${placeId}/site/version/list`, method: "GET", signal }, + options, + ); +}; + +export const getListVersionsQueryKey = (placeId?: string) => { + return [`/v1/place/${placeId}/site/version/list`] as const; +}; + +export const getListVersionsQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getListVersionsQueryKey(placeId); + + const queryFn: QueryFunction>> = ({ + signal, + }) => listVersions(placeId, requestOptions, signal); + + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type ListVersionsQueryResult = NonNullable< + Awaited> +>; +export type ListVersionsQueryError = void | HTTPValidationError; + +export function useListVersions< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useListVersions< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useListVersions< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary 빌드 버전 목록 + */ + +export function useListVersions< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getListVersionsQueryOptions(placeId, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} /** * 발행 시도와 거부 사유. 게이트가 막았으면 무엇이 문제였는지 detail 에 남는다. * @summary 발행 기록 */ export const listLogs = ( - placeId: string, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/site/log/list`, method: 'GET', signal - }, - options); - } - + return customFetch( + { url: `/v1/place/${placeId}/site/log/list`, method: "GET", signal }, + options, + ); +}; +export const getListLogsQueryKey = (placeId?: string) => { + return [`/v1/place/${placeId}/site/log/list`] as const; +}; - -export const getListLogsQueryKey = (placeId?: string,) => { - return [ - `/v1/place/${placeId}/site/log/list` - ] as const; - } - - -export const getListLogsQueryOptions = >, TError = void | HTTPValidationError>(placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getListLogsQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, ) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getListLogsQueryKey(placeId); - const queryKey = queryOptions?.queryKey ?? getListLogsQueryKey(placeId); + const queryFn: QueryFunction>> = ({ + signal, + }) => listLogs(placeId, requestOptions, signal); - + return { + queryKey, + queryFn, + enabled: !!placeId, + ...queryOptions, + } as UseQueryOptions>, TError, TData> & { + queryKey: DataTag; + }; +}; - const queryFn: QueryFunction>> = ({ signal }) => listLogs(placeId, requestOptions, signal); +export type ListLogsQueryResult = NonNullable< + Awaited> +>; +export type ListLogsQueryError = void | HTTPValidationError; - - - - - return { queryKey, queryFn, enabled: !!(placeId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type ListLogsQueryResult = NonNullable>> -export type ListLogsQueryError = void | HTTPValidationError - - -export function useListLogs>, TError = void | HTTPValidationError>( - placeId: string, options: { query:Partial>, TError, TData>> & Pick< +export function useListLogs< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useListLogs>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useListLogs< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useListLogs>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useListLogs< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 발행 기록 */ -export function useListLogs>, TError = void | HTTPValidationError>( - placeId: string, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useListLogs< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + placeId: string, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getListLogsQueryOptions(placeId, options); - const queryOptions = getListLogsQueryOptions(placeId,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - /** * ★ 해지는 삭제가 아니라 상태 전이다. 색인된 페이지를 갑자기 404 로 만들면 AI 검색이 그 자리를 다시 OTA 로 채운다. * @summary 발행 상태 전이(중지·재개·내림) */ export const changeStatus = ( - placeId: string, - reqSiteStatus: ReqSiteStatus, - options?: SecondParameter,signal?: AbortSignal + placeId: string, + reqSiteStatus: ReqSiteStatus, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/place/${placeId}/site/status`, method: 'POST', - headers: {'Content-Type': 'application/json', }, - data: reqSiteStatus, signal + return customFetch( + { + url: `/v1/place/${placeId}/site/status`, + method: "POST", + headers: { "Content-Type": "application/json" }, + data: reqSiteStatus, + signal, }, - options); - } - + options, + ); +}; +export const getChangeStatusMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqSiteStatus }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqSiteStatus }, + TContext +> => { + const mutationKey = ["changeStatus"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; -export const getChangeStatusMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqSiteStatus}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{placeId: string;data: ReqSiteStatus}, TContext> => { + const mutationFn: MutationFunction< + Awaited>, + { placeId: string; data: ReqSiteStatus } + > = (props) => { + const { placeId, data } = props ?? {}; -const mutationKey = ['changeStatus']; -const {mutation: mutationOptions, request: requestOptions} = options ? - options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? - options - : {...options, mutation: {...options.mutation, mutationKey}} - : {mutation: { mutationKey, }, request: undefined}; + return changeStatus(placeId, data, requestOptions); + }; - + return { mutationFn, ...mutationOptions }; +}; +export type ChangeStatusMutationResult = NonNullable< + Awaited> +>; +export type ChangeStatusMutationBody = ReqSiteStatus; +export type ChangeStatusMutationError = void | HTTPValidationError; - const mutationFn: MutationFunction>, {placeId: string;data: ReqSiteStatus}> = (props) => { - const {placeId,data} = props ?? {}; - - return changeStatus(placeId,data,requestOptions) - } - - - - - return { mutationFn, ...mutationOptions }} - - export type ChangeStatusMutationResult = NonNullable>> - export type ChangeStatusMutationBody = ReqSiteStatus - export type ChangeStatusMutationError = void | HTTPValidationError - - /** +/** * @summary 발행 상태 전이(중지·재개·내림) */ -export const useChangeStatus = (options?: { mutation?:UseMutationOptions>, TError,{placeId: string;data: ReqSiteStatus}, TContext>, request?: SecondParameter} - , queryClient?: QueryClient): UseMutationResult< - Awaited>, - TError, - {placeId: string;data: ReqSiteStatus}, - TContext - > => { +export const useChangeStatus = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { placeId: string; data: ReqSiteStatus }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { placeId: string; data: ReqSiteStatus }, + TContext +> => { + const mutationOptions = getChangeStatusMutationOptions(options); - const mutationOptions = getChangeStatusMutationOptions(options); - - return useMutation(mutationOptions, queryClient); - } - /** + return useMutation(mutationOptions, queryClient); +}; +/** * 로그인한 계정(회사)이 가진 사이트 전부. 아직 사이트가 만들어지지 않은 사업장도 site_id=null 로 함께 내려간다 — 위저드를 걸어오다 만 것을 목록에서 잃지 않게 한다. * @summary 내 사이트 목록 */ export const listMySites = ( - params?: ListMySitesParams, - options?: SecondParameter,signal?: AbortSignal + params?: ListMySitesParams, + options?: SecondParameter, + signal?: AbortSignal, ) => { - - - return customFetch( - {url: `/v1/site/list`, method: 'GET', - params, signal - }, - options); - } - + return customFetch( + { url: `/v1/site/list`, method: "GET", params, signal }, + options, + ); +}; +export const getListMySitesQueryKey = (params?: ListMySitesParams) => { + return [`/v1/site/list`, ...(params ? [params] : [])] as const; +}; - -export const getListMySitesQueryKey = (params?: ListMySitesParams,) => { - return [ - `/v1/site/list`, ...(params ? [params]: []) - ] as const; - } - - -export const getListMySitesQueryOptions = >, TError = void | HTTPValidationError>(params?: ListMySitesParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getListMySitesQueryOptions = < + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params?: ListMySitesParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, ) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; -const {query: queryOptions, request: requestOptions} = options ?? {}; + const queryKey = queryOptions?.queryKey ?? getListMySitesQueryKey(params); - const queryKey = queryOptions?.queryKey ?? getListMySitesQueryKey(params); + const queryFn: QueryFunction>> = ({ + signal, + }) => listMySites(params, requestOptions, signal); - + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; - const queryFn: QueryFunction>> = ({ signal }) => listMySites(params, requestOptions, signal); +export type ListMySitesQueryResult = NonNullable< + Awaited> +>; +export type ListMySitesQueryError = void | HTTPValidationError; - - - - - return { queryKey, queryFn, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } -} - -export type ListMySitesQueryResult = NonNullable>> -export type ListMySitesQueryError = void | HTTPValidationError - - -export function useListMySites>, TError = void | HTTPValidationError>( - params: undefined | ListMySitesParams, options: { query:Partial>, TError, TData>> & Pick< +export function useListMySites< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params: undefined | ListMySitesParams, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< DefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): DefinedUseQueryResult & { queryKey: DataTag } -export function useListMySites>, TError = void | HTTPValidationError>( - params?: ListMySitesParams, options?: { query?:Partial>, TError, TData>> & Pick< + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useListMySites< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params?: ListMySitesParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< UndefinedInitialDataOptions< Awaited>, TError, Awaited> - > , 'initialData' - >, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } -export function useListMySites>, TError = void | HTTPValidationError>( - params?: ListMySitesParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useListMySites< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params?: ListMySitesParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; /** * @summary 내 사이트 목록 */ -export function useListMySites>, TError = void | HTTPValidationError>( - params?: ListMySitesParams, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} - , queryClient?: QueryClient - ): UseQueryResult & { queryKey: DataTag } { +export function useListMySites< + TData = Awaited>, + TError = void | HTTPValidationError, +>( + params?: ListMySitesParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getListMySitesQueryOptions(params, options); - const queryOptions = getListMySitesQueryOptions(params,options) + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; - const query = useQuery(queryOptions, queryClient) as UseQueryResult & { queryKey: DataTag }; - - query.queryKey = queryOptions.queryKey ; + query.queryKey = queryOptions.queryKey; return query; } - - - - diff --git a/solution/frontend/src/features/builder/SitePreview.tsx b/solution/frontend/src/features/builder/SitePreview.tsx index 56e58fd..ba3edae 100644 --- a/solution/frontend/src/features/builder/SitePreview.tsx +++ b/solution/frontend/src/features/builder/SitePreview.tsx @@ -19,6 +19,16 @@ */ import {useCallback, useEffect, useRef, useState} from 'react'; import type {ViewportMode} from '@o2o/shared'; +import {LoaderCircle} from 'lucide-react'; +import {onSiteThemeSaved} from '@/features/publish/siteTheme'; + +/** + * 미리보기가 그려지기를 기다리는 상한. 넘으면 로딩 표시를 걷고 iframe 을 그대로 보여준다. + * + * ★ 영영 안 걷히는 로딩바는 안 띄우느니만 못하다 — 구 번들이 올라가 있어 `o2o:preview-painted` + * 를 안 보내는 동안에도 편집은 돼야 한다(발행본 번들은 재발행 전까지 옛것일 수 있다). + */ +const PAINT_TIMEOUT_MS = 12000; /** * 각 해상도에서 iframe 에 줄 **크기**. 발행본이 그 기기에서 보는 것과 같아야 한다. @@ -50,6 +60,67 @@ export function SitePreview({ const ref = useRef(null); const boxRef = useRef(null); const [scale, setScale] = useState(1); + const [painted, setPainted] = useState(false); + /** 저장이 끝날 때마다 올린다 — 값이 바뀌면 iframe 을 다시 띄워 새 payload 를 받는다. */ + const [reloadToken, setReloadToken] = useState(0); + /** 첫 로드인지. 첫 로드만 화면을 덮고, 편집 중 새로고침은 얇은 띠만 보여준다. */ + const firstPaintDone = useRef(false); + + /** + * 편집이 서버에 저장되면 미리보기를 다시 그린다. + * + * ★ 이게 없으면 섹션을 드래그해 순서를 바꿔도 **캔버스가 그대로**다. 왼쪽 목록만 움직이고 + * 사장님에게는 "드래그가 안 먹는" 것으로 보인다(실측 2026-09-15). 섹션 켜고 끄기· + * 배리에이션·색도 전부 같았다 — iframe 은 한 번 뜬 뒤 다시 그릴 계기가 없었다. + * ★ 스토어가 아니라 **저장 완료**를 듣는 이유는 siteTheme.onSiteThemeSaved 주석에 있다. + */ + useEffect(() => onSiteThemeSaved(() => setReloadToken((n) => n + 1)), []); + + /** + * 다시 그릴 때 보던 자리를 지킨다. 맨 위로 튀면 사장님은 방금 옮긴 섹션을 다시 찾아야 한다. + * ★ 첫 로드에는 복원할 것이 없으므로 건너뛴다. + */ + const scrollTop = useRef(0); + useEffect(() => { + if (!reloadToken) return; + const frame = ref.current; + if (!frame?.contentWindow) return; + scrollTop.current = frame.contentWindow.scrollY; + setPainted(false); + frame.contentWindow.location.reload(); + }, [reloadToken]); + + /** + * 미리보기가 **실제로 그려졌는지**. iframe 의 `load` 로는 알 수 없다 — + * 그때는 셸만 뜬 상태이고 payload fetch·웹폰트 대기가 남아 있어 화면은 아직 희다 + * (`site/src/entry-client.tsx` signalPreviewPainted 의 같은 주석). + * + * ★ 신호를 받지 못해도 상한(PAINT_TIMEOUT_MS)에서 걷는다. 구 번들이 올라가 있으면 + * 신호 자체가 없는데, 그 경우 로딩바가 영영 안 사라진다. + * ★ 출처를 확인한다 — 같은 오리진에서 온 우리 iframe 의 메시지만 받는다. + */ + useEffect(() => { + setPainted(false); + const frame = ref.current; + const onMessage = (event: MessageEvent) => { + if (event.origin !== window.location.origin) return; + if (event.source !== frame?.contentWindow) return; + if ((event.data as {type?: string} | null)?.type !== 'o2o:preview-painted') return; + setPainted(true); + firstPaintDone.current = true; + // 다시 그린 경우에만 보던 자리로 돌려놓는다. + if (scrollTop.current) frame?.contentWindow?.scrollTo(0, scrollTop.current); + }; + window.addEventListener('message', onMessage); + const timer = setTimeout(() => { + setPainted(true); + firstPaintDone.current = true; + }, PAINT_TIMEOUT_MS); + return () => { + window.removeEventListener('message', onMessage); + clearTimeout(timer); + }; + }, [placeId, viewport, reloadToken]); /* * ★ iframe 은 **진짜 폭(1024·768·390)** 을 유지하고, 자리에 안 들어가면 축소해서 넣는다. @@ -122,13 +193,32 @@ export function SitePreview({ return (
{/* 축소한 만큼 실제 차지하는 크기도 줄여 준다 — 안 그러면 아래쪽에 빈 공간이 남는다. */} -
+
+ {/* ★ iframe 을 덮는다(자리를 밀어내지 않는다). 레이아웃이 움직이면 그려지는 순간 + 화면이 튀고, 그 튐이 곧 "로딩이 끝났다" 보다 먼저 눈에 들어온다. + ★ 처음 들어올 때만 덮는다. 편집 중에는 옛 그림을 그대로 두고 위에 띠만 올린다 — + 섹션을 옮길 때마다 화면이 하얘지면 방금 무엇을 옮겼는지 놓친다. */} + {!painted && + (firstPaintDone.current ? ( +
+ +
+ ) : ( +
+ +

미리보기를 그리는 중입니다…

+
+ ))}