update
This commit is contained in:
@@ -18,6 +18,7 @@ import type { GitopsViewGroups } from './views/groups/index.js';
|
||||
import type { GitopsViewSecrets } from './views/secrets/index.js';
|
||||
import type { GitopsViewPipelines } from './views/pipelines/index.js';
|
||||
import type { GitopsViewBuildlog } from './views/buildlog/index.js';
|
||||
import type { GitopsViewActions } from './views/actions/index.js';
|
||||
|
||||
@customElement('gitops-dashboard')
|
||||
export class GitopsDashboard extends DeesElement {
|
||||
@@ -39,6 +40,7 @@ export class GitopsDashboard extends DeesElement {
|
||||
{ name: 'Secrets', iconName: 'lucide:key', element: (async () => (await import('./views/secrets/index.js')).GitopsViewSecrets)() },
|
||||
{ name: 'Pipelines', iconName: 'lucide:play', element: (async () => (await import('./views/pipelines/index.js')).GitopsViewPipelines)() },
|
||||
{ name: 'Build Log', iconName: 'lucide:scrollText', element: (async () => (await import('./views/buildlog/index.js')).GitopsViewBuildlog)() },
|
||||
{ name: 'Actions', iconName: 'lucide:zap', element: (async () => (await import('./views/actions/index.js')).GitopsViewActions)() },
|
||||
];
|
||||
|
||||
private resolvedViewTabs: Array<{ name: string; iconName: string; element: any }> = [];
|
||||
|
||||
@@ -6,3 +6,4 @@ import './views/groups/index.js';
|
||||
import './views/secrets/index.js';
|
||||
import './views/pipelines/index.js';
|
||||
import './views/buildlog/index.js';
|
||||
import './views/actions/index.js';
|
||||
|
||||
217
ts_web/elements/views/actions/index.ts
Normal file
217
ts_web/elements/views/actions/index.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
import * as plugins from '../../../plugins.js';
|
||||
import * as interfaces from '../../../../ts_interfaces/index.js';
|
||||
import { viewHostCss } from '../../shared/index.js';
|
||||
import {
|
||||
DeesElement,
|
||||
customElement,
|
||||
html,
|
||||
state,
|
||||
css,
|
||||
cssManager,
|
||||
type TemplateResult,
|
||||
} from '@design.estate/dees-element';
|
||||
|
||||
@customElement('gitops-view-actions')
|
||||
export class GitopsViewActions extends DeesElement {
|
||||
@state()
|
||||
accessor lastScanTimestamp: number = 0;
|
||||
|
||||
@state()
|
||||
accessor isScanning: boolean = false;
|
||||
|
||||
@state()
|
||||
accessor lastResult: {
|
||||
connectionsScanned: number;
|
||||
secretsFound: number;
|
||||
errors: string[];
|
||||
durationMs: number;
|
||||
} | null = null;
|
||||
|
||||
@state()
|
||||
accessor statusError: string = '';
|
||||
|
||||
public static styles = [
|
||||
cssManager.defaultStyles,
|
||||
viewHostCss,
|
||||
css`
|
||||
.action-cards {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 24px;
|
||||
max-width: 720px;
|
||||
}
|
||||
.action-card {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 12px;
|
||||
padding: 28px;
|
||||
}
|
||||
.action-card-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.action-card-description {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 140px 1fr;
|
||||
gap: 8px 16px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.info-label {
|
||||
color: #888;
|
||||
}
|
||||
.info-value {
|
||||
color: #ddd;
|
||||
font-family: monospace;
|
||||
}
|
||||
.info-value.scanning {
|
||||
color: #f0c040;
|
||||
}
|
||||
.info-value.error {
|
||||
color: #ff6060;
|
||||
}
|
||||
.button-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
.errors-list {
|
||||
margin-top: 12px;
|
||||
max-height: 160px;
|
||||
overflow-y: auto;
|
||||
font-size: 12px;
|
||||
color: #ff8080;
|
||||
font-family: monospace;
|
||||
line-height: 1.6;
|
||||
background: rgba(255, 0, 0, 0.05);
|
||||
border-radius: 6px;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
const lastScanFormatted = this.lastScanTimestamp
|
||||
? new Date(this.lastScanTimestamp).toLocaleString()
|
||||
: 'Never';
|
||||
|
||||
return html`
|
||||
<div class="view-title">Actions</div>
|
||||
<div class="view-description">System actions and maintenance tasks</div>
|
||||
<div class="action-cards">
|
||||
<div class="action-card">
|
||||
<div class="action-card-title">Secrets Cache Scan</div>
|
||||
<div class="action-card-description">
|
||||
Secrets are automatically scanned and cached every 24 hours.
|
||||
Use "Force Full Scan" to trigger an immediate refresh of all secrets
|
||||
across all connections, projects, and groups.
|
||||
</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-label">Status</div>
|
||||
<div class="info-value ${this.isScanning ? 'scanning' : ''}">
|
||||
${this.isScanning ? 'Scanning...' : 'Idle'}
|
||||
</div>
|
||||
<div class="info-label">Last Scan</div>
|
||||
<div class="info-value">${lastScanFormatted}</div>
|
||||
${this.lastResult ? html`
|
||||
<div class="info-label">Connections</div>
|
||||
<div class="info-value">${this.lastResult.connectionsScanned}</div>
|
||||
<div class="info-label">Secrets Found</div>
|
||||
<div class="info-value">${this.lastResult.secretsFound}</div>
|
||||
<div class="info-label">Duration</div>
|
||||
<div class="info-value">${(this.lastResult.durationMs / 1000).toFixed(1)}s</div>
|
||||
${this.lastResult.errors.length > 0 ? html`
|
||||
<div class="info-label">Errors</div>
|
||||
<div class="info-value error">${this.lastResult.errors.length}</div>
|
||||
` : ''}
|
||||
` : ''}
|
||||
</div>
|
||||
${this.statusError ? html`
|
||||
<div class="errors-list">${this.statusError}</div>
|
||||
` : ''}
|
||||
${this.lastResult?.errors?.length ? html`
|
||||
<div class="errors-list">
|
||||
${this.lastResult.errors.map((e) => html`<div>${e}</div>`)}
|
||||
</div>
|
||||
` : ''}
|
||||
<div class="button-row">
|
||||
<dees-button
|
||||
.disabled=${this.isScanning}
|
||||
@click=${() => this.forceScan()}
|
||||
>Force Full Scan</dees-button>
|
||||
<dees-button
|
||||
@click=${() => this.refreshStatus()}
|
||||
>Refresh Status</dees-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async firstUpdated() {
|
||||
await this.refreshStatus();
|
||||
}
|
||||
|
||||
private getIdentity(): interfaces.data.IIdentity | null {
|
||||
try {
|
||||
const stored = localStorage.getItem('smartstate_loginStatePart');
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored);
|
||||
return parsed.identity || null;
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private async refreshStatus(): Promise<void> {
|
||||
const identity = this.getIdentity();
|
||||
if (!identity) return;
|
||||
|
||||
try {
|
||||
this.statusError = '';
|
||||
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_GetScanStatus
|
||||
>('/typedrequest', 'getScanStatus');
|
||||
const response = await typedRequest.fire({ identity });
|
||||
this.lastScanTimestamp = response.lastScanTimestamp;
|
||||
this.isScanning = response.isScanning;
|
||||
this.lastResult = response.lastResult;
|
||||
} catch (err) {
|
||||
this.statusError = `Failed to get status: ${err}`;
|
||||
}
|
||||
}
|
||||
|
||||
private async forceScan(): Promise<void> {
|
||||
const identity = this.getIdentity();
|
||||
if (!identity) return;
|
||||
|
||||
try {
|
||||
this.statusError = '';
|
||||
this.isScanning = true;
|
||||
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_ForceScanSecrets
|
||||
>('/typedrequest', 'forceScanSecrets');
|
||||
const response = await typedRequest.fire({ identity });
|
||||
this.lastResult = {
|
||||
connectionsScanned: response.connectionsScanned,
|
||||
secretsFound: response.secretsFound,
|
||||
errors: response.errors,
|
||||
durationMs: response.durationMs,
|
||||
};
|
||||
this.lastScanTimestamp = Date.now();
|
||||
this.isScanning = false;
|
||||
} catch (err) {
|
||||
this.statusError = `Scan failed: ${err}`;
|
||||
this.isScanning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user