Files
projax/web/templates/admin.tmpl
mAi c486a8b028 feat(phase 3o admin-index): /admin landing + system panel + nav consolidation
The three admin pages (classify, caldav, bulk) had no shared entry point —
m navigated around and couldn't find them. /admin is now their index:

- 3 cards, each linking to the underlying tool, with live counts
  (orphan count via projax.items_unified predicate; calendar count via
  ListCalendars; item count via projax.items where deleted_at IS NULL
  AND archived = false)
- CalDAV card auto-disables when DAV_URL isn't configured
- System panel: version (build-time ldflags hook), last migration
  (projax.schema_migrations top row), MCP status (token present
  yes/no — token itself never displayed), upstream health (DAV +
  Gitea + Supabase, parallel-probed with 1s HTTP timeout each,
  cached 30s)

web/admin.go houses the handler + cache + probeURL helper + count
queries. Templates/admin.tmpl renders the cards + system grid.
admin_test.go covers /admin render + nav-link presence on every
chrome-bearing route.

Nav consolidation: the three separate admin links in layout.tmpl
collapse to one /admin entry. Pre-existing TestTreeRenders updated
to assert the new shape.

Probe-URL caveat: probeURL counts any HTTP response as "alive" (incl.
4xx) — the admin panel measures reachability, not authorisation. CalDAV
returns 401 on bare GET; Gitea returns 200 at the root; Supabase same.
All show green when alive.
2026-05-16 02:26:07 +02:00

52 lines
1.8 KiB
Cheetah

{{define "content"}}
<h1>Admin</h1>
<p class="muted">Tools for ongoing maintenance of projax data + integrations. The pages also live as direct routes (linked in each card).</p>
<section class="admin-cards">
{{range .Cards}}
<article class="admin-card {{if .Disabled}}disabled{{end}}">
<header>
<h2>
{{if .Disabled}}{{.Title}}{{else}}<a href="{{.Href}}">{{.Title}}</a>{{end}}
</h2>
{{if not .Disabled}}
<p class="count"><strong>{{.Count}}</strong> <small class="muted">{{.CountLabel}}</small></p>
{{else}}
<p class="count muted">— {{.DisabledMsg}}</p>
{{end}}
</header>
<p class="desc">{{.Description}}</p>
</article>
{{end}}
</section>
<section class="admin-system">
<h2>System</h2>
<dl class="system-grid">
<dt>Version</dt>
<dd>{{if .Panel.Version}}<code>{{.Panel.Version}}</code>{{else}}<span class="muted">unset (build without -ldflags)</span>{{end}}</dd>
<dt>Last migration</dt>
<dd>{{if .Panel.MigrationLast}}<code>{{.Panel.MigrationLast}}</code>{{else}}<span class="muted">no migrations recorded</span>{{end}}</dd>
<dt>MCP</dt>
<dd>{{if .Panel.MCPEnabled}}<span class="ok">enabled</span> · <code>/mcp/rpc</code>{{else}}<span class="muted">disabled</span> (PROJAX_MCP_TOKEN unset){{end}}</dd>
{{if .Panel.Healths}}
<dt>Upstreams</dt>
<dd>
<ul class="health-list">
{{range .Panel.Healths}}
<li class="health-row {{if .OK}}health-ok{{else}}health-down{{end}}">
<span class="dot" aria-hidden="true"></span>
<strong>{{.Name}}</strong>
<a href="{{.URL}}" target="_blank" rel="noopener noreferrer"><code>{{.URL}}</code></a>
</li>
{{end}}
</ul>
</dd>
{{end}}
</dl>
</section>
{{end}}