Skip to main content

Admin Panel Features

Full operational control over users, jobs, API keys, billing, audit logs, and system health.

The admin panel lives at admin.acesense.io (Firebase Hosting site acesense-admin). All pages are gated by the email allowlist in the Firestore isAdmin() rule + Firebase custom claim admin: true.


๐Ÿ  Dashboardโ€‹

Real-time overview of the platform:

  • Stats cards: total users, total jobs, completed/running/pending/failed counts
  • Recent jobs (latest 5)
  • Recent users (latest 5)
  • Jobs-per-day chart (last 7 days)
  • Status distribution pie
  • Last-updated timestamp + manual refresh

๐Ÿ‘ฅ Users (/users)โ€‹

Full user lifecycle management with a detail drawer per user.

List viewโ€‹

  • Checkbox-selectable rows
  • Filter by plan (free / trial / pro / team), status (active / suspended), joined date (7d / 30d / all)
  • Search persists in ?q=โ€ฆ URL param
  • Plan pill (color-coded), last-activity column, total-jobs and completed-jobs counts (computed live from the jobs collection)

Detail drawer (click any row)โ€‹

  • Gravatar (SHA-256 hash, identicon fallback) + profile metadata
  • Job stats grid: total / completed / last job at
  • Plan dropdown โ€” free / trial / pro / team. Calls adminSetUserPlan Cloud Function. Updates users/{uid}.plan AND a Firebase custom claim, audited in admin_audit/{autoid}.
  • Suspend / Unsuspend toggle โ€” calls adminSetUserSuspended. Suspending also calls admin.auth().updateUser({disabled: true}) and revokeRefreshTokens() to kick existing sessions immediately.
  • Send password reset โ€” calls sendPasswordResetCustom.
  • Export user data โ€” calls exportUserData, opens the returned ZIP URL in a new tab.
  • API keys section โ€” read-only embedded list of every key the user owns (name, plan, balance, spent, last used, status). Deeper management on /admin-api-keys.
  • Subscription metadata โ€” renders trialEndsAt, welcomeEmailSent, emailVerified and any other subscription.* fields when present.

Bulk actionsโ€‹

When one or more rows are checked:

  • Export selected to CSV (id, email, displayName, plan, isActive, totalJobs, completedJobs, joinedAt, lastJobAt)
  • Send password reset (bulk) โ€” sequential calls with success/fail counts

Existing flows preservedโ€‹

Add User ยท Edit User ยท Delete User ยท View Jobs modal โ€” all unchanged.


๐ŸŽฌ Jobs (/jobs)โ€‹

Real-time job control with a Firestore onSnapshot listener โ€” the page updates live as jobs change status.

List viewโ€‹

  • Status filter tabs auto-include any status seen in the dataset (defensive against new statuses)
  • Filters: status, date range (24h / 7d / 30d / all), has-result (with / without / all), userId autocomplete (top-5 most-frequent userIds)
  • Status pill colors: done/completed (green), running/processing/analyzing/merging (blue), pending (amber), failed/error (red), unknown (gray)
  • "Live / Reconnecting" badge in the header โ€” indicates whether the listener is connected
  • Checkbox-selectable rows

Detail drawerโ€‹

  • Job ID, userId (links to /users?q={userId}), status pill, fileName, createdAt/updatedAt/processedAt, retryCount
  • Show raw โ€” toggle to dump the full Firestore document as JSON
  • Storage paths โ€” videoPath + resultPath, each with a "Download" link that mints a temporary signed URL via getDownloadURL
  • RunPod console link โ€” when runpodJobId is present, opens the queue page in the RunPod console
  • Result viewer โ€” fetches the result JSON via signed URL, renders summary cards (total shots, completed sessions, max speed) + collapsible JSON tree

Action buttonsโ€‹

  • Retry โ€” calls adminRetryJob (re-dispatches to RunPod, increments retryCount, resets status)
  • Mark as done / pending / failed โ€” manual status flip via Firestore updateDoc
  • Delete โ€” uses the existing deleteJobWithFiles helper (Storage + Firestore in one go)

Bulk actionsโ€‹

  • Export selected to CSV (id, userId, status, fileName, createdAt, processedAt, retryCount, videoPath, resultPath)
  • Retry selected โ€” sequential adminRetryJob calls with progress counter
  • Delete selected โ€” confirmation modal listing all selected rows

๐Ÿ“Š Operations (/operations)โ€‹

System-health + per-user-quota + recent-rejection dashboard.

Health checks (auto-refresh every 30s)โ€‹

ServiceEndpointWhat "healthy" means
Cloud Functionshttps://europe-west1-acesense-prod.cloudfunctions.net/health200 with {status, region, timestamp}
REST APIhttps://acesense-prod-api.web.app/v1/healthz200 with {status: "ok"}
MCP serverhttps://acesense-prod-api.web.app/mcp POST tools/listresponse includes invalid api key (proves reachable) or 200

Each card shows green/red dot + last-checked timestamp.

Per-user quota tableโ€‹

Reads users/{uid}.usage for every user. Displays today's uploadCount, concurrent, plan tier, and computed daily/concurrency limits:

  • free: 3/day + 1 concurrent
  • pro: 50/day + 3 concurrent
  • team: 200/day + 5 concurrent

