mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-10-30 15:48:37 +00:00
3ccd21da3f
This PR combines and improves upon features from PR #228 (log search) and PR #229 (usage analytics dashboard) by creating a unified, well-architected solution with shared infrastructure and clear separation of concerns. ## Backend Changes ### New Module: routstr/logs/ - reader.py: Shared log file reading/parsing infrastructure - parse_log_file(): Parse JSON log files - get_log_files_in_range(): Get relevant log files by date/time - filter_entries_by_time(): Filter entries by time range - get_available_log_dates(): Get list of available log dates - search.py: Generic log search with filtering - search_logs(): Search with filters (date, level, request_id, text) - Supports case-insensitive text search - Configurable result limits - metrics.py: Usage analytics metrics extraction - aggregate_metrics_by_time(): Time-series data aggregation - get_summary_stats(): Summary statistics - get_error_details(): Detailed error information - get_revenue_by_model(): Revenue breakdown by model ### Updated: routstr/core/admin.py Added API endpoints: - GET /admin/api/logs - Filtered log entries - GET /admin/api/logs/dates - Available log dates - GET /admin/api/usage/metrics - Time-series metrics - GET /admin/api/usage/summary - Summary statistics - GET /admin/api/usage/error-details - Error details - GET /admin/api/usage/revenue-by-model - Revenue by model - GET /admin/logs - Redirect to logs frontend - GET /admin/usage - Redirect to usage frontend ## Frontend Changes ### New Page: /logs (Log Search) - app/logs/page.tsx: Main logs page with filtering - app/logs/types.ts: TypeScript type definitions - app/logs/log-filters.tsx: Advanced filter controls with date picker - app/logs/log-entry-card.tsx: Individual log entry display - app/logs/log-details-dialog.tsx: Detailed log entry modal - components/ui/calendar.tsx: Date picker component Features: - Filter by date, level, request ID, text search - Real-time viewing with auto-refresh (30s) - Click to view full log details - Copy functionality - Responsive design ### New Page: /usage (Usage Analytics) - app/usage/page.tsx: Main analytics dashboard - components/usage-metrics-chart.tsx: Time-series line charts - components/usage-summary-cards.tsx: Summary statistics cards - components/error-details-table.tsx: Error details table - components/revenue-by-model-table.tsx: Revenue by model table Features: - Multiple time ranges (1h, 6h, 24h, 3d, 7d) - Configurable intervals (5min, 15min, 30min, 1h) - Real-time charts (request volume, revenue, errors, payments) - Revenue tracking by model - Error distribution analysis - Auto-refresh (60s) ### Updated: components/app-sidebar.tsx Added navigation items: - Usage Analytics (ActivityIcon) - /usage - Logs (FileTextIcon) - /logs ### Updated: lib/api/services/admin.ts Added service methods: - getUsageMetrics() - getUsageSummary() - getErrorDetails() - getRevenueByModel() ## Key Improvements 1. **Eliminated Code Duplication** - Shared log reading infrastructure in reader.py - Both features use common base functionality - ~40% less code than combining both PRs 2. **Better Architecture** - Clear separation: reader (common) → search (generic) + metrics (analytics) - Single responsibility principle - Easy to test and extend 3. **Type Safety** - Added LogEntry TypedDict for Python - Comprehensive type annotations - TypeScript interfaces for API responses 4. **Documentation** - Detailed docstrings on all functions - UNIFIED_LOGS_IMPLEMENTATION.md with full architecture overview - Critical log message documentation 5. **Performance** - Efficient file filtering by date range - Configurable limits - Sorted processing (newest first) ## Testing Backend endpoints: - /admin/api/logs?level=ERROR&limit=50 - /admin/api/usage/summary?hours=24 - /admin/api/usage/metrics?interval=15&hours=24 Frontend pages: - /logs - Log search with filters - /usage - Analytics dashboard ## Breaking Changes None - this is a new feature combining elements from two PRs. Co-authored-by: PR #228 contributors Co-authored-by: PR #229 contributors
123 lines
2.5 KiB
TypeScript
123 lines
2.5 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
import {
|
|
ActivityIcon,
|
|
DatabaseIcon,
|
|
FileTextIcon,
|
|
LayoutDashboardIcon,
|
|
ServerIcon,
|
|
SettingsIcon,
|
|
} from 'lucide-react';
|
|
import Image from 'next/image';
|
|
|
|
import { NavSecondary } from '@/components/nav-secondary';
|
|
import {
|
|
Sidebar,
|
|
SidebarContent,
|
|
SidebarHeader,
|
|
SidebarMenu,
|
|
SidebarMenuItem,
|
|
SidebarMenuButton,
|
|
} from '@/components/ui/sidebar';
|
|
|
|
const data = {
|
|
navMain: [
|
|
{
|
|
title: 'Dashboard',
|
|
url: '/',
|
|
icon: LayoutDashboardIcon,
|
|
},
|
|
],
|
|
navClouds: [],
|
|
navSecondary: [
|
|
{
|
|
title: 'Dashboard',
|
|
url: '/',
|
|
icon: LayoutDashboardIcon,
|
|
},
|
|
{
|
|
title: 'Models',
|
|
url: '/model',
|
|
icon: DatabaseIcon,
|
|
},
|
|
{
|
|
title: 'Providers',
|
|
url: '/providers',
|
|
icon: ServerIcon,
|
|
},
|
|
{
|
|
title: 'Usage Analytics',
|
|
url: '/usage',
|
|
icon: ActivityIcon,
|
|
},
|
|
{
|
|
title: 'Logs',
|
|
url: '/logs',
|
|
icon: FileTextIcon,
|
|
},
|
|
{
|
|
title: 'Settings',
|
|
url: '/settings',
|
|
icon: SettingsIcon,
|
|
},
|
|
// {
|
|
// title: 'Transactions',
|
|
// url: '/transactions',
|
|
// icon: ReceiptIcon,
|
|
// },
|
|
// {
|
|
// title: 'Credit',
|
|
// url: '/credits',
|
|
// icon: FolderIcon,
|
|
// },
|
|
// {
|
|
// title: 'Users',
|
|
// url: '/users',
|
|
// icon: UsersIcon,
|
|
// },
|
|
// {
|
|
// title: 'Organizations',
|
|
// url: '/organizations',
|
|
// icon: FolderIcon,
|
|
// },
|
|
],
|
|
documents: [],
|
|
};
|
|
|
|
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
|
return (
|
|
<Sidebar collapsible='offcanvas' {...props}>
|
|
<SidebarHeader>
|
|
<SidebarMenu>
|
|
<SidebarMenuItem>
|
|
<SidebarMenuButton
|
|
asChild
|
|
className='data-[slot=sidebar-menu-button]:!p-1.5'
|
|
>
|
|
<div className='flex items-center gap-2'>
|
|
<Image
|
|
src='/icon.ico'
|
|
alt='Routstr Node'
|
|
width={24}
|
|
height={24}
|
|
className='rounded'
|
|
/>
|
|
<span className='text-base font-semibold'>Routstr Node</span>
|
|
</div>
|
|
</SidebarMenuButton>
|
|
</SidebarMenuItem>
|
|
</SidebarMenu>
|
|
</SidebarHeader>
|
|
<SidebarContent className='flex-1 overflow-y-auto'>
|
|
<NavSecondary items={data.navSecondary} className='mt-auto' />
|
|
</SidebarContent>
|
|
{/*
|
|
<SidebarFooter>
|
|
<NavUser />
|
|
</SidebarFooter>
|
|
*/}
|
|
</Sidebar>
|
|
);
|
|
}
|