Media Library & Operational Logs (Admin)
Audience: Ops · Super-admins · Support · Engineering · Where in app: /admin/call-backgrounds, /admin/call-recordings, /admin/default-videos, /admin/api-request-logs, /admin/email-logs, /admin/logs, /admin/notifications, /admin/webhook-events, /admin/storage/maintenance · Access: Super-admin only (verify — admin-portal routes; the log routes are passport-authed admin-only).
This cluster covers two things. Media library: the platform-default assets shown in product surfaces (call backgrounds, the widget/invitation default videos) and the browsing UI for call recordings. Operational logs: read-only forensics for support and engineering — what API requests came in, what emails went out (and which failed), what errored in the system, what notifications were broadcast, plus the webhook-event registry tenants can subscribe to.
What it does
- Call Backgrounds — upload/delete the default background images offered in calls; mark one default.
- Call Recordings — browse recordings across four sources (Twilio, AI, inbound/outbound AI phone, forwarded), with playback.
- Default Videos — manage the default widget video and the client-invitation demo video (file upload or URL); set the active default.
- API Request Logs — inspect incoming API requests (method, path, platform); 30-day TTL.
- Email Logs — every sent/failed outbound email with full headers and content.
- System Logs — error/info/warning entries with company/session/user context; 30-day TTL.
- Notifications — broadcast an admin notification to companies/users and view history.
- Webhook Events — the catalog of subscribable webhook event types with sample payloads.
- Storage Maintenance — storage stats (Mongo + S3) and dry-run-first session-recording cleanup.
How it works
Frontend repos live under frontend/src/repositories/admin/ and views under frontend/src/views/admin/. Media uploads use multipart form-data (backgrounds) or a chunked/streamed upload with client-side compression (utils/streamUpload, default videos). Recordings are not stored in Mongo directly here — playback URLs point at ms-communication endpoints (Twilio compositions, Ultravox/ElevenLabs AI recordings). Logs are MongoDB collections; two of them (api_requests, logs) carry a 30-day TTL index that auto-purges old rows.
Screens & fields
Call Backgrounds — /admin/call-backgrounds
Source: call-backgrounds/List.vue → frontend/src/repositories/admin/call-backgrounds.js (/default-call-backgrounds). Model DefaultCallBackgroundModel.js (collection default_call_background).
Grid of background image cards; one shows a Default badge. An upload card creates a new background (POST multipart with the image file). Delete per card.
| Field | Notes |
|---|---|
file | ObjectId → files (the stored image) |
default | bool — the active default background |
| timestamps | created_at / updated_at |
Call Recordings — /admin/call-recordings
Source: call-recordings/Index.vue (tabbed) → frontend/src/repositories/admin/call-recordings.js. Four tabs:
| Tab | Source | Repo call / endpoint |
|---|---|---|
| Twilio Recordings | Twilio compositions | getCallRecordings → /call-recordings; playback …/twilio/play-recording/:sid |
| AI Recordings | Ultravox/ElevenLabs AI calls | getAICallRecordings → /ai-call-recordings; playback …/ultravox/play-ai-recording/:callId |
| Inbound & Outbound | ms-communication AI phone calls | getAIPhoneTranscripts → …/inbound-outbound-calls (transcripts + ElevenLabs audio) |
| Forwarded Calls | forwarded calls | getForwardedCalls → /call-forwarded |
Common list filters: page/pageSize, sort/order, start/end date. Helpers: getCallType (video vs audio), formatDuration (→ HH:mm:ss), markCallAsHeard / markAllCallsAsHeard, getUnheardCallsCount, deleteRecording (soft delete), getElevenLabsAudio (blob). The inbound/outbound super-admin view can pass include_deleted=true to show soft-deleted calls; deleteAIPhoneCall soft-deletes (sets deleted_at, company-scoped).
Default Videos — /admin/default-videos
Source: default-videos/List.vue ("Video Templates") → frontend/src/repositories/admin/default-videos.js (/default-video). Model DefaultVideoModel.js (collection defaultvideos).
Category selector drives the view: widget (grid of templates) and client_invitation (a single demo-video URL field). Upload is staged (Uploading → Compressing → Saving) with progress; videos can also be created from a URL (createDefaultVideoUrl). Star icon = Set as Default (setDefaultVideo → PUT /create-default-video/:id).
| Field | Notes |
|---|---|
video_path | stored video path (URL-sourced) |
video_file | ObjectId → files (uploaded file) |
category | widget (default) / client_invitation |
is_default | bool — active default for the category |
has_audio | bool |
is_active | bool, default true |
API Request Logs — /admin/api-request-logs + …/:id
Source: api-request-logs/List.vue + Detail.vue → frontend/src/repositories/admin/api-request-logs.js (/api-request-logs). Controller ApiRequestLogController, model ApiRequestModel.js (collection api_requests).
| Filter | Notes |
|---|---|
| Search | path / method / IP / platform |
| Method | All · GET · POST · PUT · PATCH · DELETE |
| Platform | sortable column/filter |
| Start/End date | range |
List columns: Method, Path, Platform, IP, time. Detail shows the full record:
| Field | Notes |
|---|---|
method, path | |
headers, params, query, body | mixed/JSON, nullable |
ip, user_agent, platform | |
user_id | ref → users |
created_at |
Retention: 30 days — Schema.index({ created_at: -1 }, { expireAfterSeconds: 60*60*24*30 }) (the same index serves date-range sort).
Email Logs — /admin/email-logs + …/:id
Source: email-logs/List.vue + Detail.vue → frontend/src/repositories/admin/email-logs.js (/email-logs). Model EmailLogModel.js (collection email_logs).
| Filter | Notes |
|---|---|
| Search | |
| Sort / order | |
| Start/End date | range |
Status enum: sent / failed (the repo also exposes EMAIL_LOG_STATUS with SENT: 'send' and color helpers — green/sent, red/failed). Fields: from, to, cc, bcc, reply_to (all mixed — string or array), subject, content, status, error (mixed), timestamps. Indexed on {status, created_at} and {created_at}. No TTL — email logs are retained.
System Logs — /admin/logs
Source: logs/List.vue ("System Logs") → frontend/src/repositories/admin/logs.js (/error-logs). Model LogModel.js (collection logs).
Filters: search, sort (Date newest/oldest). A grouped view is available (getGroupedErrorLogs → /error-logs/grouped). List columns include Type, Description, context, Actions (view, delete). Fields:
| Field | Notes |
|---|---|
type | error (default) / info / warning |
description | |
company_id, user_session_id, service_room_id | string context, nullable |
user_type | admin / user (default user) |
created_at |
Indexed on {type, created_at}. Retention: 30 days (TTL on created_at). Individual logs can be deleted (deleteErrorLog).
Storage Maintenance — /admin/storage/maintenance
Source: frontend/src/views/admin/StorageMaintenance.vue → frontend/src/repositories/admin/storage.js. Backend: AdminStorageController (backend/app/routes/adminStorageRoutes.js), user_type === 'admin' enforced per request. Two panels, both background jobs with progress polling:
| Panel | Endpoints | What |
|---|---|---|
| Storage Stats (read-only) | POST /admin/storage/stats + GET /admin/storage/stats/jobs/:job_id | Whole-DB and session_files sizes from Mongo metadata (no collection scan) + live S3 bucket object count/size (paged listing — large buckets take a minute) |
| Session Recordings Cleanup | POST /admin/storage/session-files-cleanup + GET /admin/storage/session-files-cleanup/jobs/:job_id | Deletes session_files docs + their S3/local chunks. Selectors: older_than_days (retention sweep) and/or min_files (runaway kiosk/bot sessions) — at least one required |
Cleanup is proxied to ms-sessions' api-key-gated /migrations/session-files-cleanup; stats are computed in the backend (shares the Mongo DB and S3 bucket). Every action defaults to dry run — review counts, then Apply (confirm dialog; deletion is permanent).
Notifications — /admin/notifications
Source: notifications/Index.vue → frontend/src/repositories/admin/notifications.js. Compose + send an admin broadcast (POST /admin/notifications/send) and view history (GET /admin/notifications). Model AdminNotificationModel.js (collection admin_notifications).
| Field | Notes |
|---|---|
title, message | required content |
target_type | all_companies / companies / all_users / users |
target_ids[] | specific company/user ids when not "all" (typeahead via searchCompanies / searchUsers) |
target_names[] | resolved labels |
sent_by | admin who sent it |
recipients_count | computed fan-out size |
created_at |
Targeting "companies"/"users" reveals a search box to pick specific recipients; "all_companies"/"all_users" broadcasts. (Per-recipient delivered notifications land in the notifications collection — NotificationModel.js.)
Webhook Events — /admin/webhook-events
Source: webhook-events/Index.vue → frontend/src/repositories/admin/webhook-events.js (/webhook-events). Model WebhookEventModel.js (collection webhook_events). The catalog of event types companies can subscribe their webhooks to — not the per-delivery log.
Filter: status. Create/edit via modal:
| Field | Notes |
|---|---|
name | machine key, e.g. new_lead |
label | display, e.g. New Lead |
description | when it fires |
sample_payload | mixed/JSON example shown to subscribers |
status | active / inactive |
created_by | ref → users |
Distinguish from per-delivery webhook logs (
WebhookLogModel.js) and tenant webhook configs (WebhookModel.js) — this screen manages the event-type definitions only.
Behaviors & edge cases
- Retention:
api_requestsandlogsauto-purge after 30 days (TTL index).email_logs,admin_notifications,webhook_events, and media collections are retained until manually deleted. Recording media retention is governed by the upstream provider (Twilio / ElevenLabs), not these screens. - One default per category: call backgrounds have a single
default; default videos have oneis_defaultpercategory(widget vs client_invitation). Setting a new default flips the previous one. - Default-video upload is staged and compressed client-side (Uploading 0–40% → Compressing 40–80% → Saving 80–99%); URL-sourced videos skip compression.
- Recordings are external — playback URLs resolve against ms-communication; a
404usually means the provider purged the asset. Inbound/outbound AI calls are soft-deleted (deleted_at) and only re-appear withinclude_deleted=true. - Notification "all" vs targeted:
all_companies/all_usersignoretarget_ids;companies/usersrequire picked ids.recipients_countreflects the resolved fan-out. - Webhook Events are definitions, not deliveries — editing a sample payload changes documentation/subscription scaffolding, not any past delivery.
- Email logs store full content + headers — treat as sensitive (PII / message bodies).
Technical implementation
- Models:
backend/app/models/ApiRequestModel.js,EmailLogModel.js,LogModel.js,AdminNotificationModel.js,NotificationModel.js,WebhookEventModel.js,DefaultVideoModel.js,DefaultCallBackgroundModel.js. - Routes/controllers:
backend/app/routes/apiRequestLogRoutes.js+ApiRequestLogController; email-log, error-log, notification, webhook-event, default-video, call-background routes/controllers underbackend/app/. - Frontend repos:
frontend/src/repositories/admin/{call-backgrounds,call-recordings,default-videos,api-request-logs,email-logs,logs,notifications,webhook-events}.js; views underfrontend/src/views/admin/(matching folders). - Recording playback is served by ms-communication (Twilio compositions, Ultravox/ElevenLabs AI recordings).