adding claude file and some plans
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# 11 — Integrations: Incoming Webhooks, Outgoing Webhooks, Export
|
||||
|
||||
## Incoming webhooks (GitHub & Gitea)
|
||||
|
||||
- [ ] Endpoint `POST /webhooks/incoming/{provider}` (`github|gitea`), outside `/api/v1`, unauthenticated but **HMAC-verified**
|
||||
- [ ] Signature: `X-Hub-Signature-256: sha256=<hmac>` (both providers); the per-project secret is configured on the incoming side of project settings; verification is constant-time; invalid → **401**, receipt recorded with `signature_valid=false`, body NOT processed
|
||||
- [ ] Project routing: match the payload's repository URL (clone/html URL, normalized like `git_repository_url`) against projects; no match → receipt stored with `project_id NULL`, status `ignored`
|
||||
- [ ] Idempotency: `UNIQUE(source, delivery_id)` (GitHub `X-GitHub-Delivery`, Gitea `X-Gitea-Delivery`); duplicate delivery → 200, no reprocessing
|
||||
- [ ] Receipts: every request stored raw (`payload` text) with event_type, status (`pending → processed | failed | ignored`), `failure_reason`
|
||||
- [ ] **v1 automation — PR merged closes issues**:
|
||||
- [ ] On `pull_request` event with `action=closed` + `merged=true`: scan PR title + body for `close[sd]? #(\d+)` / `fix(e[sd])? #(\d+)` / `resolve[sd]? #(\d+)` (case-insensitive)
|
||||
- [ ] Each `#N` resolving to an open issue in the routed project → set status `done` **as system user (id 1)** with `via_webhook=true`; emits normal `issue.status_changed` (so notifications/SSE/search/outgoing webhooks all fire); comment-like audit trail shows the PR URL in the event payload
|
||||
- [ ] Issues already closed → skipped, noted in receipt
|
||||
- [ ] Replay: `POST /projects/{id}/webhook_receipts/{rid}/replay` re-runs processing on the stored payload (owner only)
|
||||
- [ ] Push events: recorded as receipts, `ignored` in v1 (future: commit-message automation)
|
||||
|
||||
## Outgoing webhooks
|
||||
|
||||
- [ ] Per-project registrations (owner-managed): `url`, `secret`, `active`, `event_types[]` (empty = all)
|
||||
- [ ] Delivery payload (JSON): `{event: "issue.status_changed", timestamp, project: {id, title}, actor: {id, username}, resource: {type, id, issue_num?, title?}, changes: {field: {prev, new}}}`
|
||||
- [ ] Signing: `X-SoloPM-Signature: sha256=<hex hmac-sha256(secret, raw body)>` + `X-SoloPM-Event` + `X-SoloPM-Delivery` (delivery id) headers
|
||||
- [ ] Delivery via `webhook_delivery` jobs: 10s timeout, any 2xx = success; failure → queue backoff (30s·2^n, 8 attempts) → delivery row `failed`
|
||||
- [ ] Delivery log per webhook: status, response code, truncated response body, attempts, timestamps; visible in project settings
|
||||
- [ ] `POST …/webhooks/{whId}/test` — sends a `{event: "webhook.test"}` payload immediately, records a delivery row
|
||||
- [ ] Never deliver to internal targets: resolve the URL host and reject private/loopback ranges (SSRF guard), both at registration and at delivery time
|
||||
|
||||
## Export
|
||||
|
||||
- [ ] `GET /projects/{id}/export?format=json` — streaming download `solopm-{project}-{date}.json`:
|
||||
- [ ] Full project dump: project, members (user refs by username/email), labels, epics, issues (with sub-issue/blocker/epic relations by `project_issue_id`), comments, links, wiki pages, attachment *metadata* (files not embedded; paths listed for manual copy)
|
||||
- [ ] Versioned envelope `{solopm_export: 1, exported_at, data: {...}}` — designed to be re-importable later (import itself is out of scope v1, [17-decisions.md](17-decisions.md))
|
||||
- [ ] `GET /projects/{id}/export?format=csv` — issues as CSV: `number,title,status,priority,estimate,assignee,epic,labels,parent,start_date,target_date,completed_at,created_at,updated_at` (RFC 4180 quoting)
|
||||
- [ ] Both owner-or-member accessible, streamed (no buffering whole dump in memory), audit-logged as `project.exported` ([03-domain-events.md](03-domain-events.md))
|
||||
Reference in New Issue
Block a user