check / check (push) Waiting to run
A database target's archive expiry was typed by hand as never or a raw duration such as 720h, and the target list showed it back raw. Adding or editing a database target now offers the new-webhook page's list of choices (never, 1h, 12h, 24h, 30d, 90d, 365d), defined once and shared by all three forms. The edit form starts on the stored expiry, or on the submitted one after a refused save; a stored value outside the choices is listed under its own value, so saving unchanged keeps it. The target list shows the expiry in plain units: "30 days", "12 hours", "never". Model: opus-5-5
88 lines
4.8 KiB
HTML
88 lines
4.8 KiB
HTML
{{template "base" .}}
|
|
|
|
{{define "title"}}Edit {{.Target.Name}} - Webhooker{{end}}
|
|
|
|
{{define "content"}}
|
|
<div class="max-w-2xl mx-auto px-6 py-8">
|
|
<div class="mb-6">
|
|
<a href="/hook/{{.Webhook.ID}}" class="btn-small">← Back to {{.Webhook.Name}}</a>
|
|
<h1 class="text-2xl font-medium text-gray-900 mt-2">Edit Target</h1>
|
|
<p class="text-sm text-gray-500 mt-1">Type: {{.Target.Type}}. A target's type cannot be changed; create a new target to deliver a different way.</p>
|
|
</div>
|
|
|
|
<div class="card p-6">
|
|
{{if .Error}}
|
|
<div class="alert-error">{{.Error}}</div>
|
|
{{end}}
|
|
|
|
{{if or (eq .Target.Type "http") (eq .Target.Type "slack")}}
|
|
<div class="mb-6 rounded-md bg-gray-50 p-4 text-sm text-gray-700">
|
|
This form shows the target's destination in full, including any credential carried in its URL or headers. It is the only page that does; everywhere else the value is masked.
|
|
</div>
|
|
{{end}}
|
|
|
|
<form method="POST" action="/hook/{{.Webhook.ID}}/targets/{{.Target.ID}}/edit" class="space-y-6">
|
|
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
|
|
|
<div class="form-group">
|
|
<label for="name" class="label">Name</label>
|
|
<input type="text" id="name" name="name" value="{{.TargetForm.Name}}" required class="input">
|
|
</div>
|
|
|
|
{{if eq .Target.Type "http"}}
|
|
<div class="form-group">
|
|
<label for="url" class="label">Destination URL</label>
|
|
<input type="url" id="url" name="url" value="{{.TargetForm.URL}}" required class="input">
|
|
<p class="text-xs text-gray-500 mt-1">Revalidated on save; destinations that resolve to private or link-local addresses are rejected.</p>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="headers" class="label">Headers</label>
|
|
<textarea id="headers" name="headers" rows="4" class="input" placeholder="Authorization: Bearer ...">{{.TargetForm.Headers}}</textarea>
|
|
<p class="text-xs text-gray-500 mt-1">One <code>Name: value</code> per line, sent with every delivery. Leave blank for none. <code>Host</code>, <code>Content-Length</code>, <code>Transfer-Encoding</code>, <code>Connection</code>, <code>Trailer</code> and <code>User-Agent</code> are set by the delivery engine and are rejected here rather than silently ignored. Headers set here are dropped if a redirect leaves the destination's own origin, so a credential cannot follow one to another host.</p>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="timeout" class="label">Timeout (seconds)</label>
|
|
<input type="number" id="timeout" name="timeout" value="{{.TargetForm.Timeout}}" min="0" max="{{.MaxTimeout}}" class="input">
|
|
<p class="text-xs text-gray-500 mt-1">Per-request timeout, at most {{.MaxTimeout}} seconds. Leave blank to use the default.</p>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if eq .Target.Type "slack"}}
|
|
<div class="form-group">
|
|
<label for="url" class="label">Webhook URL</label>
|
|
<input type="url" id="url" name="url" value="{{.TargetForm.URL}}" required class="input">
|
|
<p class="text-xs text-gray-500 mt-1">Slack or Mattermost incoming webhook URL. Revalidated on save.</p>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if eq .Target.Type "database"}}
|
|
<div class="form-group">
|
|
<label for="expiry" class="label">Archive Expiry</label>
|
|
<select id="expiry" name="expiry" class="input">
|
|
{{range .ArchiveExpiryChoices}}
|
|
<option value="{{.Value}}"{{if .Selected}} selected{{end}}>{{.Label}}</option>
|
|
{{end}}
|
|
</select>
|
|
<p class="text-xs text-gray-500 mt-1">Archived events older than this are deleted from the archive; never keeps them all.</p>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if or (eq .Target.Type "http") (eq .Target.Type "slack")}}
|
|
<div class="form-group">
|
|
<label for="max_retries" class="label">Max retries</label>
|
|
<input type="number" id="max_retries" name="max_retries" value="{{.TargetForm.MaxRetries}}" min="0" max="20" class="input">
|
|
<p class="text-xs text-gray-500 mt-1">This is the total number of delivery attempts, not retries on top of the first: a value of 3 makes three attempts in all. 0 means a single attempt with no retries and no circuit breaker.</p>
|
|
</div>
|
|
{{end}}
|
|
|
|
<div class="flex gap-3">
|
|
<button type="submit" class="btn-primary">Save Changes</button>
|
|
<a href="/hook/{{.Webhook.ID}}" class="btn-secondary">Cancel</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{{end}}
|