Files
baron_qa_write/README.md
T
root a420407c18
Deploy EG-BIM QA Gateway / deploy (push) Successful in 48s
fix: support PKCE public client without client secret
2026-09-18 19:59:06 +09:00

90 lines
6.4 KiB
Markdown

# EG-BIM Q&A static pages
`index.html`, `write.html`, `detail.html` 세 페이지로 구성한 EG-BIM Q&A UI입니다. 원본 `egbim_homepage`의 Q&A 화면 구성과 패키지 S/W 메뉴 방향을 참고해, PHP/그누보드/DB 의존성 없이 정적 호스팅에서 동작하도록 분리했습니다.
## 로컬 확인
정적 파일 서버에서 루트를 열면 됩니다.
```bash
python3 -m http.server 4173
```
그 다음 `http://localhost:4173/`에 접속합니다. 브라우저 보안 정책 때문에 `file://` 직접 열기보다 정적 서버를 사용하는 편이 안전합니다.
## feedback.hmac.kr DB 기준 연동
덤프의 EG-BIM 운영 대상은 다음 값으로 고정했습니다.
- `workspaces.id = 6`, `workspace_code = EGBIM`
- `channels.name = Q&A`, `channels.id = 01a0ae40-51c2-7647-a93c-0249b3759777`
- 카테고리: `ERROR_QNA`, `IMPROVEMENT_QNA`, `GENERAL_QNA`
- 신규 상태: `support_tickets.status_code = RECEIVED`, `feedback_status = NEW`
- 첨부 저장소: `storage_bucket = qa_cdn`
작성 시 브라우저가 UUID를 하나 생성합니다. 같은 UUID를 `feedback.id`, `feedback.source_record_id`, `support_tickets.idempotency_key`에 사용하므로 중복 제출을 판별할 수 있습니다. API 서버는 envelope를 받아 다음 DB 레코드를 하나의 트랜잭션으로 생성해야 합니다.
- `feedbacks`: `id`, `channel_id`, `source_namespace`, `source_record_id`, `data`
- `support_tickets`: 작성자/테넌트/제목/내용/분류/상태/`idempotency_key`
- `feedback_comment_attachments`: 답변 댓글 이미지가 생길 때 `comment_id``qa_cdn` 메타데이터
- `support_attachments`: 운영 호환 레이어가 필요할 때 동일 파일의 티켓 첨부 메타데이터
현재 작성 페이지에는 댓글 입력 UI가 없으므로 `comments``commentAttachments`는 빈 배열로 보냅니다. 관리 페이지에서 댓글과 이미지를 작성할 때 같은 규칙으로 `feedback_comments`/`feedback_comment_attachments``ticket_comments`/`support_attachments`를 생성할 수 있도록 envelope를 열어 두었습니다.
## 연동 지점
- SSO: 헤더 로그인 링크와 작성 페이지의 로그인 가이드는 `https://test.baroncs.co.kr/`로 연결됩니다. `baron_user`, `baron_claims`, Descope 쿠키, JWT payload와 `sessionStorage`를 우선 읽고, `ssoSessionEndpoint`가 설정되면 `credentials: include`로 세션 bridge를 호출합니다. 브라우저에서 읽은 JWT claim은 표시/전송용 힌트일 뿐이며, feedback 서버는 반드시 SSO 세션 또는 토큰을 서버 측에서 검증해야 합니다.
- 작성자 식별자: `ssoSubject`/`requesterId`, `userUuid`, `tenantId`/`requesterTenantId`, `tenantIds`, `scope`, `roles`, 이메일·이름·부서·전화번호를 payload에 넣습니다. `requester_id``requester_tenant_id`가 없으면 제출을 차단합니다.
- API: `assets/config.js``apiBaseUrl`에 API origin을 넣으면 `POST {apiBaseUrl}/v1/qa/uploads/presign`으로 업로드 URL을 받고, 파일을 `qa_cdn`에 직접 업로드한 뒤 `POST {apiBaseUrl}/v1/qa/feedbacks`로 DB용 envelope를 보냅니다. 두 엔드포인트의 인증/응답 규격은 실제 feedback 서버에 맞춰야 합니다.
- presign 응답: `{ "uploads": [{ "uploadUrl": "...", "storageKey": "...", "storageBucket": "qa_cdn", "headers": {} }] }` 형태를 기대합니다. R2 access key/secret은 정적 페이지에 넣지 않습니다.
- API 주소가 비어 있으면 테스트를 위해 브라우저 `localStorage`에만 저장하며, 첨부파일은 `local-preview/...` 메타데이터만 생성합니다.
## Cloudflare R2
`wrangler.toml``src/index.js`를 추가해 Worker가 R2 정적 파일을 제공하도록 구성했습니다. `/``/index.html`은 공개 목록, `/egbim/``/write.html`, `/detail.html`은 로그인 보호 경로입니다. 업로드 스크립트는 루트와 `/egbim/` 경로에 현재 EG-BIM 파일을 함께 올려, 기존 링크와 향후 제품별 prefix 확장을 모두 지원합니다.
```bash
npm install
npm run check
npm run r2:upload
npm run deploy
```
`wrangler deploy`는 Worker와 R2 binding을 배포하고, `npm run r2:upload`는 정적 파일을 R2 bucket에 올립니다. `npx wrangler r2 object put`은 각 파일을 개별 업로드하므로 업로드 결과를 확인하기 쉽습니다.
## Wrangler secret 등록
실제 secret은 저장소에 만들지 않습니다. `qa-secrets.json.example`을 복사해 `qa-secrets.json`을 만들고 값을 채운 뒤 등록합니다.
```bash
cp qa-secrets.json.example qa-secrets.json
openssl rand -hex 32
npx wrangler secret bulk qa-secrets.json --name baron-qa-gateway-test
```
`AUTH_CLIENT_ID`, `AUTH_AUTHORIZE_URL`, `AUTH_TOKEN_URL`, 선택적인 `AUTH_USERINFO_URL``wrangler.toml`에 실제 SSO 값으로 설정해야 합니다. 이 RP는 PKCE Public Client이므로 `AUTH_CLIENT_SECRET`은 사용하지 않으며, `SESSION_SECRET`만 secret으로 등록합니다.
Worker는 OAuth Authorization Code + PKCE를 사용하고, callback에서 검증한 사용자 claim을 서명된 HttpOnly 세션 쿠키에 저장합니다. 브라우저의 `GET /auth/session`은 정규화된 작성자 정보만 반환합니다.
## Gitea Actions 등록값
저장소 Settings → Actions → Secrets에 아래 2개를 등록합니다.
| 이름 | 종류 | 값 |
|---|---|---|
| `CLOUDFLARE_API_TOKEN` | Secret | Workers Scripts Edit + Workers R2 Storage Edit 권한의 Cloudflare API Token |
| `SESSION_SECRET` | Secret | `openssl rand -hex 32`로 생성한 세션 서명키 |
`CLOUDFLARE_ACCOUNT_ID`는 secret으로 등록할 필요가 없습니다. `wrangler.toml``81fa2d48964d31dd0da9558f9ce601d1`로 설정되어 있습니다.
Cloudflare API Token에는 최소한 다음 권한이 필요합니다.
- Account → Workers Scripts → Edit
- Account → Workers R2 Storage → Edit
- Account → Account Settings → Read
- Custom Domain route를 Actions에서 변경할 경우 Zone → Workers Routes → Edit
`.gitea/workflows/deploy.yml``main` push 또는 수동 실행 시 세션 secret을 Worker에 등록하고 R2 업로드 후 `baron-qa-gateway-test`를 배포합니다. 실제 secret 값은 로그에 출력하지 않습니다.
현재 화면은 API 설정 전에도 QA 흐름을 확인할 수 있도록 샘플 글과 로컬 테스트 저장을 포함합니다. 운영 반영 시 `apiBaseUrl`과 feedback API endpoint를 설정하고 로컬 fallback 제거 여부를 결정하세요.