Admin Dashboard
The Admin Dashboard provides site-wide administration capabilities for managing users, teams, and viewing usage analytics.
Configuration
Site administrators are configured via the admin section in config/app.config.ts:
admin: {
emails: [
'admin@example.com',
'another-admin@example.com',
],
}
Users whose email addresses are in this list will see the "Admin" link in the sidebar and have access to all admin features.
Admin Access
Admin access is granted if the user meets either condition:
- Their email is listed in
config.admin.emails(case-insensitive) - They have
isAdmin: truein the database (legacy support)
Admin Pages
Dashboard (/admin)
The main admin dashboard displays:
- Stats Cards: Total users, teams, threads, messages, and recent activity
- Usage Chart: Visual chart showing messages and token usage over time
- Period selector: 7, 30, or 90 days
- Metric selector: Messages, Input Tokens, Output Tokens, Total Tokens
- Summary row with total, daily average, and peak day
- Plan Distribution: Breakdown of users by subscription plan
- Recent Feedback: Latest user feedback on AI responses
Navigation links to Manage Users and Manage Teams (if teams enabled).
User Management (/admin/users)
Lists all users in the system with:
| Column | Description |
|---|---|
| User | Name, email, avatar, OAuth provider |
| Plan | Dropdown to change user's subscription plan |
| Messages | Message count this month |
| Teams | Clickable pills showing team memberships with roles |
| Admin | Toggle button to grant/revoke database admin flag |
| Joined | Account creation date |
Features:
- Search: Filter users by email or name
- Pagination: Navigate through large user lists
- Team Links: Click a team pill to view that team's details
Team Management (/admin/teams)
Lists all teams in the system (only visible if teams are enabled):
| Column | Description |
|---|---|
| Team | Team name and avatar |
| Members | Number of team members |
| Threads | Number of team threads |
| Created | Team creation date |
Features:
- Search: Filter teams by name
- Pagination: Navigate through large team lists
- Click to View: Click any team row to see team details
Team Details (/admin/teams/:teamId)
Displays detailed information about a specific team:
- Stats: Member count, thread count, creation date
- Team Context: The AI context configured for team conversations
- Members List: All team members with:
- Name and email (clickable to search in user management)
- Role badge (owner, admin, member, viewer)
- Join date
Waitlist (/admin/waitlist)
Provides a basic waitlist table with invite actions.
Features:
- List: Email, name, status, created date
- Invite: Generates a single-use invite token and optionally emails it
Promo Codes (/admin/promo-codes)
Provides a lightweight promo code manager.
Features:
- Create: Code, credits, max uses, start/end window, credit expiry
- Search: Filter by code
- Status filter: Active, scheduled, expired
- Deactivate/Reactivate: Toggle availability by setting
endsAt - Copy: Copy promo code to clipboard
API Endpoints
All admin endpoints require authentication and admin access.
Stats & Analytics
| Method | Endpoint | Description |
|---|---|---|
GET | /api/admin/stats | Dashboard statistics |
GET | /api/admin/usage | Usage data over time |
GET | /api/admin/feedback | Feedback statistics and recent items |
Usage Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
days | number | 30 | Number of days of data (1-365) |
User Management
| Method | Endpoint | Description |
|---|---|---|
GET | /api/admin/users | Paginated user list |
PATCH | /api/admin/users/:userId | Update user (plan, isAdmin) |
User List Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number |
pageSize | number | 20 | Users per page (max 100) |
search | string | - | Filter by email or name |
User Update Body:
{
isAdmin?: boolean; // Grant or revoke admin status
plan?: string; // Change subscription plan
}
Team Management
| Method | Endpoint | Description |
|---|---|---|
GET | /api/admin/teams | Paginated team list |
GET | /api/admin/teams/:teamId | Team details with members |
Team List Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number |
pageSize | number | 20 | Teams per page (max 100) |
search | string | - | Filter by team name |
includeArchived | boolean | false | Include archived teams |
Waitlist
| Method | Endpoint | Description |
|---|---|---|
GET | /api/admin/waitlist | List waitlist entries |
POST | /api/admin/waitlist/:id/invite | Invite waitlist entry |
Promo Codes
| Method | Endpoint | Description |
|---|---|---|
GET | /api/admin/promo-codes | List promo codes |
POST | /api/admin/promo-codes | Create promo code |
PATCH | /api/admin/promo-codes/:id | Update promo code |
Response Types
AdminStats
interface AdminStats {
totalUsers: number;
totalTeams: number;
totalThreads: number;
totalMessages: number;
planDistribution: Record<string, number>;
newUsersLast30Days: number;
messagesLast30Days: number;
}
UsageDataPoint
interface UsageDataPoint {
date: string; // "2024-01-15"
messages: number; // Count of messages
inputTokens: number; // Total input tokens
outputTokens: number;// Total output tokens
}
AdminUser
interface AdminUser {
id: string;
email: string;
name?: string | null;
avatarUrl?: string | null;
isAdmin: boolean;
plan: string;
messagesThisMonth: number;
credits: number;
emailVerified: boolean;
oauthProvider?: string | null;
createdAt: Date;
threadCount: number;
teamCount: number;
teams: AdminUserTeam[];
}
interface AdminUserTeam {
id: string;
name: string;
role: string;
}
AdminTeam / AdminTeamDetails
interface AdminTeam {
id: string;
name: string;
memberCount: number;
threadCount: number;
createdAt: Date;
archivedAt?: Date | null;
}
interface AdminTeamDetails extends AdminTeam {
context?: string | null;
members: AdminTeamMember[];
}
interface AdminTeamMember {
id: string;
email: string;
name?: string | null;
avatarUrl?: string | null;
role: string;
joinedAt: Date;
}
Security Considerations
- Config-based access: Admin emails are defined in server configuration, not user-editable
- Case-insensitive matching: Email comparison is case-insensitive
- Self-protection: Users cannot remove their own admin status
- Audit trail: All user changes are logged with timestamps
- No destructive actions: Admin can modify users but cannot delete accounts (preserves data integrity)
Extending Admin Features
To add custom admin functionality:
- Add API endpoints in
packages/server/src/api/admin.ts - Add types in
packages/shared/src/types/admin.ts - Create pages in
packages/client/src/pages/Admin*.tsx - Add routes in
packages/client/src/App.tsxwrapped with<AdminRoute>
Example adding a new admin page:
// App.tsx
<Route
path="/admin/custom"
element={
<AdminRoute>
<AdminCustomPage />
</AdminRoute>
}
/>
The AdminRoute component handles authentication and admin access verification.