diff --git a/src/app/api/stats/route.ts b/src/app/api/stats/route.ts new file mode 100644 index 0000000..655be8e --- /dev/null +++ b/src/app/api/stats/route.ts @@ -0,0 +1,50 @@ +import { NextResponse } from 'next/server'; +import type { SystemStats } from '@/lib/stats'; + +const IS_PRODUCTION = process.env.NODE_ENV === 'production'; +const STATS_API_URL = 'http://192.168.1.3:9876/stats'; + +async function fetchStats(): Promise { + if (IS_PRODUCTION) { + const response = await fetch(STATS_API_URL, { + cache: 'no-store', + signal: AbortSignal.timeout(5000), + }); + + if (!response.ok) { + throw new Error(`Stats API error: ${response.status}`); + } + + return await response.json(); + } else { + // Development: use SSH to read /proc on NUC + const { exec } = await import('child_process'); + const { promisify } = await import('util'); + const execAsync = promisify(exec); + + const { stdout } = await execAsync( + 'ssh nuc "curl -s http://localhost:9876/stats"', + { timeout: 10000 } + ); + + return JSON.parse(stdout.trim()); + } +} + +export async function GET() { + try { + const stats = await fetchStats(); + + return NextResponse.json(stats, { + headers: { + 'Cache-Control': 'no-cache, no-store, must-revalidate', + }, + }); + } catch (error) { + console.error('Error fetching stats:', error); + return NextResponse.json( + { error: 'Failed to fetch stats', details: String(error) }, + { status: 500 } + ); + } +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 00d8b97..ffa9dbd 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,13 +1,13 @@ 'use client'; -import { Header, SearchBar, ServiceCard, BookmarkCard, CategorySection, Icon, DeploymentsTable } from '@/components'; +import { Header, SearchBar, ServiceCard, BookmarkCard, CategorySection, Icon, DeploymentsTable, OverviewTab } from '@/components'; import { usePortal } from '@/lib/PortalContext'; import { categoryLabels, categoryOrder, bookmarkCategoryLabels, bookmarkCategoryOrder, ServiceCategory, BookmarkCategory } from '@/lib/services'; -type TabId = 'services' | 'bookmarks' | 'ai' | 'whyrating' | 'deployments' | 'settings'; +type TabId = 'overview' | 'services' | 'bookmarks' | 'ai' | 'deployments' | 'settings'; const tabs: { id: TabId; label: string; icon: string }[] = [ - { id: 'whyrating', label: 'WhyRating', icon: 'whyrating' }, + { id: 'overview', label: 'Overview', icon: 'layout' }, { id: 'services', label: 'Services', icon: 'server' }, { id: 'deployments', label: 'Deployments', icon: 'rocket' }, { id: 'ai', label: 'AI', icon: 'bot' }, @@ -27,10 +27,6 @@ const aiTools = [ { name: 'Together AI', url: 'https://together.ai', icon: 'users', description: 'Open model inference' }, ]; -const whyratingLinks = [ - { name: 'WhyRating Hub', url: 'http://whyrating.nuc.lan', icon: 'whyrating', description: 'WhyRating project hub and quick links' }, -]; - export default function Home() { const { filteredServices, @@ -220,39 +216,8 @@ export default function Home() { ); - case 'whyrating': - return ( -
-
-

- WhyRating.com -

-

- Project resources and documentation -

-
-
- {whyratingLinks.map(link => ( - -
- -
-
-

{link.name}

-

{link.description}

-
- -
- ))} -
-
- ); + case 'overview': + return ; case 'deployments': return ( @@ -359,6 +324,7 @@ export default function Home() {
+ {renderTabContent()}
diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 9759072..c7a5365 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -2,6 +2,7 @@ import { usePortal } from '@/lib/PortalContext'; import { Icon } from './Icons'; +import { VitalsBar } from './VitalsBar'; interface Tab { id: string; @@ -69,6 +70,9 @@ export function Header({ activeTab, onTabChange, tabs }: HeaderProps) { + {/* Vitals */} + + {/* Tabs */}