import * as plugins from '../plugins.js'; import * as appstate from '../appstate.js'; import * as shared from './shared/index.js'; import { DeesElement, customElement, html, state, css, cssManager, type TemplateResult, } from '@design.estate/dees-element'; import { type IStatsTile } from '@design.estate/dees-catalog'; @customElement('objst-view-config') export class ObjstViewConfig extends DeesElement { @state() accessor configState: appstate.IConfigState = { config: null }; constructor() { super(); const sub = appstate.configStatePart .select((s) => s) .subscribe((configState) => { this.configState = configState; }); this.rxSubscriptions.push(sub); } async connectedCallback() { super.connectedCallback(); appstate.configStatePart.dispatchAction(appstate.fetchConfigAction, null); } public static styles = [ cssManager.defaultStyles, shared.viewHostCss, css` .sectionSpacer { margin-top: 32px; } .infoPanel { margin-top: 32px; padding: 24px; border-radius: 8px; background: ${cssManager.bdTheme('#f5f5f5', '#1a1a2e')}; border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#2a2a4a')}; } .infoPanel h2 { margin: 0 0 16px 0; font-size: 18px; font-weight: 600; color: ${cssManager.bdTheme('#333', '#ccc')}; } .infoPanel p { margin: 0 0 16px 0; font-size: 14px; color: ${cssManager.bdTheme('#666', '#999')}; line-height: 1.5; } .infoPanel .row { display: flex; align-items: center; margin-bottom: 8px; font-size: 14px; } .infoPanel .label { min-width: 260px; font-family: monospace; font-weight: 500; color: ${cssManager.bdTheme('#1565c0', '#64b5f6')}; padding: 4px 8px; border-radius: 4px; background: ${cssManager.bdTheme('#e3f2fd', '#1a237e30')}; } .infoPanel .value { color: ${cssManager.bdTheme('#666', '#999')}; margin-left: 12px; } .driveList { margin-top: 16px; } .driveList .driveItem { display: flex; align-items: center; margin-bottom: 8px; font-size: 14px; } .driveList .driveIndex { width: 32px; height: 32px; border-radius: 6px; background: ${cssManager.bdTheme('#e8eaf6', '#1a237e40')}; color: ${cssManager.bdTheme('#3f51b5', '#7986cb')}; display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 13px; margin-right: 12px; } .driveList .drivePath { font-family: monospace; color: ${cssManager.bdTheme('#333', '#e0e0e0')}; padding: 6px 12px; border-radius: 4px; background: ${cssManager.bdTheme('#e8e8e8', '#252540')}; } `, ]; public render(): TemplateResult { const config = this.configState.config; const serverTiles: IStatsTile[] = [ { id: 'objstPort', title: 'Storage API Port', value: config?.objstPort ?? '--', type: 'number', icon: 'lucide:network', color: '#2196f3', }, { id: 'uiPort', title: 'UI Port', value: config?.uiPort ?? '--', type: 'number', icon: 'lucide:monitor', color: '#00bcd4', }, { id: 'region', title: 'Region', value: config?.region ?? '--', type: 'text', icon: 'lucide:globe', color: '#607d8b', }, { id: 'storageDir', title: 'Storage Directory', value: config?.storageDirectory ?? '--', type: 'text', icon: 'lucide:hardDrive', color: '#9c27b0', }, { id: 'auth', title: 'Authentication', value: config?.authEnabled ? 'Enabled' : 'Disabled', type: 'text', icon: 'lucide:shield', color: config?.authEnabled ? '#4caf50' : '#f44336', }, { id: 'cors', title: 'CORS', value: config?.corsEnabled ? 'Enabled' : 'Disabled', type: 'text', icon: 'lucide:globe2', color: config?.corsEnabled ? '#4caf50' : '#ff9800', }, ]; const clusterTiles: IStatsTile[] = [ { id: 'clusterStatus', title: 'Cluster Status', value: config?.clusterEnabled ? 'Enabled' : 'Disabled', type: 'text', icon: 'lucide:network', color: config?.clusterEnabled ? '#4caf50' : '#ff9800', }, { id: 'nodeId', title: 'Node ID', value: config?.clusterNodeId || '(auto)', type: 'text', icon: 'lucide:fingerprint', color: '#607d8b', }, { id: 'quicPort', title: 'QUIC Port', value: config?.clusterQuicPort ?? 4433, type: 'number', icon: 'lucide:radio', color: '#00bcd4', }, { id: 'seedNodes', title: 'Seed Nodes', value: config?.clusterSeedNodes?.length ?? 0, type: 'number', icon: 'lucide:gitBranch', color: '#3f51b5', description: config?.clusterSeedNodes?.length ? config.clusterSeedNodes.join(', ') : 'No seed nodes configured', }, { id: 'heartbeatInterval', title: 'Heartbeat Interval', value: `${config?.clusterHeartbeatIntervalMs ?? 5000}ms`, type: 'text', icon: 'lucide:heartPulse', color: '#e91e63', }, { id: 'heartbeatTimeout', title: 'Heartbeat Timeout', value: `${config?.clusterHeartbeatTimeoutMs ?? 30000}ms`, type: 'text', icon: 'lucide:timer', color: '#ff5722', }, ]; const erasureTiles: IStatsTile[] = [ { id: 'dataShards', title: 'Data Shards', value: config?.erasureDataShards ?? 4, type: 'number', icon: 'lucide:layers', color: '#2196f3', }, { id: 'parityShards', title: 'Parity Shards', value: config?.erasureParityShards ?? 2, type: 'number', icon: 'lucide:shieldCheck', color: '#4caf50', }, { id: 'chunkSize', title: 'Chunk Size', value: this.formatBytes(config?.erasureChunkSizeBytes ?? 4194304), type: 'text', icon: 'lucide:puzzle', color: '#9c27b0', description: `${config?.erasureDataShards ?? 4}+${config?.erasureParityShards ?? 2} = ${Math.round(((config?.erasureParityShards ?? 2) / (config?.erasureDataShards ?? 4)) * 100)}% overhead`, }, ]; const drivePaths = config?.drivePaths?.length ? config.drivePaths : config?.storageDirectory ? [config.storageDirectory] : ['/data']; const driveTiles: IStatsTile[] = [ { id: 'driveCount', title: 'Drive Count', value: drivePaths.length, type: 'number', icon: 'lucide:hardDrive', color: '#3f51b5', }, ]; const refreshAction = { name: 'Refresh', iconName: 'lucide:refreshCw', action: async () => { await appstate.configStatePart.dispatchAction(appstate.fetchConfigAction, null); }, }; return html` Server Configuration
Cluster Configuration
${config?.clusterEnabled ? html`
Erasure Coding
` : ''}
Storage Drives
${drivePaths.map((path, i) => html`
${i + 1}
${path}
`)}

Configuration Reference

Cluster and drive settings are applied at server startup. To change them, set the environment variables and restart the server.

OBJST_CLUSTER_ENABLEDEnable clustering (true/false)
OBJST_CLUSTER_NODE_IDUnique node identifier
OBJST_CLUSTER_QUIC_PORTQUIC transport port (default: 4433)
OBJST_CLUSTER_SEED_NODESComma-separated seed node addresses
OBJST_DRIVE_PATHSComma-separated drive mount paths
OBJST_ERASURE_DATA_SHARDSData shards for erasure coding (default: 4)
OBJST_ERASURE_PARITY_SHARDSParity shards for erasure coding (default: 2)
OBJST_ERASURE_CHUNK_SIZEChunk size in bytes (default: 4194304)
OBJST_HEARTBEAT_INTERVAL_MSHeartbeat interval in ms (default: 5000)
OBJST_HEARTBEAT_TIMEOUT_MSHeartbeat timeout in ms (default: 30000)
`; } private formatBytes(bytes: number): string { if (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(i === 0 ? 0 : 1)} ${units[i]}`; } }