feat(opsserver): add health, audit, cluster health, and durable credential management hardening
This commit is contained in:
@@ -3,12 +3,12 @@ import * as appstate from '../appstate.js';
|
||||
import * as shared from './shared/index.js';
|
||||
|
||||
import {
|
||||
DeesElement,
|
||||
customElement,
|
||||
html,
|
||||
state,
|
||||
css,
|
||||
cssManager,
|
||||
customElement,
|
||||
DeesElement,
|
||||
html,
|
||||
state,
|
||||
type TemplateResult,
|
||||
} from '@design.estate/dees-element';
|
||||
import { type IStatsTile } from '@design.estate/dees-catalog';
|
||||
@@ -16,7 +16,7 @@ import { type IStatsTile } from '@design.estate/dees-catalog';
|
||||
@customElement('objst-view-config')
|
||||
export class ObjstViewConfig extends DeesElement {
|
||||
@state()
|
||||
accessor configState: appstate.IConfigState = { config: null };
|
||||
accessor configState: appstate.IConfigState = { config: null, clusterHealth: null };
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -107,11 +107,26 @@ export class ObjstViewConfig extends DeesElement {
|
||||
border-radius: 4px;
|
||||
background: ${cssManager.bdTheme('#e8e8e8', '#252540')};
|
||||
}
|
||||
.driveList .driveStatus {
|
||||
margin-left: 12px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: ${cssManager.bdTheme('#1b5e20', '#a5d6a7')};
|
||||
background: ${cssManager.bdTheme('#e8f5e9', '#1b5e2030')};
|
||||
}
|
||||
.driveList .driveMeta {
|
||||
margin-left: 12px;
|
||||
color: ${cssManager.bdTheme('#666', '#999')};
|
||||
font-size: 13px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
const config = this.configState.config;
|
||||
const clusterHealth = this.configState.clusterHealth;
|
||||
|
||||
const serverTiles: IStatsTile[] = [
|
||||
{
|
||||
@@ -164,19 +179,20 @@ export class ObjstViewConfig extends DeesElement {
|
||||
},
|
||||
];
|
||||
|
||||
const clusterEnabled = clusterHealth?.enabled ?? config?.clusterEnabled ?? false;
|
||||
const clusterTiles: IStatsTile[] = [
|
||||
{
|
||||
id: 'clusterStatus',
|
||||
title: 'Cluster Status',
|
||||
value: config?.clusterEnabled ? 'Enabled' : 'Disabled',
|
||||
value: clusterEnabled ? 'Enabled' : 'Disabled',
|
||||
type: 'text',
|
||||
icon: 'lucide:network',
|
||||
color: config?.clusterEnabled ? '#4caf50' : '#ff9800',
|
||||
color: clusterEnabled ? '#4caf50' : '#ff9800',
|
||||
},
|
||||
{
|
||||
id: 'nodeId',
|
||||
title: 'Node ID',
|
||||
value: config?.clusterNodeId || '(auto)',
|
||||
value: clusterHealth?.nodeId || config?.clusterNodeId || '(auto)',
|
||||
type: 'text',
|
||||
icon: 'lucide:fingerprint',
|
||||
color: '#607d8b',
|
||||
@@ -216,13 +232,31 @@ export class ObjstViewConfig extends DeesElement {
|
||||
icon: 'lucide:timer',
|
||||
color: '#ff5722',
|
||||
},
|
||||
{
|
||||
id: 'quorum',
|
||||
title: 'Quorum',
|
||||
value: clusterHealth?.enabled
|
||||
? clusterHealth.quorumHealthy ? 'Healthy' : 'Degraded'
|
||||
: 'Standalone',
|
||||
type: 'text',
|
||||
icon: 'lucide:activity',
|
||||
color: clusterHealth?.quorumHealthy ? '#4caf50' : '#ff9800',
|
||||
},
|
||||
{
|
||||
id: 'peers',
|
||||
title: 'Peers',
|
||||
value: clusterHealth?.peers?.length ?? 0,
|
||||
type: 'number',
|
||||
icon: 'lucide:share2',
|
||||
color: '#3f51b5',
|
||||
},
|
||||
];
|
||||
|
||||
const erasureTiles: IStatsTile[] = [
|
||||
{
|
||||
id: 'dataShards',
|
||||
title: 'Data Shards',
|
||||
value: config?.erasureDataShards ?? 4,
|
||||
value: clusterHealth?.erasure?.dataShards ?? config?.erasureDataShards ?? 4,
|
||||
type: 'number',
|
||||
icon: 'lucide:layers',
|
||||
color: '#2196f3',
|
||||
@@ -230,7 +264,7 @@ export class ObjstViewConfig extends DeesElement {
|
||||
{
|
||||
id: 'parityShards',
|
||||
title: 'Parity Shards',
|
||||
value: config?.erasureParityShards ?? 2,
|
||||
value: clusterHealth?.erasure?.parityShards ?? config?.erasureParityShards ?? 2,
|
||||
type: 'number',
|
||||
icon: 'lucide:shieldCheck',
|
||||
color: '#4caf50',
|
||||
@@ -238,25 +272,31 @@ export class ObjstViewConfig extends DeesElement {
|
||||
{
|
||||
id: 'chunkSize',
|
||||
title: 'Chunk Size',
|
||||
value: this.formatBytes(config?.erasureChunkSizeBytes ?? 4194304),
|
||||
value: this.formatBytes(
|
||||
clusterHealth?.erasure?.chunkSizeBytes ?? 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`,
|
||||
description: `${clusterHealth?.erasure?.dataShards ?? config?.erasureDataShards ?? 4}+${
|
||||
clusterHealth?.erasure?.parityShards ?? config?.erasureParityShards ?? 2
|
||||
}`,
|
||||
},
|
||||
];
|
||||
|
||||
const drivePaths = config?.drivePaths?.length
|
||||
const drivePaths = clusterHealth?.drives?.length
|
||||
? clusterHealth.drives.map((drive) => drive.path)
|
||||
: config?.drivePaths?.length
|
||||
? config.drivePaths
|
||||
: config?.storageDirectory
|
||||
? [config.storageDirectory]
|
||||
: ['/data'];
|
||||
? [config.storageDirectory]
|
||||
: ['/data'];
|
||||
|
||||
const driveTiles: IStatsTile[] = [
|
||||
{
|
||||
id: 'driveCount',
|
||||
title: 'Drive Count',
|
||||
value: drivePaths.length,
|
||||
value: clusterHealth?.drives?.length ?? drivePaths.length,
|
||||
type: 'number',
|
||||
icon: 'lucide:hardDrive',
|
||||
color: '#3f51b5',
|
||||
@@ -274,48 +314,97 @@ export class ObjstViewConfig extends DeesElement {
|
||||
return html`
|
||||
<objst-sectionheading>Server Configuration</objst-sectionheading>
|
||||
<dees-statsgrid
|
||||
.tiles=${serverTiles}
|
||||
.gridActions=${[refreshAction]}
|
||||
.tiles="${serverTiles}"
|
||||
.gridActions="${[refreshAction]}"
|
||||
></dees-statsgrid>
|
||||
|
||||
<div class="sectionSpacer">
|
||||
<objst-sectionheading>Cluster Configuration</objst-sectionheading>
|
||||
</div>
|
||||
<dees-statsgrid .tiles=${clusterTiles}></dees-statsgrid>
|
||||
<dees-statsgrid .tiles="${clusterTiles}"></dees-statsgrid>
|
||||
|
||||
${config?.clusterEnabled ? html`
|
||||
<div class="sectionSpacer">
|
||||
<objst-sectionheading>Erasure Coding</objst-sectionheading>
|
||||
</div>
|
||||
<dees-statsgrid .tiles=${erasureTiles}></dees-statsgrid>
|
||||
` : ''}
|
||||
${clusterEnabled
|
||||
? html`
|
||||
<div class="sectionSpacer">
|
||||
<objst-sectionheading>Erasure Coding</objst-sectionheading>
|
||||
</div>
|
||||
<dees-statsgrid .tiles="${erasureTiles}"></dees-statsgrid>
|
||||
`
|
||||
: ''}
|
||||
|
||||
<div class="sectionSpacer">
|
||||
<objst-sectionheading>Storage Drives</objst-sectionheading>
|
||||
</div>
|
||||
<dees-statsgrid .tiles=${driveTiles}></dees-statsgrid>
|
||||
<dees-statsgrid .tiles="${driveTiles}"></dees-statsgrid>
|
||||
<div class="driveList">
|
||||
${drivePaths.map((path, i) => html`
|
||||
<div class="driveItem">
|
||||
<div class="driveIndex">${i + 1}</div>
|
||||
<span class="drivePath">${path}</span>
|
||||
</div>
|
||||
`)}
|
||||
${(clusterHealth?.drives?.length
|
||||
? clusterHealth.drives
|
||||
: drivePaths.map((path, index) => ({ path, index, status: 'configured' }))).map((
|
||||
drive,
|
||||
i,
|
||||
) =>
|
||||
html`
|
||||
<div class="driveItem">
|
||||
<div class="driveIndex">${i + 1}</div>
|
||||
<span class="drivePath">${drive.path}</span>
|
||||
<span class="driveStatus">${drive.status}</span>
|
||||
${drive.usedBytes !== undefined && drive.totalBytes !== undefined
|
||||
? html`
|
||||
<span class="driveMeta">${this.formatBytes(drive.usedBytes)} / ${this
|
||||
.formatBytes(drive.totalBytes)}</span>
|
||||
`
|
||||
: ''}
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div class="infoPanel">
|
||||
<h2>Configuration Reference</h2>
|
||||
<p>Cluster and drive settings are applied at server startup. To change them, set the environment variables and restart the server.</p>
|
||||
<div class="row"><span class="label">OBJST_CLUSTER_ENABLED</span><span class="value">Enable clustering (true/false)</span></div>
|
||||
<div class="row"><span class="label">OBJST_CLUSTER_NODE_ID</span><span class="value">Unique node identifier</span></div>
|
||||
<div class="row"><span class="label">OBJST_CLUSTER_QUIC_PORT</span><span class="value">QUIC transport port (default: 4433)</span></div>
|
||||
<div class="row"><span class="label">OBJST_CLUSTER_SEED_NODES</span><span class="value">Comma-separated seed node addresses</span></div>
|
||||
<div class="row"><span class="label">OBJST_DRIVE_PATHS</span><span class="value">Comma-separated drive mount paths</span></div>
|
||||
<div class="row"><span class="label">OBJST_ERASURE_DATA_SHARDS</span><span class="value">Data shards for erasure coding (default: 4)</span></div>
|
||||
<div class="row"><span class="label">OBJST_ERASURE_PARITY_SHARDS</span><span class="value">Parity shards for erasure coding (default: 2)</span></div>
|
||||
<div class="row"><span class="label">OBJST_ERASURE_CHUNK_SIZE</span><span class="value">Chunk size in bytes (default: 4194304)</span></div>
|
||||
<div class="row"><span class="label">OBJST_HEARTBEAT_INTERVAL_MS</span><span class="value">Heartbeat interval in ms (default: 5000)</span></div>
|
||||
<div class="row"><span class="label">OBJST_HEARTBEAT_TIMEOUT_MS</span><span class="value">Heartbeat timeout in ms (default: 30000)</span></div>
|
||||
<p>
|
||||
Cluster and drive settings are applied at server startup. To change them, set the environment
|
||||
variables and restart the server.
|
||||
</p>
|
||||
<div class="row">
|
||||
<span class="label">OBJST_CLUSTER_ENABLED</span><span class="value"
|
||||
>Enable clustering (true/false)</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">OBJST_CLUSTER_NODE_ID</span><span class="value"
|
||||
>Unique node identifier</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">OBJST_CLUSTER_QUIC_PORT</span><span class="value"
|
||||
>QUIC transport port (default: 4433)</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">OBJST_CLUSTER_SEED_NODES</span><span class="value"
|
||||
>Comma-separated seed node addresses</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">OBJST_DRIVE_PATHS</span><span class="value"
|
||||
>Comma-separated drive mount paths</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">OBJST_ERASURE_DATA_SHARDS</span><span class="value"
|
||||
>Data shards for erasure coding (default: 4)</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">OBJST_ERASURE_PARITY_SHARDS</span><span class="value"
|
||||
>Parity shards for erasure coding (default: 2)</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">OBJST_ERASURE_CHUNK_SIZE</span><span class="value"
|
||||
>Chunk size in bytes (default: 4194304)</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">OBJST_HEARTBEAT_INTERVAL_MS</span><span class="value"
|
||||
>Heartbeat interval in ms (default: 5000)</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">OBJST_HEARTBEAT_TIMEOUT_MS</span><span class="value"
|
||||
>Heartbeat timeout in ms (default: 30000)</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user