Toggle "Approaching limit" filters to users at โ‰ฅ 80% utilization on either axis.

Recent quota_exceeded rejectionsโ€‹

Lists jobs with errorCode: 'quota_exceeded' from the last 7 days. Useful for spotting users hitting tier caps and candidates for plan upgrades.

RunPod fleetโ€‹

Placeholder card with a button linking to https://www.runpod.io/console/serverless. Live RunPod metrics require RunPod's API key โ€” see Settings.


๐Ÿ“œ Audit log (/audit-log)โ€‹

Three tabs over the audit Firestore collections.

Signed URL auditโ€‹

signed_url_audit/ โ€” every signed download URL minted for RunPod (or any consumer).

Columns: issuedAt, expiresAt, jobId (link), userId (link), consumer, urlHash (truncated SHA-256), status (active / revoked).

Per-row Revoke button โ†’ calls adminRevokeSignedUrl({ auditId }) to flag the row revoked. (The URL itself can't be physically revoked once minted; the audit row tracks intent + supports future bucket-level rotation.)

Moderation auditโ€‹

moderation_audit/ โ€” every Cloud Vision SafeSearch decision on a job's first frame.

Columns: decidedAt, jobId, userId, verdict (allow / skipped / csam_suspected / adult / violence / racy_high), reason, reviewedBy.

Click any row โ†’ modal with the raw safeSearchAnnotations SafeSearch scores.

Admin actionsโ€‹

admin_audit/ โ€” every admin-callable invocation (plan changes, suspensions, balance adjustments, retries, revocations).

Columns: at, adminEmail, action, target, payload (truncated; click to expand to JSON modal).

Common featuresโ€‹

  • Per-tab date-range filter (24h / 7d / 30d / all)
  • Free-text search
  • CSV export

๐Ÿ”‘ API Keys (Admin) (/admin-api-keys)โ€‹

Cross-user view of every api_keys/ document โ€” different from /developer which is per-user.

Aggregate stat cardsโ€‹

  • Total active keys
  • Total balance across all keys (EUR)
  • Total spent ever (EUR)
  • Top-spending key (name + amount)

Tableโ€‹

Columns: keyId (masked), userId (link), name, plan, balance (EUR), spentTotal (EUR), rateLimit (req/min), lastUsedAt, status (active / revoked).

Per-row actionsโ€‹

  • Adjust balance โ€” modal with EUR delta + reason text โ†’ calls adminAdjustApiKeyBalance({ keyId, deltaCents: Math.round(eur*100), reason }). Transactional, clamped to โ‰ฅ 0, audit row in api_keys/{keyId}/balance_adjustments/{autoid}.
  • Revoke โ€” confirmation, then sets revokedAt: serverTimestamp() directly via Firestore updateDoc.

Filtersโ€‹

  • Status (active / revoked / all)
  • Plan (all / free / pro / team)
  • Search

Bulkโ€‹

CSV export of selected/all rows.


๐Ÿš€ Developer (/developer)โ€‹

End-user-facing API key management for the signed-in admin's own account.

  • Stat cards: active keys, total balance, total spent
  • Create API key (one-time fullKey reveal with copy-to-clipboard + warning)
  • Balance changes are admin-managed through adminAdjustApiKeyBalance
  • Revoke

๐Ÿ’พ Storage (/storage)โ€‹

File browser over Firebase Storage. List, navigate, delete.


๐Ÿ“ˆ Analytics (/analytics)โ€‹

Charts: jobs over time, processing-time trend, status distribution, peak hours, top users, file size + resolution breakdown.


โš™๏ธ Settings (/settings)โ€‹

System info, admin email allowlist, platform configuration. Read-only summary of:

  • Firestore rules isAdmin() allowlist
  • Firebase project + region
  • Function deploy state (recent timestamps)

To add/remove admins: update isAdmin() in acesense-frontend/firestore.rules and redeploy rules.


๐Ÿ”ฌ Research Decisions (/research-decisions)โ€‹

Internal log of compliance-driven product decisions. See decisions/.


๐ŸŽน Keyboardโ€‹

Cmd+K (Mac) / Ctrl+K opens the command palette with quick-nav to every page above.


๐Ÿ›ก๏ธ Backend admin callablesโ€‹

Every "admin action" button in the panel routes through one of these Cloud Functions in acesense-auth-function/admin/index.ts:

CallablePurposeAudit
adminRetryJobRe-dispatch a failed/stalled job to RunPodadmin_audit/{autoid}
adminSetUserPlanChange users/{uid}.plan + custom claimadmin_audit/{autoid}
adminSetUserSuspendedFlip isActive + Firebase Auth disabled + revoke sessionsadmin_audit/{autoid}
adminAdjustApiKeyBalanceCredit / debit prepaid balance, transactional, clamped to โ‰ฅ 0admin_audit/{autoid} + api_keys/{keyId}/balance_adjustments/{autoid}
adminRevokeSignedUrlFlag a signed-URL audit row revokedadmin_audit/{autoid}
sendPasswordResetCustomSend branded password-reset email via Resend(existing)
exportUserDataGDPR data export โ€” generates ZIP and returns signed URL(existing)

Each is gated by requireAdmin(request) in shared/admin-auth.ts โ€” same allowlist as the Firestore rule.