Files
kikootwo eef6ae3462 Add admin system logs UI and API support
Introduce a complete admin System Logs feature: adds frontend components (filters, date picker, active filter chips, rows, detail panel, skeletons, pagination, toolbar, user typeahead, and styles) under src/app/admin/logs/components, plus hooks (useAutoRefreshControl, useLogsUrlState, useUserSearch) and types. Add constants for job labels and log filters, wire URL-driven filters/search/date-range/hasError/user/audiobookQuery with pause-on-interact behavior and page-size options. Update API route (/api/admin/logs) to support the expanded query params and exported where-builder. Update documentation (TABLEOFCONTENTS and admin-dashboard) and add/adjust tests for the new admin logs UI and API behavior.
2026-05-18 08:29:32 -04:00

7.0 KiB

Admin Dashboard

Status: Implemented

Comprehensive overview of system metrics, active requests, download monitoring, and quick access to settings.

Sections

  • Metrics: Total requests, active downloads, completed/failed requests, total users, system health
  • Requests Awaiting Approval: Grid of requests pending admin approval (approve/deny buttons, auto-refresh)
  • Active Downloads: Real-time table with title, progress, speed, ETA
  • Request Management: Full-featured table with filtering, sorting, pagination
  • Quick Actions: Links to settings, users, scheduled jobs, system logs

Data Sources

GET /api/admin/metrics

  • Total requests (all time)
  • Active downloads (status: 'downloading')
  • Completed requests (status: 'downloaded' or 'available', last 30 days)
  • Failed requests (status: 'failed', last 30 days)
  • Total users
  • System health indicators

GET /api/admin/downloads/active

  • Request ID, title, progress %, speed, ETA, user

GET /api/admin/requests (Paginated)

  • Query params: page, pageSize (10|25|50|100), search, status, userId, sortBy, sortOrder
  • Returns: requests[], total, page, pageSize, totalPages
  • Sorting: createdAt (default), completedAt, title, user, status
  • Filtering: by status, by user, text search (title/author)

GET /api/admin/requests/recent (Legacy)

  • Request ID, title, user, status, created/completed dates
  • Limited to 50 entries, no filtering

GET /api/admin/requests/pending-approval

  • Requests with status 'awaiting_approval', includes audiobook + user details
  • Returns: requests array, count

POST /api/admin/requests/[id]/approve

  • Action: 'approve' (set status to 'pending', trigger search) or 'deny' (set status to 'denied')
  • Validates request is in 'awaiting_approval' status

GET /api/admin/users

  • User ID, Plex ID, username, email, role, avatar, created/updated dates, last login, request count, autoApproveRequests

PUT /api/admin/users/[id]

  • Update user role (user/admin), autoApproveRequests (true/false/null)
  • Prevents self-demotion

GET /api/admin/settings/auto-approve

  • Get global auto-approve setting (boolean)

PATCH /api/admin/settings/auto-approve

  • Update global auto-approve setting (boolean)

GET /api/admin/logs

  • Query params: page, limit, status, type, search, dateFrom, dateTo, hasError, userId, audiobookQuery
  • limit: one of 25/50/100 (default 50; invalid values clamp to 50)
  • status: 'all' or one of pending/active/completed/failed/delayed/stuck
  • type: 'all' or any job type key
  • dateFrom / dateTo: ISO UTC strings; invalid dates silently dropped
  • hasError: 'true' or '1' → status in (failed, stuck) OR errorMessage IS NOT NULL
  • userId: uuid → filters via request.userId
  • audiobookQuery: free text → OR-contains (case-insensitive) on request.audiobook.{title,author}
  • search: free text → 6-column OR: bullJobId (startsWith, case-sensitive), errorMessage (contains-i), events.some.message (contains-i), request.audiobook.title/author (contains-i), request.user.plexUsername (contains-i)
  • hasError + search combine under top-level AND; other filters compose via AND on where
  • Where-builder: exported buildLogsWhere(params) in route file (pure, testable)
  • Returns: { logs, pagination: { page, limit, total, totalPages } }

