mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 02:23:47 +00:00
- Add NodeRealtimeStats and InboundTraffic TypeScript interfaces - Type getNodesRealtime return as NodeRealtimeStats[] - Add new Traffic tab to AdminRemnawave page showing per-node inbound/outbound traffic sorted by total bytes - Display download/upload/total per inbound tag for each node
456 lines
12 KiB
TypeScript
456 lines
12 KiB
TypeScript
import { apiClient } from './client';
|
|
|
|
// Status & Connection
|
|
export interface ConnectionStatus {
|
|
status: string;
|
|
message: string;
|
|
api_url?: string;
|
|
status_code?: number;
|
|
system_info?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface RemnaWaveStatusResponse {
|
|
is_configured: boolean;
|
|
configuration_error?: string;
|
|
connection?: ConnectionStatus;
|
|
}
|
|
|
|
// System Statistics
|
|
export interface SystemSummary {
|
|
users_online: number;
|
|
total_users: number;
|
|
active_connections: number;
|
|
nodes_online: number;
|
|
users_last_day: number;
|
|
users_last_week: number;
|
|
users_never_online: number;
|
|
total_user_traffic: number;
|
|
}
|
|
|
|
export interface ServerInfo {
|
|
cpu_cores: number;
|
|
memory_total: number;
|
|
memory_used: number;
|
|
memory_free: number;
|
|
uptime_seconds: number;
|
|
}
|
|
|
|
export interface Bandwidth {
|
|
realtime_download: number;
|
|
realtime_upload: number;
|
|
realtime_total: number;
|
|
}
|
|
|
|
export interface TrafficPeriod {
|
|
current: number;
|
|
previous: number;
|
|
difference?: string;
|
|
}
|
|
|
|
export interface TrafficPeriods {
|
|
last_2_days: TrafficPeriod;
|
|
last_7_days: TrafficPeriod;
|
|
last_30_days: TrafficPeriod;
|
|
current_month: TrafficPeriod;
|
|
current_year: TrafficPeriod;
|
|
}
|
|
|
|
export interface SystemStatsResponse {
|
|
system: SystemSummary;
|
|
users_by_status: Record<string, number>;
|
|
server_info: ServerInfo;
|
|
bandwidth: Bandwidth;
|
|
traffic_periods: TrafficPeriods;
|
|
nodes_realtime: Record<string, unknown>[];
|
|
nodes_weekly: Record<string, unknown>[];
|
|
last_updated?: string;
|
|
}
|
|
|
|
// Nodes
|
|
export interface NodeInfo {
|
|
uuid: string;
|
|
name: string;
|
|
address: string;
|
|
country_code?: string;
|
|
is_connected: boolean;
|
|
is_disabled: boolean;
|
|
is_node_online: boolean;
|
|
is_xray_running: boolean;
|
|
users_online: number;
|
|
traffic_used_bytes?: number;
|
|
traffic_limit_bytes?: number;
|
|
last_status_change?: string;
|
|
last_status_message?: string;
|
|
xray_uptime: number;
|
|
is_traffic_tracking_active: boolean;
|
|
traffic_reset_day?: number;
|
|
notify_percent?: number;
|
|
consumption_multiplier: number;
|
|
created_at?: string;
|
|
updated_at?: string;
|
|
provider_uuid?: string;
|
|
versions?: { xray: string; node: string } | null;
|
|
system?: {
|
|
info: {
|
|
arch: string;
|
|
cpus: number;
|
|
cpuModel: string;
|
|
memoryTotal: number;
|
|
hostname: string;
|
|
platform: string;
|
|
release: string;
|
|
type: string;
|
|
version: string;
|
|
networkInterfaces: string[];
|
|
};
|
|
stats: {
|
|
memoryFree: number;
|
|
memoryUsed: number;
|
|
uptime: number;
|
|
loadAvg: number[];
|
|
interface?: {
|
|
interface: string;
|
|
rxBytesPerSec: number;
|
|
txBytesPerSec: number;
|
|
rxTotal: number;
|
|
txTotal: number;
|
|
} | null;
|
|
};
|
|
} | null;
|
|
active_plugin_uuid?: string;
|
|
config_profile?: {
|
|
active_config_profile_uuid: string | null;
|
|
active_inbounds: Array<{
|
|
uuid: string;
|
|
profile_uuid: string;
|
|
tag: string;
|
|
type: string;
|
|
network: string | null;
|
|
security: string | null;
|
|
port: number | null;
|
|
}>;
|
|
};
|
|
}
|
|
|
|
export interface NodesListResponse {
|
|
items: NodeInfo[];
|
|
total: number;
|
|
}
|
|
|
|
export interface NodesOverview {
|
|
total: number;
|
|
online: number;
|
|
offline: number;
|
|
disabled: number;
|
|
total_users_online: number;
|
|
nodes: NodeInfo[];
|
|
}
|
|
|
|
export interface NodeStatisticsResponse {
|
|
node: NodeInfo;
|
|
realtime?: Record<string, unknown>;
|
|
usage_history: Record<string, unknown>[];
|
|
last_updated?: string;
|
|
}
|
|
|
|
export interface NodeActionResponse {
|
|
success: boolean;
|
|
message?: string;
|
|
is_disabled?: boolean;
|
|
}
|
|
|
|
// Realtime Traffic
|
|
export interface InboundTraffic {
|
|
tag: string;
|
|
downloadBytes: number;
|
|
uploadBytes: number;
|
|
totalBytes: number;
|
|
}
|
|
|
|
export interface NodeRealtimeStats {
|
|
nodeUuid: string;
|
|
nodeName: string;
|
|
downloadBytes: number;
|
|
uploadBytes: number;
|
|
totalBytes: number;
|
|
usersOnline: number;
|
|
inbounds: InboundTraffic[];
|
|
outbounds: InboundTraffic[];
|
|
}
|
|
|
|
// Squads
|
|
export interface SquadWithLocalInfo {
|
|
uuid: string;
|
|
name: string;
|
|
members_count: number;
|
|
inbounds_count: number;
|
|
inbounds: Record<string, unknown>[];
|
|
local_id?: number;
|
|
display_name?: string;
|
|
country_code?: string;
|
|
is_available?: boolean;
|
|
is_trial_eligible?: boolean;
|
|
price_kopeks?: number;
|
|
max_users?: number;
|
|
current_users?: number;
|
|
is_synced: boolean;
|
|
}
|
|
|
|
export interface SquadsListResponse {
|
|
items: SquadWithLocalInfo[];
|
|
total: number;
|
|
}
|
|
|
|
export interface SquadDetailResponse extends SquadWithLocalInfo {
|
|
description?: string;
|
|
sort_order?: number;
|
|
active_subscriptions: number;
|
|
}
|
|
|
|
export interface SquadOperationResponse {
|
|
success: boolean;
|
|
message?: string;
|
|
data?: Record<string, unknown>;
|
|
}
|
|
|
|
// Migration
|
|
export interface MigrationPreviewResponse {
|
|
squad_uuid: string;
|
|
squad_name: string;
|
|
current_users: number;
|
|
max_users?: number;
|
|
users_to_migrate: number;
|
|
}
|
|
|
|
export interface MigrationStats {
|
|
source_uuid: string;
|
|
target_uuid: string;
|
|
total: number;
|
|
updated: number;
|
|
panel_updated: number;
|
|
panel_failed: number;
|
|
source_removed: number;
|
|
target_added: number;
|
|
}
|
|
|
|
export interface MigrationResponse {
|
|
success: boolean;
|
|
message?: string;
|
|
error?: string;
|
|
data?: MigrationStats;
|
|
}
|
|
|
|
// Inbounds
|
|
export interface InboundsListResponse {
|
|
items: Record<string, unknown>[];
|
|
total: number;
|
|
}
|
|
|
|
// Auto Sync
|
|
export interface AutoSyncStatus {
|
|
enabled: boolean;
|
|
times: string[];
|
|
next_run?: string;
|
|
is_running: boolean;
|
|
last_run_started_at?: string;
|
|
last_run_finished_at?: string;
|
|
last_run_success?: boolean;
|
|
last_run_reason?: string;
|
|
last_run_error?: string;
|
|
last_user_stats?: Record<string, unknown>;
|
|
last_server_stats?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface AutoSyncRunResponse {
|
|
started: boolean;
|
|
success?: boolean;
|
|
error?: string;
|
|
user_stats?: Record<string, unknown>;
|
|
server_stats?: Record<string, unknown>;
|
|
reason?: string;
|
|
}
|
|
|
|
// Sync
|
|
export interface SyncResponse {
|
|
success: boolean;
|
|
message?: string;
|
|
data?: Record<string, unknown>;
|
|
}
|
|
|
|
export const adminRemnawaveApi = {
|
|
// Status & Connection
|
|
getStatus: async (): Promise<RemnaWaveStatusResponse> => {
|
|
const response = await apiClient.get('/cabinet/admin/remnawave/status');
|
|
return response.data;
|
|
},
|
|
|
|
// System Statistics
|
|
getSystemStats: async (): Promise<SystemStatsResponse> => {
|
|
const response = await apiClient.get('/cabinet/admin/remnawave/system');
|
|
return response.data;
|
|
},
|
|
|
|
// Nodes
|
|
getNodes: async (): Promise<NodesListResponse> => {
|
|
const response = await apiClient.get('/cabinet/admin/remnawave/nodes');
|
|
return response.data;
|
|
},
|
|
|
|
getNodesOverview: async (): Promise<NodesOverview> => {
|
|
const response = await apiClient.get('/cabinet/admin/remnawave/nodes/overview');
|
|
return response.data;
|
|
},
|
|
|
|
getNodesRealtime: async (): Promise<NodeRealtimeStats[]> => {
|
|
const response = await apiClient.get('/cabinet/admin/remnawave/nodes/realtime');
|
|
return response.data;
|
|
},
|
|
|
|
getNode: async (uuid: string): Promise<NodeInfo> => {
|
|
const response = await apiClient.get(`/cabinet/admin/remnawave/nodes/${uuid}`);
|
|
return response.data;
|
|
},
|
|
|
|
getNodeStatistics: async (uuid: string): Promise<NodeStatisticsResponse> => {
|
|
const response = await apiClient.get(`/cabinet/admin/remnawave/nodes/${uuid}/statistics`);
|
|
return response.data;
|
|
},
|
|
|
|
nodeAction: async (
|
|
uuid: string,
|
|
action: 'enable' | 'disable' | 'restart',
|
|
): Promise<NodeActionResponse> => {
|
|
const response = await apiClient.post(`/cabinet/admin/remnawave/nodes/${uuid}/action`, {
|
|
action,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
restartAllNodes: async (): Promise<NodeActionResponse> => {
|
|
const response = await apiClient.post('/cabinet/admin/remnawave/nodes/restart-all');
|
|
return response.data;
|
|
},
|
|
|
|
// Squads
|
|
getSquads: async (): Promise<SquadsListResponse> => {
|
|
const response = await apiClient.get('/cabinet/admin/remnawave/squads');
|
|
return response.data;
|
|
},
|
|
|
|
getSquad: async (uuid: string): Promise<SquadDetailResponse> => {
|
|
const response = await apiClient.get(`/cabinet/admin/remnawave/squads/${uuid}`);
|
|
return response.data;
|
|
},
|
|
|
|
createSquad: async (data: {
|
|
name: string;
|
|
inbound_uuids?: string[];
|
|
}): Promise<SquadOperationResponse> => {
|
|
const response = await apiClient.post('/cabinet/admin/remnawave/squads', data);
|
|
return response.data;
|
|
},
|
|
|
|
updateSquad: async (
|
|
uuid: string,
|
|
data: { name?: string; inbound_uuids?: string[] },
|
|
): Promise<SquadOperationResponse> => {
|
|
const response = await apiClient.patch(`/cabinet/admin/remnawave/squads/${uuid}`, data);
|
|
return response.data;
|
|
},
|
|
|
|
deleteSquad: async (uuid: string): Promise<SquadOperationResponse> => {
|
|
const response = await apiClient.delete(`/cabinet/admin/remnawave/squads/${uuid}`);
|
|
return response.data;
|
|
},
|
|
|
|
squadAction: async (
|
|
uuid: string,
|
|
data: {
|
|
action: 'add_all_users' | 'remove_all_users' | 'delete' | 'rename' | 'update_inbounds';
|
|
name?: string;
|
|
inbound_uuids?: string[];
|
|
},
|
|
): Promise<SquadOperationResponse> => {
|
|
const response = await apiClient.post(`/cabinet/admin/remnawave/squads/${uuid}/action`, data);
|
|
return response.data;
|
|
},
|
|
|
|
// Migration
|
|
getMigrationPreview: async (uuid: string): Promise<MigrationPreviewResponse> => {
|
|
const response = await apiClient.get(
|
|
`/cabinet/admin/remnawave/squads/${uuid}/migration-preview`,
|
|
);
|
|
return response.data;
|
|
},
|
|
|
|
migrateSquad: async (sourceUuid: string, targetUuid: string): Promise<MigrationResponse> => {
|
|
const response = await apiClient.post('/cabinet/admin/remnawave/squads/migrate', {
|
|
source_uuid: sourceUuid,
|
|
target_uuid: targetUuid,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Inbounds
|
|
getInbounds: async (): Promise<InboundsListResponse> => {
|
|
const response = await apiClient.get('/cabinet/admin/remnawave/inbounds');
|
|
return response.data;
|
|
},
|
|
|
|
// Auto Sync
|
|
getAutoSyncStatus: async (): Promise<AutoSyncStatus> => {
|
|
const response = await apiClient.get('/cabinet/admin/remnawave/sync/auto/status');
|
|
return response.data;
|
|
},
|
|
|
|
toggleAutoSync: async (enabled: boolean): Promise<SyncResponse> => {
|
|
const response = await apiClient.post('/cabinet/admin/remnawave/sync/auto/toggle', { enabled });
|
|
return response.data;
|
|
},
|
|
|
|
runAutoSync: async (): Promise<AutoSyncRunResponse> => {
|
|
const response = await apiClient.post('/cabinet/admin/remnawave/sync/auto/run');
|
|
return response.data;
|
|
},
|
|
|
|
// Manual Sync
|
|
syncFromPanel: async (
|
|
mode: 'all' | 'new_only' | 'update_only' = 'all',
|
|
): Promise<SyncResponse> => {
|
|
const response = await apiClient.post('/cabinet/admin/remnawave/sync/from-panel', { mode });
|
|
return response.data;
|
|
},
|
|
|
|
syncToPanel: async (): Promise<SyncResponse> => {
|
|
const response = await apiClient.post('/cabinet/admin/remnawave/sync/to-panel');
|
|
return response.data;
|
|
},
|
|
|
|
syncServers: async (): Promise<SyncResponse> => {
|
|
const response = await apiClient.post('/cabinet/admin/remnawave/sync/servers');
|
|
return response.data;
|
|
},
|
|
|
|
validateSubscriptions: async (): Promise<SyncResponse> => {
|
|
const response = await apiClient.post('/cabinet/admin/remnawave/sync/subscriptions/validate');
|
|
return response.data;
|
|
},
|
|
|
|
cleanupSubscriptions: async (): Promise<SyncResponse> => {
|
|
const response = await apiClient.post('/cabinet/admin/remnawave/sync/subscriptions/cleanup');
|
|
return response.data;
|
|
},
|
|
|
|
syncSubscriptionStatuses: async (): Promise<SyncResponse> => {
|
|
const response = await apiClient.post('/cabinet/admin/remnawave/sync/subscriptions/statuses');
|
|
return response.data;
|
|
},
|
|
|
|
getSyncRecommendations: async (): Promise<SyncResponse> => {
|
|
const response = await apiClient.get('/cabinet/admin/remnawave/sync/recommendations');
|
|
return response.data;
|
|
},
|
|
};
|
|
|
|
export default adminRemnawaveApi;
|