Skip to main content

Overview

The Kuest admin panel provides comprehensive tools for managing your prediction market platform. Access it at /admin (requires admin wallet authorization).

Admin Dashboard Structure

// Admin layout from src/app/[locale]/admin/layout.tsx
<AdminLayout>
  <AdminHeader />  {/* Top navigation */}
  <AdminSidebar /> {/* Left navigation menu */}
  <Content />      {/* Main content area */}
</AdminLayout>
The admin sidebar provides access to all management sections:
  • General Settings: Platform identity and integrations
  • Theme Settings: Visual customization
  • Locales: Multi-language configuration
  • Affiliate Program: Referral system management
  • Market Context: AI-powered market insights
  • Users: User management and statistics
  • Events: Market visibility and sync control
  • Categories: Tag and category management
  • Create Event: Market creation interface

Admin Features

General Settings

Configure core platform settings at /admin (general settings page).

Brand Identity

1

Company Logo

Upload your platform logo:Supported formats:
  • SVG (recommended, scalable)
  • PNG, JPG, WebP (raster images)
Logo modes:
  • image: Use uploaded raster image
  • svg: Use SVG code (stored in database)
// Logo configuration
interface ThemeSiteSettings {
  logoMode: 'image' | 'svg'
  logoSvg: string           // SVG code
  logoImagePath: string     // S3/Supabase storage path
  logoImageUrl: string      // Public URL
}
2

Company Name & Description

Set your platform identity:
  • Company Name: Displayed in headers, wallet dialogs (max 80 chars)
  • Company Description: Used in metadata, SEO (max 180 chars)
siteName: "Your Market Platform"
siteDescription: "Decentralized prediction markets powered by Kuest"
3

PWA Icons

Configure Progressive Web App icons for mobile installation:
  • Icon 192x192: Standard app icon
  • Icon 512x512: High-resolution icon
Used by browser install prompts and home screen shortcuts.

Community and Analytics

Track platform usage with Google Analytics 4:
googleAnalyticsId: "G-XXXXXXXXXX"
The platform automatically initializes GA4 when configured.
Link to your Discord server:
discordLink: "https://discord.gg/your-invite"
Displayed in navigation and footer.

OpenRouter Integration

Configure AI-powered features:
// From AdminGeneralSettingsForm.tsx
interface OpenRouterSettings {
  apiKey: string              // OpenRouter API key
  model: string               // Preferred model (e.g., "perplexity/sonar")
  isApiKeyConfigured: boolean // Key is set
  modelOptions: Array<{
    id: string
    label: string
    contextWindow?: number
  }>
}
Features powered by OpenRouter:
  • AI-generated resolution rules
  • Automatic event translations
  • Market context generation
  • Content validation
1

Get API Key

Generate an API key at openrouter.ai/settings/keys
2

Configure Key

Enter your API key in the General Settings form. The platform never displays the full key after saving (shows ••••••••••••••••).
3

Select Model

Choose your preferred model:
  • Let OpenRouter decide: Automatic selection (default)
  • Specific model: Choose from available models
Recommended: Models with live browsing (e.g., perplexity/sonar) for best market context generation.
4

Refresh Models

Click the refresh button to fetch the latest available models from OpenRouter.

LI.FI Integration

Configure cross-chain swap and bridge functionality:
interface LiFiSettings {
  lifiIntegrator: string  // Your app ID from li.fi
  lifiApiKey: string      // API key (optional, increases rate limits)
}
Default Limits:
  • Without API key: 200 requests per 2 hours
  • With API key: 200 requests per minute (rolling 2-hour window)
Generate credentials at li.fi.

Market and Fee Settings

Set the Polygon wallet address to receive transaction fees:
feeRecipientWallet: "0xYourPolygonAddress"
This address receives platform fees from market trades.
Configure which wallet addresses can create markets:
// One address per line
marketCreators: `
0xCreator1Address
0xCreator2Address
0xCreator3Address
`
Important: Markets from these addresses only appear on your fork. Leave empty to show only main Kuest markets.

User Management

Manage platform users at /admin/users.

