Skip to content

[Admin][UI] Moderation controls + admin audit-log viewer #41

Closed

Description

@serge-ivo

[Admin][UI] Moderation controls + admin audit-log viewer

Why

Surface the moderation actions and make the admin-action audit log reviewable (self-oversight; a gap PAS still has).

Scope

  • Reusable confirm-dialog moderation controls (suspend/unsuspend user, grant/revoke role, unpublish/delete agent, cancel instance, revoke key) — embedded in the User/Agent/Instance detail pages, all calling the Moderation API.
  • Audit-log viewer page: read admin_audit_log (needs a GET /v1/admin/audit endpoint — add here if not already) — actor, action, target, detail, time; filter by actor/action/target.

Acceptance criteria

  • Every moderation action requires explicit confirm and shows success/failure.
  • Audit-log viewer lists actions with filters; each moderation action appears in it.
  • Non-admin cannot reach any of it (server 403 + hidden UI).

Relevant code

  • store/admin/ Moderation components + Audit page; consumes Moderation API + admin_audit_log.

References

  • FGS grant/revoke + delist; FAS deprovision.

Part of the PAGS Admin Portal epic. Depends on UI scaffold + Moderation API + Foundation (audit log).

Activity

  1. serge-ivo commented on Aug 6, 2026

    @serge-ivo
    ContributorAuthor

    Shipped in 66fcc23 (CI + Deploy Host green; live at https://proagentstore.online/admin/).

    What already existed: Audit.tsx — an unfiltered list of the last 200 rows. GET /v1/admin/audit already existed too (#28), so no new endpoint was needed. Nothing called the moderation API.

    Built: a reusable DangerAction control + an AuditTrail component (store/admin/src/lib/moderation.tsx), wired into User, Agent and Instance detail — suspend/unsuspend, roles, key revoke, unpublish, delete, cancel.

    How the destructive actions are guarded. The API's constraints are expressed, not papered over:

    • Nothing fires on one click. Opening a control is separate from confirming it. Where the API demands an echo, so does the UI: agent delete requires typing the slug, key revoke the provider name, suspend the login. A mis-click or the wrong row destroys nothing.
    • A 409 is information, not a failure. Delete against an agent with live subscribers surfaces as "N live subscriber instance(s) are still attached — forcing cancels them as part of the delete", and forcing is a second, differently-labelled button (Cancel N subscriber(s) & delete). force is never sent on the first attempt, so nobody force-deletes without having read the number they are stranding.
    • Key revoke is one button per stored provider. There is no "revoke all" — an omitted field must never be able to mean "wipe every key".
    • Self-targeting is refused up front. /v1/auth/me gives the operator their own id, so "suspend" is disabled with a reason on your own account and the admin checkbox is locked rather than offered and then rejected. The server still refuses; the UI just stops offering a button that can only error.
    • Roles is a checkbox set (matching the API's set-not-patch semantics), showing the before/after list in the confirm copy, and re-keyed on the persisted roles so a save cannot leave stale checkboxes on screen.

    The audit log is not a separate silo. Every detail page carries the trail for its own target and refreshes it the moment an action lands — "what has already been done to this account" is a question you have while acting, not later. Rows expand to the recorded before-state. The Audit page itself gains actor/action/target filters (actions listed explicitly, so "has anyone ever deleted an agent" can be asked and answered empty), a row limit, expandable detail, and deep links back to the target — suppressed after a .delete, where the bare id is the honest answer.

    All three ACs met; the non-admin gate (server requireAdmin + the /v1/admin/me app gate) was already in place from #28/#35.

    Left out: store/admin has no test infrastructure (no vitest/testing-library) and CI typechecks the console but not the admin app — the admin build only runs in deploy-host.yml. Verified here with pnpm typecheck, biome check, a production vite build, and a post-deploy check that the new strings are live in the served bundle.

  2. added a commit that references this issue on Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    admin-portalPAGS admin/operator portalenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions