mirror of
https://github.com/saltbo/zpan.git
synced 2026-08-30 17:50:07 +08:00
28c16eb2b0
* feat: add /api/ihost/images CRUD API with two-stage and stream-proxy upload Implements the full image-hosting CRUD at /api/ihost/images: - POST (JSON): two-stage upload — creates draft row + returns presigned URL - POST (multipart): stream-proxy to S3 via PicGo-compatible tool response - GET /: cursor-based list with optional pathPrefix filter - GET /🆔 detail with org isolation enforced - PATCH /:id action=confirm: transitions draft → active, increments quota - DELETE /🆔 hard-deletes S3 object + DB row, decrements quota Auth: session (all verbs) or apiKey with image-hosting:upload (POST only). Path validation: no .., no leading/trailing /, max depth 5, max 256 chars. Collision: auto-appends 4-hex suffix on (orgId, path) conflict. MIME gate: allows png/jpeg/gif/webp; rejects svg+xml with 415. Size gate: max 20 MB enforced at both JSON and multipart paths. Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f * fix(ihost): resolve PR #317 blockers — API key auth, status codes, test coverage - Blocker 1: replace raw SQL key lookup with auth.api.verifyApiKey() so the SHA-256-hashed better-auth API keys are verified correctly - Blocker 2: add explicit pre-checks in JSON branch returning 413 for size > 20 MB, 415 for SVG/unsupported MIME before falling through to zod (which was returning 400 for all of these); also guard non-JSON content type → 415 - Blocker 3: replace raw insertApiKey() SQL helper with createTestApiKey() that calls auth.api.createApiKey() server-side so tests use properly hashed keys; fix expected status codes (401 for missing permission, 415/413); add quota-refund assertion in S3 failure test; add quota exceeded confirm test - Additional: handle selectStorage failure → 503, use Number.isFinite guard for Content-Length, add null guards to getOrgId/getUserId test helpers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ihost): correct rebase conflicts — merge T5 schema + remove duplicate tables - Merge T5's image_hosting_configs/image_hostings FK constraints with CRUD service (resolveActiveImageByToken + incrementAccessCount) and CRUD schemas - Remove duplicate table definitions in test/setup.ts left by rebase conflict resolution (keep T5's FK-constrained versions, add apikey table once) - Fix org-isolation test: insert a real organization row to satisfy the image_hostings.org_id FK constraint added by T5 Agent-Profile: https://agent-kanban.dev/agents/$AK_AGENT_ID * chore: trigger CI on rebased PR #317 Agent-Profile: https://agent-kanban.dev/agents/$AK_AGENT_ID * chore(ihost): add export comment to trigger CI sync event Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(ihost): add targeted tests to meet 95% patch coverage gate Cover previously uncovered patch lines: - s3.ts: add putObject unit test (was 0% — 3 missing lines) - ihost.ts: add tests for 503 no-storage, 413 Content-Length header, 415 unsupported content-type, 400 zod parse failure, 400 missing file field, 415 non-image MIME in multipart, 422 quota exceeded in multipart, nanoid fallback after collision retries, validatePath edge cases (starts-with-/, ends-with-/, invalid chars, path too long) - image-hosting.ts: add direct service tests for deleteImageHosting null guard and confirmImageHosting with size=0; add validatePath tests via multipart path (bypasses zod max-256 guard) - ihost.ts: remove dead code (unreachable 'Unknown action' branch — patchIhostImageSchema discriminated union only allows 'confirm') Agent-Profile: https://agent-kanban.dev/agents/$AK_AGENT_ID --------- Co-authored-by: Bob <aibob@mails.agent-kanban.dev> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>