88 lines
3.0 KiB
JavaScript
88 lines
3.0 KiB
JavaScript
export function formatBytes(bytes) {
|
|
if (!bytes || bytes === 0) return '0 B';
|
|
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${units[i]}`;
|
|
}
|
|
|
|
export function formatUptime(seconds) {
|
|
if (!seconds) return '—';
|
|
const h = Math.floor(seconds / 3600);
|
|
const m = Math.floor((seconds % 3600) / 60);
|
|
if (h > 0) return `${h}h ${m}m`;
|
|
return `${m}m`;
|
|
}
|
|
|
|
export function protoName(n) {
|
|
const PROTOS = { 1:'ICMP', 2:'IGMP', 6:'TCP', 17:'UDP', 41:'IPv6', 47:'GRE', 50:'ESP', 51:'AH', 58:'ICMPv6', 89:'OSPF', 132:'SCTP' };
|
|
return PROTOS[n] || (n ? `IP/${n}` : '—');
|
|
}
|
|
|
|
export function protoColor(n) {
|
|
const COLORS = { 6:'#38bdf8', 17:'#a78bfa', 1:'#fbbf24', 58:'#fb923c' };
|
|
return COLORS[n] || '#64748b';
|
|
}
|
|
|
|
export function serviceLabel(port, proto) {
|
|
const TCP = {
|
|
20:'FTP-data', 21:'FTP', 22:'SSH', 23:'Telnet', 25:'SMTP', 53:'DNS',
|
|
80:'HTTP', 110:'POP3', 143:'IMAP', 443:'HTTPS', 465:'SMTPS', 587:'SMTP',
|
|
993:'IMAPS', 995:'POP3S', 1194:'OpenVPN', 1883:'MQTT', 3306:'MySQL',
|
|
3389:'RDP', 5900:'VNC', 6443:'K8s API', 8080:'HTTP-alt', 8443:'HTTPS-alt',
|
|
8883:'MQTT-TLS', 9000:'Custom', 51820:'WireGuard',
|
|
};
|
|
const UDP = {
|
|
53:'DNS', 67:'DHCP', 68:'DHCP', 123:'NTP', 161:'SNMP', 500:'IKE',
|
|
514:'Syslog', 1194:'OpenVPN', 4500:'IPsec', 5353:'mDNS', 51820:'WireGuard',
|
|
};
|
|
if (proto === 6 && TCP[port]) return TCP[port];
|
|
if (proto === 17 && UDP[port]) return UDP[port];
|
|
if (TCP[port]) return TCP[port];
|
|
return port ? `port ${port}` : '';
|
|
}
|
|
|
|
export const DEVICE_GROUPS = ['IoT', 'Servers', 'Mobile', 'Network', 'Desktop', 'Media'];
|
|
export const GROUP_COLORS = {
|
|
IoT:'#f59e0b', Servers:'#3b82f6', Mobile:'#ec4899',
|
|
Network:'#10b981', Desktop:'#8b5cf6', Media:'#f97316',
|
|
};
|
|
|
|
export function timeAgo(ms) {
|
|
if (!ms) return '—';
|
|
const sec = Math.floor((Date.now() - ms) / 1000);
|
|
if (sec < 10) return 'just now';
|
|
if (sec < 60) return `${sec}s ago`;
|
|
const min = Math.floor(sec / 60);
|
|
if (min < 60) return `${min}m ago`;
|
|
return `${Math.floor(min / 60)}h ago`;
|
|
}
|
|
|
|
export function deviceIcon(device) {
|
|
if (device.is_wired) return '🖥';
|
|
const oui = (device.oui || '').toLowerCase();
|
|
if (oui.includes('apple')) return '🍎';
|
|
if (oui.includes('samsung')) return '📱';
|
|
if (oui.includes('sony')) return '🎮';
|
|
if (oui.includes('lg')) return '📺';
|
|
if (oui.includes('intel')) return '💻';
|
|
if (oui.includes('raspberry')) return '🍓';
|
|
return '📡';
|
|
}
|
|
|
|
// Map app/category codes to readable names
|
|
const APP_NAMES = {
|
|
0: 'Other', 1: 'Instant messaging', 2: 'P2P', 3: 'Thumbnails',
|
|
4: 'Streaming media', 5: 'VoIP', 6: 'Gaming', 7: 'Remote access',
|
|
8: 'News feeds', 9: 'Streaming video', 10: 'Email', 11: 'News',
|
|
12: 'Downloads', 13: 'Business apps', 14: 'Social networking',
|
|
15: 'Web browsing', 255: 'Unknown',
|
|
};
|
|
|
|
export function appName(cat) {
|
|
if (typeof cat === 'number') return APP_NAMES[cat] || `Cat ${cat}`;
|
|
return cat || 'Unknown';
|
|
}
|
|
|
|
export const OUT_COLOR = '#38bdf8';
|
|
export const IN_COLOR = '#f472b6';
|