User Table

View and filter users:
// User data structure
interface User {
  id: string
  address: string           // EVM wallet address
  username: string | null
  email: string | null
  email_verified: boolean
  is_admin: boolean
  created_at: Date
  updated_at: Date
  // Trading statistics
  total_volume?: number
  total_trades?: number
}
Available Actions:
  • Search by address or username
  • Filter by admin status
  • Sort by registration date, volume, trades
  • View user details

User Statistics

The table displays:
  • Total registered users
  • Active traders (users with trades)
  • Total trading volume
  • Admin user count

Event Management

Control market visibility and sync settings at /admin/events.

Event Table Features

Visibility Control

Show or hide events from the platform:
await updateEventVisibilityAction(eventId, isHidden)
Hidden events are not visible to users but remain in the database.

Sync Settings

Configure automatic event syncing:
await updateEventSyncSettings(eventId, {
  autoSync: boolean,
  syncInterval: number
})

Sports Finalization

Mark sports events as final:
await updateEventSportsFinalState(eventId, isFinal)
Prevents further updates to sports market scores.

Livestream URL

Add livestream links to events:
await updateEventLivestreamUrl(eventId, url)
Displayed on event pages for user engagement.

Auto-Deploy New Events

Toggle automatic deployment of synced events:
const autoDeployNewEventsEnabled = await loadAutoDeployNewEventsEnabled()

// When enabled, newly synced events from Kuest API
// are automatically made visible on your platform

Event Filters

Filter by:
  • Main category (Politics, Sports, Crypto, etc.)
  • Status (active, resolved, archived, draft)
  • Visibility (hidden/visible)
  • Date range
Search:
  • Event title
  • Event slug
  • Market questions

Category Management

Manage tags and categories at /admin/categories.

Category Operations

1

View Categories

The table displays:
  • Category name and slug
  • Associated main category
  • Event count
  • Creation date
2

Update Categories

Edit category properties:
await updateCategory(categoryId, {
  name: string,
  slug: string,
  mainCategorySlug: string
})
3

Manage Translations

Add category translations for internationalization:
await updateCategoryTranslations(categoryId, {
  en: "English Name",
  es: "Nombre en Español",
  fr: "Nom en Français"
})

Main Categories

Main categories organize events into broad topics:
interface MainCategory {
  id: number
  name: string
  slug: string
  childs: Array<{  // Subcategories
    name: string
    slug: string
  }>
}
Fetch via API: GET /admin/api/main-tags

Theme Settings

Customize platform appearance at /admin/theme. Customization Options:
  • Color schemes
  • Typography
  • Layout spacing
  • Component styling
  • Dark/light mode defaults

Locale Configuration

Manage supported languages at /admin/locales. Features:
  • Enable/disable languages
  • Set default locale
  • Configure locale-specific settings
  • Manage translations

Affiliate Program

Configure referral system at /admin/affiliate. Settings:
  • Commission rates
  • Payout thresholds
  • Referral tracking
  • Affiliate dashboard access

Market Context Settings

Configure AI-powered market insights at /admin/market-context.
interface MarketContextSettings {
  apiKey: string          // OpenRouter API key
  model: string           // AI model to use
  systemPrompt: string    // Context generation prompt
  maxTokens: number       // Response length limit
}
Market context provides users with:
  • Market background information
  • Historical data
  • Related events
  • AI-generated insights

Admin API Endpoints

The admin panel uses dedicated API routes:
// User management
GET  /admin/api/users
POST /admin/api/users/[id]/update

// Event management
GET  /admin/api/events
POST /admin/api/events/check-slug

// Category management
GET  /admin/api/categories
GET  /admin/api/main-tags

// Market creation
GET  /admin/api/create-event/allowed-creators
POST /admin/api/create-event/ai

// OpenRouter integration
POST /admin/api/openrouter-models

Data Tables

Admin tables use a consistent pattern:
// Table component structure
<DataTable
  columns={columns}        // Column definitions
  data={data}             // Table data
  searchPlaceholder="..." // Search field
  filters={filters}       // Filter controls
  pagination={pagination} // Page controls
