velxio/frontend/src/services/metricsService.ts

243 lines
6.0 KiB
TypeScript
Raw Normal View History

2026-04-26 05:46:52 +07:00
import axios from 'axios';
const API_BASE = import.meta.env.VITE_API_BASE || '/api';
const api = axios.create({ baseURL: API_BASE, withCredentials: true });
// ── Client-side tracking ─────────────────────────────────────────────────────
export interface RunEventPayload {
project_id?: string | null;
board_fqbn?: string | null;
}
export async function reportRunEvent(payload: RunEventPayload): Promise<void> {
// Best-effort fire-and-forget.
try {
await api.post('/metrics/run', payload);
} catch {
// Telemetry must never break the user flow.
}
}
// ── Admin dashboard ──────────────────────────────────────────────────────────
export interface OverviewResponse {
total_users: number;
total_projects: number;
public_projects: number;
private_projects: number;
total_compiles: number;
total_compile_errors: number;
total_runs: number;
compile_success_rate: number;
dau: number;
wau: number;
mau: number;
new_users_30d: number;
new_projects_30d: number;
}
export interface TimeseriesPoint {
bucket: string;
value: number;
}
export interface TimeseriesResponse {
metric: string;
bucket: string;
range_days: number;
points: TimeseriesPoint[];
}
export interface BoardBreakdown {
board_family: string | null;
board_fqbn: string | null;
compile_count: number;
compile_error_count: number;
run_count: number;
distinct_users: number;
distinct_projects: number;
}
export interface BoardsResponse {
families: BoardBreakdown[];
fqbns: BoardBreakdown[];
}
export interface BoardDiversityBucket {
bucket: string;
user_count: number;
}
export interface BoardDiversityResponse {
buckets: BoardDiversityBucket[];
total_users_with_compiles: number;
}
export interface TopUserEntry {
user_id: string;
username: string;
value: number;
}
export interface TopProjectEntry {
project_id: string;
project_name: string;
owner_username: string;
value: number;
}
export interface UserMetricsResponse {
user_id: string;
username: string;
total_compiles: number;
total_compile_errors: number;
total_runs: number;
last_active_at: string | null;
boards_used: string[];
fqbns_used: string[];
project_count: number;
timeseries: TimeseriesPoint[];
}
export interface CountryEntry {
country: string | null;
user_count: number;
signup_count: number;
compile_count: number;
run_count: number;
distinct_users_active: number;
}
export interface CountriesResponse {
range_days: number;
entries: CountryEntry[];
}
export async function adminGetCountries(rangeDays = 90): Promise<CountriesResponse> {
const { data } = await api.get<CountriesResponse>('/admin/metrics/countries', {
params: { range_days: rangeDays },
});
return data;
}
export interface ProjectMetricsResponse {
project_id: string;
project_name: string;
owner_username: string;
compile_count: number;
compile_error_count: number;
run_count: number;
update_count: number;
last_compiled_at: string | null;
last_run_at: string | null;
timeseries: TimeseriesPoint[];
}
export async function adminGetOverview(): Promise<OverviewResponse> {
const { data } = await api.get<OverviewResponse>('/admin/metrics/overview');
return data;
}
export async function adminGetTimeseries(
metric: 'compile' | 'compile_error' | 'run' | 'save' | 'create' | 'project_open',
rangeDays = 30,
bucket: 'hour' | 'day' | 'week' = 'day',
): Promise<TimeseriesResponse> {
const { data } = await api.get<TimeseriesResponse>('/admin/metrics/timeseries', {
params: { metric, range_days: rangeDays, bucket },
});
return data;
}
export async function adminGetBoards(rangeDays = 90): Promise<BoardsResponse> {
const { data } = await api.get<BoardsResponse>('/admin/metrics/boards', {
params: { range_days: rangeDays },
});
return data;
}
export async function adminGetBoardDiversity(): Promise<BoardDiversityResponse> {
const { data } = await api.get<BoardDiversityResponse>('/admin/metrics/board-diversity');
return data;
}
export async function adminGetTopUsers(
metric: 'compiles' | 'runs' = 'compiles',
limit = 20,
): Promise<TopUserEntry[]> {
const { data } = await api.get<TopUserEntry[]>('/admin/metrics/top-users', {
params: { metric, limit },
});
return data;
}
export async function adminGetTopProjects(
metric: 'compiles' | 'runs' | 'updates' = 'compiles',
limit = 20,
): Promise<TopProjectEntry[]> {
const { data } = await api.get<TopProjectEntry[]>('/admin/metrics/top-projects', {
params: { metric, limit },
});
return data;
}
export async function adminGetUserMetrics(
userId: string,
rangeDays = 30,
): Promise<UserMetricsResponse> {
const { data } = await api.get<UserMetricsResponse>(`/admin/metrics/users/${userId}`, {
params: { range_days: rangeDays },
});
return data;
}
export interface DailyProjectActivity {
date: string;
project_id: string | null;
project_name: string | null;
compiles: number;
compile_errors: number;
runs: number;
saves: number;
}
export interface DailyTotals {
date: string;
compiles: number;
compile_errors: number;
runs: number;
saves: number;
distinct_projects: number;
}
export interface UserDailyActivityResponse {
user_id: string;
username: string;
range_days: number;
entries: DailyProjectActivity[];
daily_totals: DailyTotals[];
}
export async function adminGetUserActivity(
userId: string,
rangeDays = 30,
): Promise<UserDailyActivityResponse> {
const { data } = await api.get<UserDailyActivityResponse>(
`/admin/metrics/users/${userId}/activity`,
{ params: { range_days: rangeDays } },
);
return data;
}
2026-04-26 05:46:52 +07:00
export async function adminGetProjectMetrics(
projectId: string,
rangeDays = 30,
): Promise<ProjectMetricsResponse> {
const { data } = await api.get<ProjectMetricsResponse>(`/admin/metrics/projects/${projectId}`, {
params: { range_days: rangeDays },
});
return data;
}