Files
bedolaga-cabinet/src/api/banSystem.ts
kewldan cf3837163e chore(tooling): миграция с ESLint + Prettier на Biome
Один инструмент вместо девяти dev-зависимостей. Замеры на этом репо
(483 файла, медиана из 3 прогонов, один и тот же компьютер):

- eslint .            ~20.4s  ->  biome lint .    ~2s
- prettier --check .  ~8.8s   ->  biome format .  ~0.2s
- суммарно линт+формат ~29s   ->  biome check .   ~2-3s (~10x)

Паритет с прежними правилами:
- форматирование бинарно совместимо с Prettier-конфигом (biome format
  --write тронул 9 файлов из ~480 — микроразличия);
- запрет window.confirm/alert/open/prompt и navigator.clipboard
  (Telegram WebView) перенесён GritQL-плагином
  biome-plugins/telegram-webview-guards.grit c теми же сообщениями
  и исключениями для src/platform/** и clipboard.ts; плагин сразу
  нашёл window.open в Wheel.tsx под старым eslint-disable —
  переведён на biome-ignore;
- react-hooks-правила: useExhaustiveDependencies/useHookAtTopLevel;
- новые для проекта правила (a11y и пр.) не включались или понижены
  до warning, чтобы миграция не смешивалась с чисткой кода — включать
  можно отдельными PR.

Ограничения (задокументированы): сортировка tailwind-классов
(prettier-plugin-tailwindcss) в Biome пока nursery — не включена;
CSS исключён из Biome (парсер спотыкается о Tailwind-синтаксис
globals.css) — файл один и правится редко.

CI не меняется: имена npm-скриптов lint/format:check сохранены.
lint-staged переведён на biome check --write.
2026-07-12 00:12:15 +03:00

462 lines
11 KiB
TypeScript

import apiClient from './client';
// === Types ===
export interface BanSystemStatus {
enabled: boolean;
configured: boolean;
}
export interface BanSystemStats {
total_users: number;
active_users: number;
users_over_limit: number;
total_requests: number;
total_punishments: number;
active_punishments: number;
nodes_online: number;
nodes_total: number;
agents_online: number;
agents_total: number;
panel_connected: boolean;
uptime_seconds: number | null;
}
export interface BanUserIPInfo {
ip: string;
first_seen: string | null;
last_seen: string | null;
node: string | null;
request_count: number;
country_code: string | null;
country_name: string | null;
city: string | null;
}
export interface BanUserRequestLog {
timestamp: string;
source_ip: string;
destination: string | null;
dest_port: number | null;
protocol: string | null;
action: string | null;
node: string | null;
}
export interface BanUserListItem {
email: string;
unique_ip_count: number;
total_requests: number;
limit: number | null;
is_over_limit: boolean;
blocked_count: number;
last_seen: string | null;
}
export interface BanUsersListResponse {
users: BanUserListItem[];
total: number;
offset: number;
limit: number;
}
export interface BanUserDetailResponse {
email: string;
unique_ip_count: number;
total_requests: number;
limit: number | null;
is_over_limit: boolean;
blocked_count: number;
ips: BanUserIPInfo[];
recent_requests: BanUserRequestLog[];
network_type: string | null;
}
export interface BanPunishmentItem {
id: number | null;
user_id: string;
uuid: string | null;
username: string;
reason: string | null;
punished_at: string;
enable_at: string | null;
ip_count: number;
limit: number;
enabled: boolean;
enabled_at: string | null;
node_name: string | null;
}
export interface BanPunishmentsListResponse {
punishments: BanPunishmentItem[];
total: number;
}
export interface BanHistoryResponse {
items: BanPunishmentItem[];
total: number;
}
export interface BanUserRequest {
username: string;
minutes: number;
reason?: string;
}
export interface UnbanResponse {
success: boolean;
message: string;
}
export interface BanNodeItem {
name: string;
address: string | null;
is_connected: boolean;
last_seen: string | null;
users_count: number;
agent_stats: Record<string, unknown> | null;
}
export interface BanNodesListResponse {
nodes: BanNodeItem[];
total: number;
online: number;
}
export interface BanAgentItem {
node_name: string;
sent_total: number;
dropped_total: number;
batches_total: number;
reconnects: number;
failures: number;
queue_size: number;
queue_max: number;
dedup_checked: number;
dedup_skipped: number;
filter_checked: number;
filter_filtered: number;
health: string;
is_online: boolean;
last_report: string | null;
}
export interface BanAgentsSummary {
total_agents: number;
online_agents: number;
total_sent: number;
total_dropped: number;
avg_queue_size: number;
healthy_count: number;
warning_count: number;
critical_count: number;
}
export interface BanAgentsListResponse {
agents: BanAgentItem[];
summary: BanAgentsSummary | null;
total: number;
online: number;
}
export interface BanTrafficViolationItem {
id: number | null;
username: string;
email: string | null;
violation_type: string;
description: string | null;
bytes_used: number;
bytes_limit: number;
detected_at: string;
resolved: boolean;
}
export interface BanTrafficViolationsResponse {
violations: BanTrafficViolationItem[];
total: number;
}
export interface BanTrafficTopItem {
username: string;
bytes_total: number;
bytes_limit: number | null;
over_limit: boolean;
}
export interface BanTrafficResponse {
enabled: boolean;
stats: Record<string, unknown> | null;
top_users: BanTrafficTopItem[];
recent_violations: BanTrafficViolationItem[];
}
// === Settings Types ===
export interface BanSettingDefinition {
key: string;
value: unknown;
type: string;
min_value: number | null;
max_value: number | null;
editable: boolean;
description: string | null;
category: string | null;
}
export interface BanSettingsResponse {
settings: BanSettingDefinition[];
}
export interface BanWhitelistRequest {
username: string;
}
// === Report Types ===
export interface BanReportTopViolator {
username: string;
count: number;
}
export interface BanReportResponse {
period_hours: number;
current_users: number;
current_ips: number;
punishment_stats: Record<string, unknown> | null;
top_violators: BanReportTopViolator[];
}
// === Health Types ===
export interface BanHealthComponent {
name: string;
status: string;
message: string | null;
details: Record<string, unknown> | null;
}
export interface BanHealthResponse {
status: string;
uptime: number | null;
components: BanHealthComponent[];
}
export interface BanHealthDetailedResponse {
status: string;
uptime: number | null;
components: Record<string, unknown>;
}
// === Agent History Types ===
export interface BanAgentHistoryItem {
timestamp: string;
sent_total: number;
dropped_total: number;
queue_size: number;
batches_total: number;
}
export interface BanAgentHistoryResponse {
node: string;
hours: number;
records: number;
delta: Record<string, unknown> | null;
first: Record<string, unknown> | null;
last: Record<string, unknown> | null;
history: BanAgentHistoryItem[];
}
// === API ===
export const banSystemApi = {
// Status
getStatus: async (): Promise<BanSystemStatus> => {
const response = await apiClient.get('/cabinet/admin/ban-system/status');
return response.data;
},
// Stats
getStats: async (): Promise<BanSystemStats> => {
const response = await apiClient.get('/cabinet/admin/ban-system/stats');
return response.data;
},
// Users
getUsers: async (
params: { offset?: number; limit?: number; status?: string } = {},
): Promise<BanUsersListResponse> => {
const response = await apiClient.get('/cabinet/admin/ban-system/users', { params });
return response.data;
},
getUsersOverLimit: async (limit: number = 50): Promise<BanUsersListResponse> => {
const response = await apiClient.get('/cabinet/admin/ban-system/users/over-limit', {
params: { limit },
});
return response.data;
},
searchUsers: async (query: string): Promise<BanUsersListResponse> => {
const response = await apiClient.get(
`/cabinet/admin/ban-system/users/search/${encodeURIComponent(query)}`,
);
return response.data;
},
getUser: async (email: string): Promise<BanUserDetailResponse> => {
const response = await apiClient.get(
`/cabinet/admin/ban-system/users/${encodeURIComponent(email)}`,
);
return response.data;
},
// Punishments
getPunishments: async (): Promise<BanPunishmentsListResponse> => {
const response = await apiClient.get('/cabinet/admin/ban-system/punishments');
return response.data;
},
unbanUser: async (userId: string): Promise<UnbanResponse> => {
const response = await apiClient.post(`/cabinet/admin/ban-system/punishments/${userId}/unban`);
return response.data;
},
banUser: async (data: BanUserRequest): Promise<UnbanResponse> => {
const response = await apiClient.post('/cabinet/admin/ban-system/ban', data);
return response.data;
},
getPunishmentHistory: async (query: string, limit: number = 20): Promise<BanHistoryResponse> => {
const response = await apiClient.get(
`/cabinet/admin/ban-system/history/${encodeURIComponent(query)}`,
{
params: { limit },
},
);
return response.data;
},
// Nodes
getNodes: async (): Promise<BanNodesListResponse> => {
const response = await apiClient.get('/cabinet/admin/ban-system/nodes');
return response.data;
},
// Agents
getAgents: async (
params: { search?: string; health?: string; status?: string } = {},
): Promise<BanAgentsListResponse> => {
const response = await apiClient.get('/cabinet/admin/ban-system/agents', { params });
return response.data;
},
getAgentsSummary: async (): Promise<BanAgentsSummary> => {
const response = await apiClient.get('/cabinet/admin/ban-system/agents/summary');
return response.data;
},
// Traffic violations
getTrafficViolations: async (limit: number = 50): Promise<BanTrafficViolationsResponse> => {
const response = await apiClient.get('/cabinet/admin/ban-system/traffic/violations', {
params: { limit },
});
return response.data;
},
// Full Traffic
getTraffic: async (): Promise<BanTrafficResponse> => {
const response = await apiClient.get('/cabinet/admin/ban-system/traffic');
return response.data;
},
getTrafficTop: async (limit: number = 20): Promise<BanTrafficTopItem[]> => {
const response = await apiClient.get('/cabinet/admin/ban-system/traffic/top', {
params: { limit },
});
return response.data;
},
// Settings
getSettings: async (): Promise<BanSettingsResponse> => {
const response = await apiClient.get('/cabinet/admin/ban-system/settings');
return response.data;
},
getSetting: async (key: string): Promise<BanSettingDefinition> => {
const response = await apiClient.get(`/cabinet/admin/ban-system/settings/${key}`);
return response.data;
},
setSetting: async (key: string, value: string): Promise<BanSettingDefinition> => {
const response = await apiClient.post(`/cabinet/admin/ban-system/settings/${key}`, null, {
params: { value },
});
return response.data;
},
toggleSetting: async (key: string): Promise<BanSettingDefinition> => {
const response = await apiClient.post(`/cabinet/admin/ban-system/settings/${key}/toggle`);
return response.data;
},
// Whitelist
whitelistAdd: async (username: string): Promise<UnbanResponse> => {
const response = await apiClient.post('/cabinet/admin/ban-system/settings/whitelist/add', {
username,
});
return response.data;
},
whitelistRemove: async (username: string): Promise<UnbanResponse> => {
const response = await apiClient.post('/cabinet/admin/ban-system/settings/whitelist/remove', {
username,
});
return response.data;
},
// Reports
getReport: async (hours: number = 24): Promise<BanReportResponse> => {
const response = await apiClient.get('/cabinet/admin/ban-system/report', {
params: { hours },
});
return response.data;
},
// Health
getHealth: async (): Promise<BanHealthResponse> => {
const response = await apiClient.get('/cabinet/admin/ban-system/health');
return response.data;
},
getHealthDetailed: async (): Promise<BanHealthDetailedResponse> => {
const response = await apiClient.get('/cabinet/admin/ban-system/health/detailed');
return response.data;
},
// Agent History
getAgentHistory: async (
nodeName: string,
hours: number = 24,
): Promise<BanAgentHistoryResponse> => {
const response = await apiClient.get(
`/cabinet/admin/ban-system/agents/${encodeURIComponent(nodeName)}/history`,
{
params: { hours },
},
);
return response.data;
},
// User Punishment History
getUserHistory: async (email: string, limit: number = 20): Promise<BanHistoryResponse> => {
const response = await apiClient.get(
`/cabinet/admin/ban-system/users/${encodeURIComponent(email)}/history`,
{
params: { limit },
},
);
return response.data;
},
};