90be636d66
Gitea registry client with tag listing and pattern matching, cron-based polling scheduler with first-poll safety, poll state persistence. DeployTriggerer interface for decoupled deploy triggering.
3.7 KiB
3.7 KiB
Phase 6: Webhook Handler
Status: ✅ Complete Parent plan: PLAN.md Domain: backend
Objective
Implement the secret UUID-based webhook endpoint that receives image push notifications from CI systems, with auto-creation of unknown projects.
Tasks
- Task 1: Implement webhook HTTP handler —
POST /api/webhook/:secret-uuid - Task 2: Validate incoming payload — extract image name and tag
- Task 3: Look up project by image name in store — match against configured project images
- Task 4: If known project: match tag to stage via tag patterns, determine if auto_deploy
- Task 5: If unknown project: auto-create project with defaults from image inspection (EXPOSE port, labels)
- Task 6: Generate and store webhook secret UUID in settings (on first launch)
- Task 7: Implement webhook URL regeneration (new UUID, invalidates old one)
- Task 8: Define webhook payload struct (
{"image": "registry/org/app:tag"})
Files to Modify/Create
internal/webhook/handler.go— webhook HTTP handler + payload parsinginternal/webhook/matcher.go— project/stage matching logicinternal/webhook/autocreate.go— auto-create project from unknown image
Acceptance Criteria
- Valid webhook URL with correct UUID triggers processing
- Invalid/missing UUID returns 404 (no information leak)
- Known images are matched to projects and stages
- Unknown images trigger auto-creation with sensible defaults
- Webhook URL can be regenerated
Notes
- Webhook URL format:
POST /api/webhook/d8f2a1e9-... - No authentication needed beyond the secret UUID
- Auto-created projects use: image EXPOSE port, "dev" as default stage, auto_deploy: true
- The webhook handler calls into the deployer (Phase 7) — for now, define the interface/callback
- Keep the handler thin — it matches and delegates
Review Checklist
- All tasks completed
- No information leak on invalid UUIDs
- Payload validation rejects malformed input
- Auto-creation uses safe defaults
- Handler is stateless (delegates to store/deployer)
Handoff to Next Phase
Exported API
webhook.NewHandler(store, deployer, inspector)— creates the HTTP handlerwebhook.Handler.Route()— returns achi.Routerto mount at/api/webhookwebhook.EnsureWebhookSecret(store)— generates UUID on first launch, returns current secretwebhook.RegenerateWebhookSecret(store)— replaces secret with new UUID, invalidates old onewebhook.ParseImageRef(ref)— parsesregistry/owner/name:taginto components
Interfaces Defined
webhook.DeployTriggerer—TriggerDeploy(ctx, projectID, stageID, imageTag) error(mirrorsregistry.DeployTriggerer)webhook.ImageInspector—InspectImage(ctx, imageRef) (docker.ImageInfo, error)(wrapsdocker.Client)
Integration Points
- Mount the webhook router:
r.Mount("/api/webhook", webhookHandler.Route()) - Call
webhook.EnsureWebhookSecret(store)at application startup to generate the secret on first launch - The deployer must implement
webhook.DeployTriggerer(same signature asregistry.DeployTriggerer) - The Docker client (
*docker.Client) satisfieswebhook.ImageInspectordirectly
Auto-Create Behavior
- Unknown images create a project with name from image name, port from EXPOSE, healthcheck from image metadata
- A default "dev" stage is created with
tag_pattern: "*",auto_deploy: true,max_instances: 1 - If image inspection fails (not pulled locally), project is created with port=0 and empty healthcheck
Tag Matching
- Uses
path.Match(glob semantics) — same approach as the registry poller - Stages are checked in name-sorted order; first matching stage wins