mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-29 18:13:47 +00:00
Один инструмент вместо девяти 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.
462 lines
11 KiB
TypeScript
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;
|
|
},
|
|
};
|