feat(ui): add type filter toggles

This commit is contained in:
2026-02-18 23:13:28 -08:00
commit a4cff9894c
14457 changed files with 2204835 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
{
"hosts": [
{
"name": "ubuntu",
"ip": "192.168.50.61",
"sshUser": "bear",
"sshKeyPath": "~/.ssh/id_ed25519",
"sshPort": 22
},
{
"name": "grizzley",
"ip": "192.168.50.84",
"sshUser": "bear",
"sshKeyPath": "~/.ssh/id_ed25519"
},
{
"name": "truenas",
"ip": "192.168.50.12",
"sshUser": "root",
"sshKeyPath": "~/.ssh/id_ed25519"
}
]
}

46
server/config.json Normal file
View File

@@ -0,0 +1,46 @@
{
"hosts": [
{
"name": "ubuntu",
"ip": "192.168.50.61",
"sshUser": "bear",
"sshKeyPath": "~/.ssh/id_ed25519",
"sshPort": 22
},
{
"name": "grizzley",
"ip": "192.168.50.84",
"sshUser": "bear",
"sshKeyPath": "~/.ssh/id_ed25519",
"sshPort": 22
},
{
"name": "truenas",
"ip": "192.168.50.12",
"sshUser": "root",
"sshKeyPath": "~/.ssh/id_ed25519",
"sshPort": 22
},
{
"name": "proxmox",
"ip": "192.168.50.11",
"sshUser": "root",
"sshKeyPath": "~/.ssh/id_ed25519",
"sshPort": 22
},
{
"name": "ice",
"ip": "192.168.50.197",
"sshUser": "bear",
"sshKeyPath": "~/.ssh/id_ed25519",
"sshPort": 22
},
{
"name": "panda",
"ip": "192.168.50.196",
"sshUser": "bear",
"sshKeyPath": "~/.ssh/id_ed25519",
"sshPort": 22
}
]
}

64
server/config.ts Normal file
View File

@@ -0,0 +1,64 @@
import fs from 'fs';
import path from 'path';
import { HostConfig } from './types';
const CONFIG_FILE = path.join(__dirname, 'config.json');
function parseEnvHosts(): HostConfig[] {
const hostsEnv = process.env.SSH_HOSTS;
if (!hostsEnv) return [];
const hosts: HostConfig[] = [];
const entries = hostsEnv.split(',').map(h => h.trim()).filter(Boolean);
for (const entry of entries) {
const [name, ip] = entry.split(':');
if (name && ip) {
hosts.push({
name: name.trim(),
ip: ip.trim(),
sshUser: process.env.SSH_USER || 'bear',
sshKeyPath: process.env.SSH_KEY,
sshPort: process.env.SSH_PORT ? parseInt(process.env.SSH_PORT, 10) : 22,
});
}
}
return hosts;
}
function parseJsonConfig(): HostConfig[] {
if (!fs.existsSync(CONFIG_FILE)) return [];
try {
const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
const data = JSON.parse(content);
if (!data.hosts || !Array.isArray(data.hosts)) {
return [];
}
return data.hosts.map((h: Partial<HostConfig>) => ({
name: h.name || '',
ip: h.ip || '',
sshUser: h.sshUser || 'bear',
sshKeyPath: h.sshKeyPath,
sshPort: h.sshPort || 22,
})).filter((h: HostConfig) => h.name && h.ip);
} catch {
return [];
}
}
export function getHostConfigs(): HostConfig[] {
const envHosts = parseEnvHosts();
if (envHosts.length > 0) {
return envHosts;
}
return parseJsonConfig();
}
export function hasConfig(): boolean {
return getHostConfigs().length > 0;
}

20
server/index.ts Normal file
View File

@@ -0,0 +1,20 @@
import express from 'express';
import cors from 'cors';
const app = express();
const PORT = 3001;
// CORS middleware for frontend communication
app.use(cors({
origin: 'http://localhost:3000',
credentials: true,
}));
// Health check endpoint
app.get('/api/health', (req, res) => {
res.json({ status: 'ok' });
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});

78
server/types.ts Normal file
View File

@@ -0,0 +1,78 @@
/**
* Backend Types for Homelab Topology Visualizer
*
* Type definitions for server-side operations including:
* - Host configuration for SSH connections
* - API response types for discovery, config, files, and stats
*/
export interface HostConfig {
/** Hostname for display in topology */
name: string;
/** IP address for SSH connection */
ip: string;
/** SSH username */
sshUser: string;
/** Optional path to SSH private key file */
sshKeyPath?: string;
/** Optional SSH port (defaults to 22) */
sshPort?: number;
}
export interface DiscoveryResponse {
/** Array of discovered hosts with their status */
hosts: Array<{
name: string;
ip: string;
online: boolean;
containers?: string[];
error?: string;
}>;
/** Timestamp of discovery run */
timestamp: string;
/** Any errors encountered during discovery */
errors: string[];
}
export interface ConfigResponse {
/** Raw YAML configuration content */
yaml: string;
/** Path to the config file */
path: string;
/** Error message if config retrieval failed */
error?: string;
}
export interface VolumeMount {
/** Source path on host */
source: string;
/** Destination path in container */
destination: string;
/** Mount mode (e.g., 'rw', 'ro') */
mode: string;
}
export interface FilesResponse {
/** Array of volume mounts */
volumes: VolumeMount[];
/** Error message if file retrieval failed */
error?: string;
}
export interface NetworkStats {
/** Bytes received */
rx: number;
/** Bytes transmitted */
tx: number;
}
export interface StatsResponse {
/** CPU usage percentage */
cpu: number;
/** Memory usage percentage */
memory: number;
/** Network I/O statistics */
network: NetworkStats;
/** Error message if stats retrieval failed */
error?: string;
}