/>

// Column definition example
const columns = [
  {
    accessorKey: 'title',
    header: 'Event Title',
    cell: ({ row }) => (
      <a href={`/event/${row.original.slug}`}>
        {row.original.title}
      </a>
    ),
  },
  {
    accessorKey: 'status',
    header: 'Status',
    filterFn: 'equals',
  },
]
Common Features:
  • Sortable columns
  • Searchable fields
  • Filterable values
  • Pagination controls
  • Bulk actions
  • Export capabilities

Server Actions

Admin operations use Next.js server actions:
// Example: Update event visibility
'use server'

export async function updateEventVisibilityAction(
  eventId: string,
  isHidden: boolean
): Promise<UpdateEventVisibilityResult> {
  try {
    // 1. Verify admin access
    const currentUser = await UserRepository.getCurrentUser()
    if (!currentUser || !currentUser.is_admin) {
      return {
        success: false,
        error: 'Unauthorized. Admin access required.'
      }
    }

    // 2. Update database
    const { data, error } = await EventRepository.setEventHiddenState(
      eventId,
      isHidden
    )
    if (error || !data) {
      return { success: false, error: error ?? 'Update failed' }
    }

    // 3. Revalidate caches
    revalidatePath('/[locale]/admin/events', 'page')
    updateTag(cacheTags.eventsGlobal)
    updateTag(cacheTags.event(data.slug))

    return { success: true, data }
  } catch (error) {
    console.error('Server action error:', error)
    return { success: false, error: 'Internal server error' }
  }
}
Action Pattern:
  1. Authenticate request
  2. Validate input
  3. Perform database operation
  4. Invalidate relevant caches
  5. Return result

Security and Authorization

Admin Access Control

Admin access is controlled via environment variable:
# .env
ADMIN_WALLETS="0xAddress1,0xAddress2,0xAddress3"
// Authorization check
const currentUser = await UserRepository.getCurrentUser()
const isAdmin = currentUser?.is_admin === true

if (!isAdmin) {
  return { error: 'Unauthorized. Admin access required.' }
}

Route Protection

All admin routes are protected:
// Middleware checks admin status
export async function middleware(request: Request) {
  const user = await getCurrentUser()
  
  if (request.url.startsWith('/admin')) {
    if (!user?.is_admin) {
      return Response.redirect('/login')
    }
  }
  
  return NextResponse.next()
}

Best Practices

Regular Backups

Back up your database regularly, especially before bulk operations.

Test Changes

Test admin actions on development/staging before production.

Monitor Actions

Review admin action logs in Sentry or your monitoring tool.

Limit Admin Access

Only add trusted wallet addresses to ADMIN_WALLETS.

Troubleshooting

Cannot Access Admin Panel

Issue: /admin redirects to home page. Solution:
  1. Verify your wallet address is in ADMIN_WALLETS environment variable
  2. Ensure you’re connected with the correct wallet
  3. Check that your user record has is_admin = true in the database
  4. Clear browser cache and reconnect wallet

Settings Not Saving

Issue: Form submits but changes don’t persist. Solution:
  1. Check browser console for errors
  2. Verify database connection
  3. Check server action logs
  4. Ensure proper permissions on storage (S3/Supabase)
  5. Review Sentry error logs

Slow Table Loading

Issue: Admin tables take a long time to load. Solution:
  1. Add database indexes on frequently queried columns
  2. Implement pagination (already built-in)
  3. Use table filters to reduce result size
  4. Cache frequently accessed data
  5. Optimize database queries

Image Upload Fails

Issue: Logo or icon upload returns an error. Solution:
  1. Check file size (recommended: < 2MB)
  2. Verify file format (PNG, JPG, WebP, SVG)
  3. Ensure S3/Supabase credentials are correct
  4. Check storage bucket permissions
  5. Verify S3_PUBLIC_URL or SUPABASE_URL is accessible

Market Creation

Create and manage prediction markets

Monitoring

Track platform health and performance

Environment Config

Configure environment variables

Database Schema

Understand the database structure