161 lines
6.5 KiB
HTML
161 lines
6.5 KiB
HTML
{{template "header" .}}
|
|
|
|
<div class="page-header">
|
|
<div class="page-header-title">
|
|
<h1>Zugänge {{template "helplink" "zugaenge"}}</h1>
|
|
<span class="help">Ein eigener Key je Mensch und je Agent. Nur so steht im
|
|
Protokoll, wer etwas geschrieben hat — und ein einzelner Zugang lässt
|
|
sich zurückziehen, ohne alle anderen zu stören.</span>
|
|
</div>
|
|
</div>
|
|
|
|
{{if .Data.NewKey}}
|
|
<div class="card key-display">
|
|
<strong>Key für „{{.Data.NewKeyName}}“</strong>
|
|
<code class="key-value">{{.Data.NewKey}}</code>
|
|
<span class="help">Wird nur jetzt angezeigt — gespeichert ist nur eine Prüfsumme.
|
|
Geht der Key verloren, muss ein neuer erzeugt werden.</span>
|
|
|
|
{{/* Drei Clients, drei Schreibweisen für denselben Endpunkt. Der Block steht
|
|
hier ausgeschrieben, weil der Klartext-Key nur auf dieser Seite ein
|
|
einziges Mal zu sehen ist — wer ihn erst aus dem README zusammensetzen
|
|
müsste, hätte ihn dann schon nicht mehr. */}}
|
|
<details class="key-instructions">
|
|
<summary>MCP-Client einrichten</summary>
|
|
|
|
<strong>Claude Code</strong>
|
|
<pre><code>claude mcp add --transport http munin-{{.Project.Slug}} {{.Data.ServerURL}}/mcp \
|
|
--header "Authorization: Bearer {{.Data.NewKey}}"</code></pre>
|
|
|
|
<strong>Cline</strong> <span class="help">(<code>cline_mcp_settings.json</code>)</span>
|
|
<pre><code>{
|
|
"mcpServers": {
|
|
"munin-{{.Project.Slug}}": {
|
|
"type": "streamableHttp",
|
|
"url": "{{.Data.ServerURL}}/mcp",
|
|
"headers": { "Authorization": "Bearer {{.Data.NewKey}}" },
|
|
"autoApprove": ["recall", "get", "list", "related"]
|
|
}
|
|
}
|
|
}</code></pre>
|
|
|
|
<strong>OpenCode</strong> <span class="help">(<code>opencode.json</code>)</span>
|
|
<pre><code>{
|
|
"mcp": {
|
|
"munin-{{.Project.Slug}}": {
|
|
"type": "remote",
|
|
"url": "{{.Data.ServerURL}}/mcp",
|
|
"enabled": true,
|
|
"headers": { "Authorization": "Bearer {{.Data.NewKey}}" }
|
|
}
|
|
}
|
|
}</code></pre>
|
|
</details>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .Data.Unused}}
|
|
<div class="message error">
|
|
{{if eq .Data.Unused 1}}Ein aktiver Zugang wurde{{else}}{{.Data.Unused}} aktive Zugänge wurden{{end}}
|
|
seit über 90 Tagen nicht benutzt. Jeder davon ist ein offener Zugang zu diesem
|
|
Projekt. Wenn niemand mehr weiß, wofür ein Key da ist, ist Zurückziehen meist
|
|
die richtige Wahl — ein neuer ist schnell angelegt.
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .Data.ExpiringSoon}}
|
|
<div class="message hint">
|
|
{{if eq .Data.ExpiringSoon 1}}Ein Zugang läuft{{else}}{{.Data.ExpiringSoon}} Zugänge laufen{{end}}
|
|
in den nächsten 14 Tagen ab. Wird er weiter gebraucht: einen neuen anlegen und in den
|
|
Clients austauschen, bevor der alte nicht mehr gilt.
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .Data.Keys}}
|
|
<div class="table-wrap">
|
|
<table class="styled-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Rolle</th>
|
|
<th>Angelegt</th>
|
|
<th>Zuletzt benutzt</th>
|
|
<th>Gültig bis</th>
|
|
<th>Status</th>
|
|
<th class="narrow right"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{$csrf := .CSRFToken}}{{$slug := .Project.Slug}}{{$jetzt := .Data.Now}}
|
|
{{range .Data.Keys}}
|
|
<tr {{if not (.Valid $jetzt)}}class="muted"{{end}}>
|
|
<td>{{.Name}}</td>
|
|
<td><code>{{.Role}}</code></td>
|
|
<td>{{datetime .CreatedAt}}</td>
|
|
<td>{{if .LastUsedAt.IsZero}}<span class="help">nie</span>{{else}}{{datetime .LastUsedAt}}{{end}}</td>
|
|
<td>{{if .ExpiresAt.IsZero}}<span class="help">unbegrenzt</span>{{else}}{{lastDay .ExpiresAt}}{{end}}</td>
|
|
<td>
|
|
{{if .Revoked}}zurückgezogen{{else if .Expired $jetzt}}abgelaufen{{else}}aktiv{{end}}
|
|
{{if .ExpiresSoon $jetzt}}<span class="badge warning" title="Läuft in den nächsten 14 Tagen ab">läuft bald ab</span>{{end}}
|
|
{{if .Unused $jetzt}}<span class="badge warning" title="Seit über 90 Tagen nicht benutzt">ungenutzt</span>{{end}}
|
|
</td>
|
|
<td class="narrow right">
|
|
{{if .Valid $jetzt}}
|
|
<form method="POST" action="/p/{{$slug}}/keys/revoke" class="row"
|
|
data-confirm="Key „{{.Name}}“ zurückziehen? Alle Clients, die ihn benutzen, verlieren sofort den Zugang.">
|
|
<input type="hidden" name="csrf_token" value="{{$csrf}}">
|
|
<input type="hidden" name="id" value="{{.ID}}">
|
|
<button type="submit" class="warning">Zurückziehen</button>
|
|
</form>
|
|
{{end}}
|
|
</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{{else}}
|
|
<div class="empty">
|
|
<p>Noch kein Zugang angelegt.</p>
|
|
<p class="help">Ohne Key kann sich niemand mit diesem Projekt verbinden.</p>
|
|
</div>
|
|
{{end}}
|
|
|
|
<section class="section">
|
|
<h2>Neuer Zugang</h2>
|
|
|
|
<div class="card">
|
|
<form method="POST" action="/p/{{.Project.Slug}}/keys/create" class="form">
|
|
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
|
|
|
<div class="field medium">
|
|
<label for="name">Name</label>
|
|
<span class="help">Wer oder was benutzt diesen Key?</span>
|
|
<input type="text" id="name" name="name" required placeholder="z.B. tom-cline oder ci-pipeline">
|
|
</div>
|
|
|
|
<div class="field medium">
|
|
<label for="role">Rolle</label>
|
|
<select id="role" name="role">
|
|
<option value="read">read — nur lesen und suchen</option>
|
|
<option value="write" selected>write — zusätzlich schreiben</option>
|
|
<option value="admin">admin — zusätzlich Zugänge verwalten</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="field short">
|
|
<label for="expires">Gültig bis</label>
|
|
<span class="help">Optional. Leer = unbegrenzt. Gilt bis einschließlich dieses Tages,
|
|
danach wie zurückgezogen. Sinnvoll für Zugänge auf Zeit, etwa für ein Projekt oder
|
|
eine Person, die nur eine Weile dabei ist.</span>
|
|
<input type="date" id="expires" name="expires" min="{{.Data.Today}}">
|
|
</div>
|
|
|
|
<div class="form-actions">
|
|
<button type="submit">Key erzeugen</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</section>
|
|
|
|
{{template "footer" .}}
|