Request Management Features

  • Filter Bar:
    • Text search (title/author, 300ms debounce)
    • Status dropdown (all statuses)
    • User dropdown (all users)
    • Clear filters button
  • Sortable Columns: Click headers to sort by title, user, status, requested, completed
  • Pagination: Page navigation, page size selector (10/25/50/100), results count
  • URL State: Filters/sort/page stored in URL query params (shareable, bookmarkable)
  • Actions: Delete, cancel, manual search, fetch ebook (via dropdown)

Features

  • Auto-refresh every 10 seconds (SWR)
  • Back to Home button in header
  • Admin role required
  • Real-time progress updates
  • Requests Awaiting Approval Section:
    • Only visible when pending approval requests exist
    • Grid layout (3 columns on desktop)
    • Book cards with cover, title, author, user info, timestamp
    • Approve (green) and Deny (red) buttons
    • Loading states during approval/denial actions
    • Toast notifications for success/errors
    • Mutates pending-approval, recent requests, metrics caches on action

Navigation

  • /admin/jobs - Scheduled jobs management (trigger, edit schedule, enable/disable)
  • /admin/settings - System settings (Plex, Prowlarr, paths)
  • /admin/users - User management (view users, change roles)
  • /admin/logs - System logs (view job history, errors, filter by status/type)

User Management Features

  • List all users with avatar, email, role, request count, last login, autoApproveRequests
  • Edit user roles (user/admin)
  • Cannot change own role (security)
  • Shows request count per user
  • Role badges (purple for admin, gray for user)
  • Global Auto-Approve Toggle:
    • Checkbox at top: "Auto-approve all requests by default"
    • Updates Configuration.auto_approve_requests
  • Per-User Auto-Approve Control:
    • Dropdown: Use Global (null), Always Auto-Approve (true), Always Require Approval (false)
    • Updates User.autoApproveRequests
    • Shows effective setting (considers global + per-user)

System Logs Features

  • Real-time job monitoring (10s SWR refresh; pauses on interact)
  • Filter row (5 pickers): Status · Job Type · Date Range · User typeahead · Audiobook free-text
    • Status: dropdown over VALID_STATUSES (from src/app/admin/logs/types.ts); labels via STATUS_OPTIONS in src/lib/constants/log-filters.ts
    • Job Type: dropdown over JOB_TYPE_LABELS insertion order (src/lib/constants/job-labels.ts)
    • Date Range: presets (Last hour / 24h / 7d / 30d / Custom / All time) — default = Last 7 days (Zach #1); Custom uses <input type="datetime-local"> rendered as local time, wired as UTC ISO
    • User: typeahead via useUserSearch (fetch-once from /api/admin/users, SWR-cached, in-memory filter, max 10 suggestions); selection sets userId = User.id
    • Audiobook: free-text → server-side OR-contains on title/author (Zach #4 — no picker)
  • Active filter chips: dismissable <button aria-label="Remove filter: X"> strip; NOT sticky (Zach #6 — scrolls with content). Errors-only renders as a chip when active.
  • Clear all filters: visible only when ≥1 filter or the search input is non-default
  • Pause-on-interact reasons (registered to useAutoRefreshControl):
    • logs-status-dropdown, logs-type-dropdown, logs-date-picker, logs-user-typeahead, logs-book-input
  • URL = source of truth via useLogsUrlState (src/app/admin/logs/hooks/); param names exported as LOG_PARAMS; same names used by /api/admin/logs
  • Shows related audiobook/user for request jobs
  • Expandable error messages, duration calc, attempt tracking, Bull job ID
  • Pagination: page-size selector (25 / 50 / 100), default 50

Tech Stack

  • React Server Components + SWR
  • Tailwind CSS
  • Prisma aggregations for metrics
  • Database queries with indexing