Part of #333 .
Scope
admin이 COMPLETED 상태의 raw_post를 검증해 prod.posts에 복사하는 엔드포인트. "승격" 아니고 검증 이 최종 액션.
Deliverables
`packages/api-server/src/domains/raw_posts/handlers.rs` — `pub async fn verify` 핸들러 + `.route("/{id}/verify", post(verify))` 추가
`packages/api-server/src/domains/raw_posts/service.rs`
`pub async fn verify_raw_post(state, id, admin_id, dto)`
assets에서 raw_post 로드 + status==COMPLETED 가드 (아니면 400)
`crate::domains::posts::service::create_post_from_raw(state.db.as_ref(), admin_id, &rp, dto)` 호출 (prod INSERT)
`state.config.server.app_env != AppEnv::Local` 일 때만 assets status=VERIFIED + verified_at/verified_by UPDATE + pipeline_events INSERT (단일 assets 트랜잭션)
step 3 실패 시 loud error log, prod에는 이미 들어감 (admin 운영 책임)
`pub(crate) async fn insert_pipeline_event(txn, raw_post_id, from, to, actor, note)` 헬퍼
`packages/api-server/src/domains/raw_posts/dto.rs` — `VerifyRawPostDto` (검증 시 admin override: artist_name, group_name, context 등 선택)
`packages/api-server/src/domains/posts/service.rs` — `pub async fn create_post_from_raw(db, admin_id, raw_post, dto)` 신규 (#7과 공유)
테스트 (MockDatabase)
verify_rejects_non_completed
verify_inserts_post_and_flips_status_when_production
verify_skips_assets_write_when_local
verify_returns_not_found_on_missing_id
utoipa 스펙 (OpenAPI) 엔드포인트 노출
Acceptance
`cargo test --lib domains::raw_posts::service::tests` 전부 통과
openapi.json 재생성 시 `/api/v1/raw-posts/{id}/verify` 노출
Depends on
Part of #333.
Scope
admin이 COMPLETED 상태의 raw_post를 검증해 prod.posts에 복사하는 엔드포인트. "승격" 아니고 검증이 최종 액션.
Deliverables
Acceptance
Depends on