mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 02:23:47 +00:00
refactor: update remnawave API types for v2.7.0
- ServerInfo: remove cpu_physical_cores, memory_available - NodeInfo: replace cpu_count/cpu_model/total_ram with versions/system, xray_uptime changed to number, users_online now required - NodeStatus: same field updates, remove dead uptime field - Extract formatUptime to shared utils/format.ts - Update AdminRemnawave and AdminDashboard components
This commit is contained in:
@@ -162,15 +162,11 @@ export interface NodeStatus {
|
|||||||
is_disabled: boolean;
|
is_disabled: boolean;
|
||||||
users_online: number;
|
users_online: number;
|
||||||
traffic_used_bytes?: number;
|
traffic_used_bytes?: number;
|
||||||
uptime?: string;
|
|
||||||
xray_version?: string;
|
|
||||||
node_version?: string;
|
|
||||||
last_status_message?: string;
|
last_status_message?: string;
|
||||||
xray_uptime?: string;
|
xray_uptime: number;
|
||||||
is_xray_running?: boolean;
|
is_xray_running?: boolean;
|
||||||
cpu_count?: number;
|
versions?: { xray: string; node: string } | null;
|
||||||
cpu_model?: string;
|
system?: Record<string, unknown> | null;
|
||||||
total_ram?: string;
|
|
||||||
country_code?: string;
|
country_code?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,11 +29,9 @@ export interface SystemSummary {
|
|||||||
|
|
||||||
export interface ServerInfo {
|
export interface ServerInfo {
|
||||||
cpu_cores: number;
|
cpu_cores: number;
|
||||||
cpu_physical_cores: number;
|
|
||||||
memory_total: number;
|
memory_total: number;
|
||||||
memory_used: number;
|
memory_used: number;
|
||||||
memory_free: number;
|
memory_free: number;
|
||||||
memory_available: number;
|
|
||||||
uptime_seconds: number;
|
uptime_seconds: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,22 +76,47 @@ export interface NodeInfo {
|
|||||||
is_disabled: boolean;
|
is_disabled: boolean;
|
||||||
is_node_online: boolean;
|
is_node_online: boolean;
|
||||||
is_xray_running: boolean;
|
is_xray_running: boolean;
|
||||||
users_online?: number;
|
users_online: number;
|
||||||
traffic_used_bytes?: number;
|
traffic_used_bytes?: number;
|
||||||
traffic_limit_bytes?: number;
|
traffic_limit_bytes?: number;
|
||||||
last_status_change?: string;
|
last_status_change?: string;
|
||||||
last_status_message?: string;
|
last_status_message?: string;
|
||||||
xray_uptime?: string;
|
xray_uptime: number;
|
||||||
is_traffic_tracking_active: boolean;
|
is_traffic_tracking_active: boolean;
|
||||||
traffic_reset_day?: number;
|
traffic_reset_day?: number;
|
||||||
notify_percent?: number;
|
notify_percent?: number;
|
||||||
consumption_multiplier: number;
|
consumption_multiplier: number;
|
||||||
cpu_count?: number;
|
|
||||||
cpu_model?: string;
|
|
||||||
total_ram?: string;
|
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
provider_uuid?: 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;
|
active_plugin_uuid?: string;
|
||||||
config_profile?: {
|
config_profile?: {
|
||||||
active_config_profile_uuid: string | null;
|
active_config_profile_uuid: string | null;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
type TopCampaignsResponse,
|
type TopCampaignsResponse,
|
||||||
type RecentPaymentsResponse,
|
type RecentPaymentsResponse,
|
||||||
} from '../api/admin';
|
} from '../api/admin';
|
||||||
|
import { formatUptime } from '../utils/format';
|
||||||
|
|
||||||
const CABINET_VERSION = __APP_VERSION__;
|
const CABINET_VERSION = __APP_VERSION__;
|
||||||
import { useCurrency } from '../hooks/useCurrency';
|
import { useCurrency } from '../hooks/useCurrency';
|
||||||
@@ -254,14 +255,16 @@ function NodeCard({ node, onRestart, onToggle, isLoading }: NodeCardProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Xray Version & Uptime */}
|
{/* Xray Version & Uptime */}
|
||||||
{(node.xray_version || node.xray_uptime) && (
|
{(node.versions?.xray || node.xray_uptime > 0) && (
|
||||||
<div className="mb-3 flex items-center gap-3 text-xs">
|
<div className="mb-3 flex items-center gap-3 text-xs">
|
||||||
{node.xray_version && (
|
{node.versions?.xray && (
|
||||||
<span className="rounded bg-dark-700/50 px-2 py-1 text-dark-300">
|
<span className="rounded bg-dark-700/50 px-2 py-1 text-dark-300">
|
||||||
Xray {node.xray_version}
|
Xray {node.versions.xray}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{node.xray_uptime && <span className="text-dark-500">Uptime: {node.xray_uptime}</span>}
|
{node.xray_uptime > 0 && (
|
||||||
|
<span className="text-dark-500">Uptime: {formatUptime(node.xray_uptime)}</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
AutoSyncStatus,
|
AutoSyncStatus,
|
||||||
} from '../api/adminRemnawave';
|
} from '../api/adminRemnawave';
|
||||||
import { usePlatform } from '../platform/hooks/usePlatform';
|
import { usePlatform } from '../platform/hooks/usePlatform';
|
||||||
|
import { formatUptime } from '../utils/format';
|
||||||
import {
|
import {
|
||||||
ServerIcon,
|
ServerIcon,
|
||||||
ChartIcon,
|
ChartIcon,
|
||||||
@@ -43,16 +44,6 @@ const formatBytes = (bytes: number): string => {
|
|||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatUptime = (seconds: number): string => {
|
|
||||||
const days = Math.floor(seconds / 86400);
|
|
||||||
const hours = Math.floor((seconds % 86400) / 3600);
|
|
||||||
const minutes = Math.floor((seconds % 3600) / 60);
|
|
||||||
|
|
||||||
if (days > 0) return `${days}d ${hours}h`;
|
|
||||||
if (hours > 0) return `${hours}h ${minutes}m`;
|
|
||||||
return `${minutes}m`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCountryFlag = (code: string | null | undefined): string => {
|
const getCountryFlag = (code: string | null | undefined): string => {
|
||||||
if (!code) return '🌍';
|
if (!code) return '🌍';
|
||||||
const codeMap: Record<string, string> = {
|
const codeMap: Record<string, string> = {
|
||||||
@@ -175,11 +166,12 @@ function NodeCard({ node, onAction, isLoading }: NodeCardProps) {
|
|||||||
{t('admin.remnawave.nodes.trafficUsed', 'used')}
|
{t('admin.remnawave.nodes.trafficUsed', 'used')}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{node.xray_uptime && (
|
{node.xray_uptime > 0 && (
|
||||||
<span>
|
<span>
|
||||||
{t('admin.remnawave.nodes.uptimeLabel', 'Uptime')}: {node.xray_uptime}
|
{t('admin.remnawave.nodes.uptimeLabel', 'Uptime')}: {formatUptime(node.xray_uptime)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{node.versions?.xray && <span>Xray {node.versions.xray}</span>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -355,15 +347,9 @@ function OverviewTab({ stats, isLoading, onRefresh }: OverviewTabProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use (total - available) instead of raw "used" to exclude disk cache/buffers
|
|
||||||
// This matches what htop/free show as actual application memory usage
|
|
||||||
const memoryActualUsed =
|
|
||||||
stats.server_info.memory_available > 0
|
|
||||||
? stats.server_info.memory_total - stats.server_info.memory_available
|
|
||||||
: stats.server_info.memory_used;
|
|
||||||
const memoryUsedPercent =
|
const memoryUsedPercent =
|
||||||
stats.server_info.memory_total > 0
|
stats.server_info.memory_total > 0
|
||||||
? Math.round((memoryActualUsed / stats.server_info.memory_total) * 100)
|
? Math.round((stats.server_info.memory_used / stats.server_info.memory_total) * 100)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -437,14 +423,14 @@ function OverviewTab({ stats, isLoading, onRefresh }: OverviewTabProps) {
|
|||||||
<div className="grid grid-cols-2 gap-3 lg:grid-cols-3">
|
<div className="grid grid-cols-2 gap-3 lg:grid-cols-3">
|
||||||
<StatCard
|
<StatCard
|
||||||
label={t('admin.remnawave.overview.cpu', 'CPU Cores')}
|
label={t('admin.remnawave.overview.cpu', 'CPU Cores')}
|
||||||
value={`${stats.server_info.cpu_cores} (${stats.server_info.cpu_physical_cores} physical)`}
|
value={stats.server_info.cpu_cores}
|
||||||
icon={<span className="text-lg">⚡</span>}
|
icon={<span className="text-lg">⚡</span>}
|
||||||
color="accent"
|
color="accent"
|
||||||
/>
|
/>
|
||||||
<StatCard
|
<StatCard
|
||||||
label={t('admin.remnawave.overview.memory', 'Memory')}
|
label={t('admin.remnawave.overview.memory', 'Memory')}
|
||||||
value={`${memoryUsedPercent}%`}
|
value={`${memoryUsedPercent}%`}
|
||||||
subValue={`${formatBytes(memoryActualUsed)} / ${formatBytes(stats.server_info.memory_total)}`}
|
subValue={`${formatBytes(stats.server_info.memory_used)} / ${formatBytes(stats.server_info.memory_total)}`}
|
||||||
icon={<span className="text-lg">💾</span>}
|
icon={<span className="text-lg">💾</span>}
|
||||||
color={memoryUsedPercent > 80 ? 'red' : memoryUsedPercent > 60 ? 'orange' : 'green'}
|
color={memoryUsedPercent > 80 ? 'red' : memoryUsedPercent > 60 ? 'orange' : 'green'}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
export function formatUptime(seconds: number): string {
|
||||||
|
const days = Math.floor(seconds / 86400);
|
||||||
|
const hours = Math.floor((seconds % 86400) / 3600);
|
||||||
|
const minutes = Math.floor((seconds % 3600) / 60);
|
||||||
|
if (days > 0) return `${days}d ${hours}h`;
|
||||||
|
if (hours > 0) return `${hours}h ${minutes}m`;
|
||||||
|
return `${minutes}m`;
|
||||||
|
}
|
||||||
|
|
||||||
export function formatPrice(kopeks: number): string {
|
export function formatPrice(kopeks: number): string {
|
||||||
const rubles = kopeks / 100;
|
const rubles = kopeks / 100;
|
||||||
return new Intl.NumberFormat('ru-RU', {
|
return new Intl.NumberFormat('ru-RU', {
|
||||||
|
|||||||
Reference in New Issue
Block a user