200 lines
8 KiB
Text
200 lines
8 KiB
Text
{{define "content"}}
|
|
<section class="hero hero-split">
|
|
<div>
|
|
<p class="eyebrow">{{.Eyebrow}}</p>
|
|
<h1>{{.Heading}}</h1>
|
|
<p class="lede">{{.Description}}</p>
|
|
</div>
|
|
{{if .APIKey}}
|
|
<div class="hero-meta">
|
|
<span class="pill {{if .APIKey.IsActive}}pill-success{{else}}pill-muted{{end}}">
|
|
{{if .APIKey.IsActive}}Active{{else}}Revoked{{end}}
|
|
</span>
|
|
<p class="resource-slug">{{.APIKey.KeyPrefix}}</p>
|
|
</div>
|
|
{{end}}
|
|
</section>
|
|
|
|
{{if .Form.RevealKey}}
|
|
<section class="alert-card alert-success">
|
|
<p><strong>Copy this API key now:</strong> <code class="secret-code">{{.Form.RevealKey}}</code></p>
|
|
<p>This is the only time the full raw key will be shown. Only the hash and short prefix are stored.</p>
|
|
</section>
|
|
{{end}}
|
|
|
|
{{if .Form.Error}}
|
|
<section class="alert-card alert-error">
|
|
<p>{{.Form.Error}}</p>
|
|
</section>
|
|
{{end}}
|
|
|
|
<section class="section-grid">
|
|
<article class="section-card">
|
|
<div class="section-heading">
|
|
<h2>Key Details</h2>
|
|
<p>Choose a stable label, minimum permissions, and one scope mode for this key.</p>
|
|
</div>
|
|
|
|
<form class="stack-form" method="post" action="{{.Form.Action}}">
|
|
{{template "csrf_field" .}}
|
|
<label class="field">
|
|
<span>Name</span>
|
|
<input type="text" name="name" value="{{.Form.Name}}" maxlength="120" required>
|
|
</label>
|
|
|
|
<label class="field">
|
|
<span>Description</span>
|
|
<textarea name="description" rows="4">{{.Form.Description}}</textarea>
|
|
</label>
|
|
|
|
<label class="field">
|
|
<span>Scope mode</span>
|
|
<select name="scope_mode" required>
|
|
<option value="all_projects" {{if eq .Form.ScopeMode "all_projects"}}selected{{end}}>all_projects</option>
|
|
<option value="project_allow_list" {{if eq .Form.ScopeMode "project_allow_list"}}selected{{end}}>project_allow_list</option>
|
|
<option value="project_deny_list" {{if eq .Form.ScopeMode "project_deny_list"}}selected{{end}}>project_deny_list</option>
|
|
<option value="tag_allow_list" {{if eq .Form.ScopeMode "tag_allow_list"}}selected{{end}}>tag_allow_list</option>
|
|
<option value="tag_deny_list" {{if eq .Form.ScopeMode "tag_deny_list"}}selected{{end}}>tag_deny_list</option>
|
|
</select>
|
|
</label>
|
|
|
|
<label class="field">
|
|
<span>Expires at</span>
|
|
<input type="text" name="expires_at" value="{{.Form.ExpiresAt}}" placeholder="2026-12-31T23:59:59Z or 2026-12-31">
|
|
</label>
|
|
|
|
<fieldset class="choice-group">
|
|
<legend>Permissions</legend>
|
|
<label class="choice-row">
|
|
<input type="checkbox" name="can_download" value="1" {{if .Form.CanDownload}}checked{{end}}>
|
|
<span><strong>updates.read</strong> style access for listing or downloading releases.</span>
|
|
</label>
|
|
<label class="choice-row">
|
|
<input type="checkbox" name="can_upload" value="1" {{if .Form.CanUpload}}checked{{end}}>
|
|
<span>Upload release artifacts.</span>
|
|
</label>
|
|
<label class="choice-row">
|
|
<input type="checkbox" name="can_delete" value="1" {{if .Form.CanDelete}}checked{{end}}>
|
|
<span>Delete or disable release records later.</span>
|
|
</label>
|
|
<label class="choice-row">
|
|
<input type="checkbox" name="can_manage_projects" value="1" {{if .Form.CanManageProjects}}checked{{end}}>
|
|
<span>Create or edit projects.</span>
|
|
</label>
|
|
</fieldset>
|
|
|
|
<fieldset class="choice-group">
|
|
<legend>Project rules</legend>
|
|
<p class="helper-copy">Used only for <code>project_allow_list</code> and <code>project_deny_list</code>.</p>
|
|
{{if .ProjectChoices}}
|
|
<div class="choice-list">
|
|
{{range .ProjectChoices}}
|
|
<label class="choice-row">
|
|
<input type="checkbox" name="project_id" value="{{.Project.ID}}" {{if .Selected}}checked{{end}}>
|
|
<span>{{.Project.Name}} <code>{{.Project.Slug}}</code> {{if not .Project.IsActive}}(archived){{end}}</span>
|
|
</label>
|
|
{{end}}
|
|
</div>
|
|
{{else}}
|
|
<p class="empty-copy">No projects exist yet.</p>
|
|
{{end}}
|
|
</fieldset>
|
|
|
|
<fieldset class="choice-group">
|
|
<legend>Tag rules</legend>
|
|
<p class="helper-copy">Used only for <code>tag_allow_list</code> and <code>tag_deny_list</code>.</p>
|
|
{{if .TagChoices}}
|
|
<div class="choice-list">
|
|
{{range .TagChoices}}
|
|
<label class="choice-row">
|
|
<input type="checkbox" name="tag_id" value="{{.Tag.ID}}" {{if .Selected}}checked{{end}}>
|
|
<span>{{.Tag.Name}} <code>{{.Tag.Slug}}</code></span>
|
|
</label>
|
|
{{end}}
|
|
</div>
|
|
{{else}}
|
|
<p class="empty-copy">No tags exist yet.</p>
|
|
{{end}}
|
|
</fieldset>
|
|
|
|
<div class="button-row">
|
|
<button class="button" type="submit">{{.Form.SubmitLabel}}</button>
|
|
<a class="button button-secondary" href="/admin/api-keys">Back to API keys</a>
|
|
</div>
|
|
</form>
|
|
</article>
|
|
|
|
{{if .APIKey}}
|
|
<article class="section-card">
|
|
<div class="section-heading">
|
|
<h2>Lifecycle</h2>
|
|
<p>Disabled or expired keys are rejected before any project scope checks happen.</p>
|
|
</div>
|
|
|
|
<p class="meta-line"><strong>Created:</strong> {{.APIKey.CreatedAt.Format "2006-01-02 15:04 UTC"}}</p>
|
|
<p class="meta-line"><strong>Updated:</strong> {{.APIKey.UpdatedAt.Format "2006-01-02 15:04 UTC"}}</p>
|
|
<p class="meta-line"><strong>Last used:</strong> {{if .APIKey.LastUsedAt}}{{.APIKey.LastUsedAt.Format "2006-01-02 15:04 UTC"}}{{else}}Never{{end}}</p>
|
|
<p class="meta-line"><strong>Expires:</strong> {{if .APIKey.ExpiresAt}}{{.APIKey.ExpiresAt.Format "2006-01-02 15:04 UTC"}}{{else}}No expiry{{end}}</p>
|
|
|
|
<form class="stack-form" method="post" action="{{.ToggleAction}}">
|
|
{{template "csrf_field" .}}
|
|
<input type="hidden" name="state" value="{{.ToggleState}}">
|
|
<button class="button button-secondary" type="submit">{{.ToggleLabel}}</button>
|
|
</form>
|
|
</article>
|
|
{{end}}
|
|
</section>
|
|
|
|
{{if .APIKey}}
|
|
<section class="section-card section-card-wide">
|
|
<div class="section-heading">
|
|
<h2>Effective Access Preview</h2>
|
|
<p>This preview shows the active projects currently reachable after scope mode evaluation.</p>
|
|
</div>
|
|
|
|
{{if .AccessibleProjects}}
|
|
<div class="resource-grid compact-grid">
|
|
{{range .AccessibleProjects}}
|
|
<article class="resource-card">
|
|
<div class="resource-header">
|
|
<div>
|
|
<h3>{{.Name}}</h3>
|
|
<p class="resource-slug">{{.Slug}}</p>
|
|
</div>
|
|
<span class="pill pill-success">Active</span>
|
|
</div>
|
|
<p class="resource-description">{{if .Description}}{{.Description}}{{else}}No description yet.{{end}}</p>
|
|
</article>
|
|
{{end}}
|
|
</div>
|
|
{{else}}
|
|
<p class="empty-copy">No active projects currently match this key.</p>
|
|
{{end}}
|
|
</section>
|
|
|
|
<section class="section-card section-card-wide">
|
|
<div class="section-heading">
|
|
<h2>Client API Quick Start</h2>
|
|
<p>Clients should authenticate with a bearer API key and then discover projects before requesting metadata or downloading an artifact.</p>
|
|
</div>
|
|
|
|
<div class="endpoint-list">
|
|
<p class="meta-line"><strong>Bearer header:</strong> <code>Authorization: Bearer {{if .Form.RevealKey}}{{.Form.RevealKey}}{{else}}<paste-api-key>{{end}}</code></p>
|
|
<p class="meta-line"><strong>Accessible projects:</strong> <code>{{.BaseURL}}/api/v1/projects</code></p>
|
|
</div>
|
|
|
|
<pre class="code-block"><code>curl -H "Authorization: Bearer {{if .Form.RevealKey}}{{.Form.RevealKey}}{{else}}<paste-api-key>{{end}}" \
|
|
{{.BaseURL}}/api/v1/projects</code></pre>
|
|
|
|
{{if .AccessibleProjects}}
|
|
<div class="endpoint-list">
|
|
{{range .AccessibleProjects}}
|
|
<p class="meta-line"><strong>{{.Name}} latest metadata:</strong> <code>{{printf "%s/api/v1/projects/%s/releases/latest" $.BaseURL .Slug}}</code></p>
|
|
{{end}}
|
|
</div>
|
|
{{else}}
|
|
<p class="empty-copy">This key does not currently expose any active projects, so client metadata and download lookups will return no accessible resources.</p>
|
|
{{end}}
|
|
</section>
|
|
{{end}}
|
|
{{end}}
|