feat(gitsync): webhook-triggad synk + helt manuellt läge
All checks were successful
build-and-push / build (push) Successful in 1m7s

- POST /api/git-hook: HMAC-SHA256-verifierad (X-Gitea-Signature),
  reagerar bara på pushar till live-grenen, svarar 202 och synkar async
- webhook-hemlighet genereras server-side (headless via config.json),
  roteras med gitRegenerateWebhookSecret
- auto_sync_minutes=0 + webhook av ⇒ ingen automatisk hämtning alls;
  webhook på ⇒ catch-up-synk vid uppstart (missade event)
- admin-UI: webhook-toggle, target-URL + secret med copy/rotate

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-02 23:52:01 +02:00
parent 9854d85237
commit 5819a6bdce
7 changed files with 236 additions and 7 deletions

View File

@@ -176,6 +176,9 @@ type Mutation {
# Replace the sync keypair (register the new public key as deploy key).
gitRegenerateKey: GitKeyResult!
# Rotate the webhook secret (update the webhook config on the git host).
gitRegenerateWebhookSecret: GitWebhookSecret!
# Delete an image file.
deleteImage(slug: String!, filename: String!): Boolean!
@@ -296,9 +299,16 @@ type GitSyncSettings {
remoteUrl: String!
liveBranch: String!
readOnly: Boolean!
# 0 = no interval polling. With webhook off too, nothing is fetched
# automatically at all (fully manual).
autoSyncMinutes: Int!
publicKey: String!
keySet: Boolean!
# Push-triggered sync: POST /api/git-hook with HMAC-SHA256 signature
# (X-Gitea-Signature) over the raw body using webhookSecret.
webhookEnabled: Boolean!
webhookSecret: String!
webhookUrl: String!
}
type GitSyncStatus {
@@ -347,6 +357,13 @@ input GitSyncInput {
liveBranch: String!
readOnly: Boolean!
autoSyncMinutes: Int!
# The secret is never taken from input — it is generated server-side
# on first enable and rotated via gitRegenerateWebhookSecret.
webhookEnabled: Boolean!
}
type GitWebhookSecret {
secret: String!
}
type ImageFile {