Files
new-api-analytics/CLAUDE.md
shangzy 20a3d399d9 fix: resolve all eslint errors (set-state-in-effect, nested components, no-explicit-any)
- Wrap synchronous setState calls in useEffect with startTransition to avoid cascading renders
- Convert nested SortIcon components to renderSortIcon helper functions
- Replace all `any` types with proper interfaces (OverviewData, TrendPoint, RankItem)
- Remove unused formatTokens import in logs page
- Add no-any rule to CLAUDE.md
2026-04-07 15:19:10 +08:00

3.0 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

@AGENTS.md

Commands

npm run dev      # Start dev server (localhost:3000)
npm run build    # Production build (standalone output)
npm run start    # Start production server
npm run lint     # ESLint (eslint-config-next/core-web-vitals + typescript)

Docker deployment: port 8019, docker-compose up with .env.production, external network sinobridge.

Architecture

Next.js 16 App Router analytics dashboard for an API gateway. React 19, TypeScript strict mode, Tailwind CSS 4, Recharts 3, PostgreSQL.

TypeScript: Never use any type. Always define proper interfaces/types for all data structures. Use unknown with type narrowing if the type is truly unknown.

Three Global Contexts (wrapped in components/ClientProviders.tsx)

  1. ThemeProvider (lib/theme.tsx) — light/dark/system, localStorage theme, supports iframe embedding via ?theme= query param and postMessage sync
  2. I18nProvider (lib/i18n.tsx) — zh/en, localStorage locale, all translation keys in a single translations object
  3. TimeRangeProvider (lib/time-range-context.tsx) — today/7d/30d/all/custom, localStorage time-range, exposes getEffectiveRange() returning { start?, end? } unix timestamps (seconds)

Pages consume these via useTheme(), useI18n(), useTimeRange(). The TimeRangeSelector component reads from context (no props).

Data Flow

Pages call getEffectiveRange()buildQuery("/api/...", { start, end }) → API route → lib/queries.ts SQL → PostgreSQL → JSON response → Recharts/tables.

Database Layer (lib/db.ts, lib/queries.ts)

  • pg pool (max 10 connections), env var PG_CONNECTION_STRING
  • All queries filter type = 2, timezone Asia/Shanghai
  • Real model name resolution: COALESCE(other::jsonb->>'upstream_model_name', model_name)
  • Quota to USD: quota / 500000
  • Parameterized queries with $N positional params

API Routes (all GET, query-param driven)

Route Key Params Returns
/api/overview start, end Aggregate stats (calls, tokens, users, models, channels)
/api/trends start, end, granularity (day/week/month) Time-series array
/api/rankings start, end, type (user/model/channel), limit Ranked items
/api/detail/[type]/[id] start, end Stats + breakdown (models or users)
/api/logs start, end, page, page_size, username, model, token_name Paginated logs
/api/aggregation start, end All users aggregated (top 500)

Styling

CSS variables in globals.css — dark theme default (cyan #00e5ff accent), light theme (blue #0284c7). Key classes: .glass (glassmorphic card), .gradient-text, .btn-accent, .input-glass, .row-glow. Portal page (/portal) has its own extensive animation system.

Path Alias

@/* maps to project root (tsconfig).

Fonts

Outfit (sans, --font-geist-sans) and JetBrains Mono (mono, --font-geist-mono) via next/font/google.