Skip to main content
Glama
truenas.ts5.5 kB
import https from 'https'; import { Config, TrueNASStatus, TrueNASAlert, TrueNASDataset, TrueNASDatasetInfo, OperationResult } from '../types.js'; let config: Config; export function initTrueNAS(cfg: Config): void { config = cfg; } async function truenasRequest(endpoint: string, method: string = 'GET', body?: any): Promise<any> { if (!config.truenasHost || !config.truenasApiKey) { throw new Error('TrueNAS is not configured'); } const url = `https://${config.truenasHost}/api/v2.0${endpoint}`; return new Promise((resolve, reject) => { const options = { method, headers: { 'Authorization': `Bearer ${config.truenasApiKey}`, 'Content-Type': 'application/json', }, // Allow self-signed certificates rejectUnauthorized: false, }; const req = https.request(url, options, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) { try { resolve(JSON.parse(data)); } catch { resolve(data); } } else { reject(new Error(`TrueNAS API error: ${res.statusCode} - ${data}`)); } }); }); req.on('error', (error) => { reject(error); }); if (body) { req.write(JSON.stringify(body)); } req.end(); }); } // Level 1 - Monitor export async function getTrueNASStatus(): Promise<TrueNASStatus> { try { // Get system info const systemInfo = await truenasRequest('/system/info'); // Get pools const pools = await truenasRequest('/pool'); const poolsFormatted = pools.map((pool: any) => { const usedBytes = pool.allocated || 0; const totalBytes = (pool.allocated || 0) + (pool.free || 0); const usedPercent = totalBytes > 0 ? parseFloat(((usedBytes / totalBytes) * 100).toFixed(2)) : 0; return { name: pool.name, status: pool.status as 'ONLINE' | 'DEGRADED' | 'OFFLINE', used_percent: usedPercent, }; }); return { version: systemInfo.version || 'unknown', uptime: systemInfo.uptime_seconds ? `${Math.floor(systemInfo.uptime_seconds / 3600)}h` : 'unknown', pools: poolsFormatted, }; } catch (error) { throw new Error(`Failed to get TrueNAS status: ${(error as Error).message}`); } } export async function getTrueNASAlerts(): Promise<{ alerts: TrueNASAlert[] }> { try { const alerts = await truenasRequest('/alert/list'); const alertsFormatted = alerts.map((alert: any) => ({ level: (alert.level || 'INFO').toUpperCase() as 'INFO' | 'WARNING' | 'CRITICAL', message: alert.formatted || alert.text || 'No message', datetime: alert.datetime || new Date().toISOString(), })); return { alerts: alertsFormatted }; } catch (error) { throw new Error(`Failed to get TrueNAS alerts: ${(error as Error).message}`); } } // Level 3 - Configure export async function listTrueNASDatasets(): Promise<{ datasets: TrueNASDataset[] }> { try { const datasets = await truenasRequest('/pool/dataset'); const datasetsFormatted = datasets.map((ds: any) => { const poolName = ds.name?.split('/')[0] || 'unknown'; return { name: ds.name, pool: poolName, used: formatBytes(ds.used?.parsed || 0), available: formatBytes(ds.available?.parsed || 0), mountpoint: ds.mountpoint || 'none', }; }); return { datasets: datasetsFormatted }; } catch (error) { throw new Error(`Failed to list TrueNAS datasets: ${(error as Error).message}`); } } export async function getTrueNASDatasetInfo(dataset: string): Promise<TrueNASDatasetInfo> { try { const ds = await truenasRequest(`/pool/dataset/id/${encodeURIComponent(dataset)}`); const poolName = ds.name?.split('/')[0] || 'unknown'; // Get snapshots count const snapshots = await truenasRequest(`/zfs/snapshot?dataset=${encodeURIComponent(dataset)}`); return { name: ds.name, pool: poolName, used: formatBytes(ds.used?.parsed || 0), available: formatBytes(ds.available?.parsed || 0), compression: ds.compression?.value || 'off', mountpoint: ds.mountpoint || 'none', snapshots: snapshots.length || 0, }; } catch (error) { throw new Error(`Failed to get dataset info: ${(error as Error).message}`); } } // Level 4 - Manage export async function createTrueNASSnapshot( dataset: string, name?: string ): Promise<OperationResult & { snapshot: string }> { try { const snapshotName = name || `snapshot-${Date.now()}`; const fullSnapshotName = `${dataset}@${snapshotName}`; await truenasRequest('/zfs/snapshot', 'POST', { dataset, name: snapshotName, }); return { success: true, snapshot: fullSnapshotName, message: `Snapshot ${fullSnapshotName} created successfully`, }; } catch (error) { return { success: false, snapshot: `${dataset}@${name || 'unknown'}`, message: `Failed to create snapshot: ${(error as Error).message}`, }; } } // Helper functions function formatBytes(bytes: number): string { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bshandley/homelab-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server