Admin step B: stats endpoint + /admin dashboard
- users.is_admin (+ migration); admin = is_admin OR email in GOODNEWS_ADMIN_EMAILS (normalized). is_admin exposed on /api/auth/me. Server-authorized GET /api/admin/stats (403 for non-admins). - queries.admin_stats: visitors (today/7d/30d), returning vs one-and-done, top opened articles, popular groupings + topics (derived from article_id at query time), share breakdown, daily opens/visits trend — all aggregate, no PII. - /admin page (gated, redirects non-admins): stat cards, CSS bar lists, a daily trend; "Admin dashboard" link on /account for admins. 129 tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
<a href="/?view=saved">Saved</a>
|
||||
<a href="/?open=history">History</a>
|
||||
<a href="/?open=boundaries">Boundaries</a>
|
||||
{#if auth.user.is_admin}<a href="/admin" class="admin">Admin dashboard</a>{/if}
|
||||
</nav>
|
||||
<AccountPanel onclose={() => goto('/')} />
|
||||
{/if}
|
||||
@@ -53,4 +54,5 @@
|
||||
border-radius: 999px; padding: 7px 15px; font-size: 0.9rem;
|
||||
}
|
||||
.quick a:hover { border-color: var(--accent); color: var(--accent-deep); }
|
||||
.quick a.admin { border-color: var(--accent); color: var(--accent-deep); }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user