cc-dashboard/src/schemas.py
Vadym Samoilenko 7b30880d44 Initial commit — CC Dashboard v1.0
Multi-tenant Claude Code monitoring dashboard.
FastAPI + PostgreSQL + Docker + SSE real-time updates.
Montserrat font, black/#FFC407 color scheme.
Apache reverse proxy config at /cc-dashboard/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:54:47 +00:00

194 lines
4.8 KiB
Python

from datetime import date, datetime
from typing import Any
from pydantic import BaseModel, EmailStr, Field
# ── Auth ──────────────────────────────────────────────────────────────────────
class LoginRequest(BaseModel):
email: EmailStr
password: str
class TokenResponse(BaseModel):
access_token: str
refresh_token: str
token_type: str = "bearer"
class RefreshRequest(BaseModel):
refresh_token: str
class ChangePasswordRequest(BaseModel):
current_password: str
new_password: str = Field(min_length=8)
# ── Users ─────────────────────────────────────────────────────────────────────
class UserOut(BaseModel):
id: str
email: str
username: str
role: str
is_active: bool
daily_overhead_hours: float
created_at: datetime
model_config = {"from_attributes": True}
class UserCreate(BaseModel):
email: EmailStr
username: str = Field(min_length=2, max_length=100)
password: str = Field(min_length=8)
role: str = Field(default="user", pattern="^(admin|user)$")
class UserUpdate(BaseModel):
username: str | None = None
role: str | None = Field(default=None, pattern="^(admin|user)$")
is_active: bool | None = None
daily_overhead_hours: float | None = Field(default=None, ge=0, le=12)
# ── API Keys ──────────────────────────────────────────────────────────────────
class ApiKeyOut(BaseModel):
id: str
label: str
key_prefix: str
is_active: bool
last_used_at: datetime | None
created_at: datetime
model_config = {"from_attributes": True}
class ApiKeyCreate(BaseModel):
label: str = Field(default="My Machine", max_length=100)
class ApiKeyCreated(ApiKeyOut):
raw_key: str # shown once
# ── Ingestion ─────────────────────────────────────────────────────────────────
class SessionPayload(BaseModel):
session_id: str
project_slug: str
date: date
start_at: datetime
end_at: datetime
message_count: int = 0
active_hours: float = 0.0
work_summary: str = ""
commits: list[str] = []
tools_used: dict[str, int] = {}
files_changed: list[str] = []
raw_stats: dict[str, Any] = {}
class IngestPayload(BaseModel):
root_path: str = ""
sessions: list[SessionPayload]
class IngestResponse(BaseModel):
accepted: int
skipped: int
# ── Dashboard ─────────────────────────────────────────────────────────────────
class KpiSummary(BaseModel):
total_hours: float
total_projects: int
working_days: int
total_sessions: int
avg_hours_per_day: float
top_project: str
total_commits: int
total_files_changed: int
period_from: date | None
period_to: date | None
class ProjectHours(BaseModel):
project_id: str
display_name: str
total_hours: float
session_count: int
working_days: int
last_active: date | None
class DailyPoint(BaseModel):
date: date
hours: float
sessions: int
class MonthlyPoint(BaseModel):
month: str # "2026-03"
hours: float
class DowPoint(BaseModel):
dow: int # 0=Mon … 6=Sun
label: str
hours: float
class ToolUsage(BaseModel):
tool: str
count: int
class SessionOut(BaseModel):
id: str
session_id: str
project_id: str
project_name: str
date: date
start_at: datetime
end_at: datetime
active_hours: float
message_count: int
work_summary: str
commits: list[str]
tools_used: dict[str, int]
files_changed: list[str]
model_config = {"from_attributes": True}
class ProjectDetail(BaseModel):
project: "ProjectOut"
daily: list[DailyPoint]
sessions: list[SessionOut]
top_files: list[dict]
top_tools: list[ToolUsage]
class ProjectOut(BaseModel):
id: str
slug: str
display_name: str
root_path: str
created_at: datetime
model_config = {"from_attributes": True}
# ── Admin ─────────────────────────────────────────────────────────────────────
class AdminStats(BaseModel):
total_users: int
active_users: int
total_sessions: int
total_hours: float
users: list[UserOut]