Files
mcias/web/templates/pgcreds.html
Kyle Isom b2f2f04646 UI: pgcreds create button; show logged-in user
* web/templates/pgcreds.html: New Credentials card is now always
  rendered; Add Credentials toggle button reveals the create form
  (hidden by default). Shows a message when all system accounts
  already have credentials. Previously the card was hidden when
  UncredentialedAccounts was empty.
* internal/ui/ui.go: added ActorName string field to PageData;
  added actorName(r) helper resolving username from JWT claims
  via DB lookup, returns empty string if unauthenticated.
* internal/ui/handlers_*.go: all full-page PageData constructors
  now pass ActorName: u.actorName(r).
* web/templates/base.html: nav bar renders actor username as a
  muted label before the Logout button when logged in.
* web/static/style.css: added .nav-actor rule (muted grey, 0.85rem).
2026-03-12 11:38:57 -07:00

135 lines
6.0 KiB
HTML

{{define "pgcreds"}}{{template "base" .}}{{end}}
{{define "title"}}PG Credentials — MCIAS{{end}}
{{define "content"}}
<div class="page-header">
<h1>Postgres Credentials</h1>
<p class="text-muted text-small">Credentials you own or have been granted access to.</p>
</div>
{{if .Creds}}
<div class="card">
<h2 style="font-size:1rem;font-weight:600;margin-bottom:1rem">Your Credentials</h2>
{{range .Creds}}
<div style="border:1px solid var(--color-border);border-radius:6px;padding:1rem;margin-bottom:1rem">
<dl style="display:grid;grid-template-columns:140px 1fr;gap:.35rem .75rem;font-size:.9rem;margin-bottom:.75rem">
<dt class="text-muted">Service Account</dt><dd>{{.ServiceUsername}}</dd>
<dt class="text-muted">Host</dt><dd>{{.PGHost}}:{{.PGPort}}</dd>
<dt class="text-muted">Database</dt><dd>{{.PGDatabase}}</dd>
<dt class="text-muted">Username</dt><dd>{{.PGUsername}}</dd>
<dt class="text-muted">Updated</dt><dd class="text-small text-muted">{{formatTime .UpdatedAt}}</dd>
</dl>
{{/* Grant management — only for the credential owner */}}
{{$credID := .ID}}
{{$svcUUID := .ServiceAccountUUID}}
{{$grants := index $.CredGrants $credID}}
{{if isPGCredOwner $.ActorID .}}
<div style="margin-top:.75rem">
<h3 style="font-size:.85rem;font-weight:600;margin-bottom:.5rem">Access Grants</h3>
{{if $grants}}
<table class="table table-sm" style="font-size:.85rem;margin-bottom:.75rem">
<thead>
<tr><th>User</th><th>Granted</th><th></th></tr>
</thead>
<tbody>
{{range $grants}}
<tr>
<td>{{.GranteeName}}</td>
<td class="text-small text-muted">{{formatTime .GrantedAt}}</td>
<td>
<button class="btn btn-sm btn-danger"
hx-delete="/accounts/{{$svcUUID}}/pgcreds/access/{{.GranteeUUID}}?_next=/pgcreds"
hx-confirm="Revoke access for {{.GranteeName}}?">Revoke</button>
</td>
</tr>
{{end}}
</tbody>
</table>
{{else}}
<p class="text-muted text-small" style="margin-bottom:.5rem">No access grants.</p>
{{end}}
{{/* Plain POST grant form with redirect back to /pgcreds */}}
<form method="POST" action="/accounts/{{.ServiceAccountUUID}}/pgcreds/access"
style="display:flex;gap:.5rem;align-items:center">
<input type="hidden" name="_csrf" value="{{$.CSRFToken}}">
<input type="hidden" name="_next" value="/pgcreds">
<select class="form-control" name="grantee_uuid" required style="flex:1">
<option value="">— select account to grant —</option>
{{range $.AllAccounts}}
<option value="{{.UUID}}">{{.Username}} ({{.AccountType}})</option>
{{end}}
</select>
<button class="btn btn-sm btn-secondary" type="submit">Grant Access</button>
</form>
</div>
{{end}}
<div style="margin-top:.75rem">
<a class="btn btn-sm btn-secondary" href="/accounts/{{.ServiceAccountUUID}}">View Account</a>
</div>
</div>
{{end}}
</div>
{{else}}
<div class="card">
<p class="text-muted">No Postgres credentials are accessible to your account.</p>
</div>
{{end}}
<div class="card" style="margin-top:1.5rem">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:.5rem">
<h2 style="font-size:1rem;font-weight:600;margin:0">New Credentials</h2>
<button class="btn btn-sm btn-secondary"
onclick="var f=document.getElementById('pgcreds-create-form');f.hidden=!f.hidden;this.textContent=f.hidden?'Add Credentials':'Cancel'">Add Credentials</button>
</div>
<div id="pgcreds-create-form" hidden>
<p class="text-muted text-small" style="margin-bottom:1rem;margin-top:.5rem">
Create a credential set for a system account. You will become the owner and
may later grant other accounts read access.
</p>
{{if .UncredentialedAccounts}}
<form method="POST" action="/pgcreds">
<input type="hidden" name="_csrf" value="{{.CSRFToken}}">
<div style="margin-bottom:.75rem">
<label class="form-label" for="pgcreds-account">Service Account</label>
<select id="pgcreds-account" class="form-control" name="account_uuid" required>
<option value="">— select system account —</option>
{{range .UncredentialedAccounts}}
<option value="{{.UUID}}">{{.Username}}</option>
{{end}}
</select>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:.5rem;margin-bottom:.5rem">
<div>
<label class="form-label" for="pgcreds-host">Host</label>
<input id="pgcreds-host" class="form-control" type="text" name="host" placeholder="db.example.com" required>
</div>
<div>
<label class="form-label" for="pgcreds-port">Port</label>
<input id="pgcreds-port" class="form-control" type="number" name="port" placeholder="5432" min="1" max="65535">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:.5rem;margin-bottom:.5rem">
<div>
<label class="form-label" for="pgcreds-db">Database</label>
<input id="pgcreds-db" class="form-control" type="text" name="database" placeholder="myapp" required>
</div>
<div>
<label class="form-label" for="pgcreds-user">Username</label>
<input id="pgcreds-user" class="form-control" type="text" name="username" placeholder="svc_user" required>
</div>
</div>
<div style="margin-bottom:.75rem">
<label class="form-label" for="pgcreds-password">Password</label>
<input id="pgcreds-password" class="form-control" type="password" name="password" required>
</div>
<button class="btn btn-secondary" type="submit">Create Credentials</button>
</form>
{{else}}
<p class="text-muted text-small">All system accounts already have credentials. Create a new system account first.</p>
{{end}}
</div>
</div>
{{end}}