fix(core): fix secrets scan upserts, connection health checks, and frontend improvements

- Add upsert pattern to SecretsScanService to prevent duplicate key errors on repeated scans
- Auto-test connection health on startup so status reflects reality
- Fix Actions view to read identity from appstate instead of broken localStorage hack
- Fetch both project and group secrets in parallel, add "All Scopes" filter to Secrets view
- Enable noCache on UtilityWebsiteServer to prevent stale browser cache
This commit is contained in:
2026-02-24 22:50:26 +00:00
parent 43131fa53c
commit e3f67d12a3
8 changed files with 78 additions and 40 deletions

View File

@@ -1,5 +1,6 @@
import * as plugins from '../../../plugins.js';
import * as interfaces from '../../../../ts_interfaces/index.js';
import * as appstate from '../../../appstate.js';
import { viewHostCss } from '../../shared/index.js';
import {
DeesElement,
@@ -160,16 +161,7 @@ export class GitopsViewActions extends DeesElement {
}
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;
return appstate.loginStatePart.getState().identity;
}
private async refreshStatus(): Promise<void> {

View File

@@ -33,7 +33,7 @@ export class GitopsViewSecrets extends DeesElement {
accessor selectedConnectionId: string = '';
@state()
accessor selectedScope: 'project' | 'group' = 'project';
accessor selectedScope: 'all' | 'project' | 'group' = 'all';
@state()
accessor selectedScopeId: string = '__all__';
@@ -71,10 +71,16 @@ export class GitopsViewSecrets extends DeesElement {
];
private get filteredSecrets() {
if (this.selectedScopeId === '__all__') {
return this.dataState.secrets;
let secrets = this.dataState.secrets;
// Filter by scope (unless "all")
if (this.selectedScope !== 'all') {
secrets = secrets.filter((s) => s.scope === this.selectedScope);
}
return this.dataState.secrets.filter((s) => s.scopeId === this.selectedScopeId);
// Filter by entity if specific one selected
if (this.selectedScopeId !== '__all__') {
secrets = secrets.filter((s) => s.scopeId === this.selectedScopeId);
}
return secrets;
}
public render(): TemplateResult {
@@ -84,20 +90,23 @@ export class GitopsViewSecrets extends DeesElement {
}));
const scopeOptions = [
{ option: 'All Scopes', key: 'all' },
{ option: 'Project', key: 'project' },
{ option: 'Group', key: 'group' },
];
const entities = this.selectedScope === 'project'
? this.dataState.projects.map((p) => ({ option: p.fullPath || p.name, key: p.id }))
: this.dataState.groups.map((g) => ({ option: g.fullPath || g.name, key: g.id }));
const entities = this.selectedScope === 'group'
? this.dataState.groups.map((g) => ({ option: g.fullPath || g.name, key: g.id }))
: this.selectedScope === 'project'
? this.dataState.projects.map((p) => ({ option: p.fullPath || p.name, key: p.id }))
: [];
const entityOptions = [
{ option: 'All', key: '__all__' },
...entities,
];
const isAllSelected = this.selectedScopeId === '__all__';
const isAllSelected = this.selectedScope === 'all' || this.selectedScopeId === '__all__';
return html`
<div class="view-title">Secrets</div>
@@ -119,20 +128,22 @@ export class GitopsViewSecrets extends DeesElement {
.options=${scopeOptions}
.selectedOption=${scopeOptions.find((o) => o.key === this.selectedScope)}
@selectedOption=${(e: CustomEvent) => {
this.selectedScope = e.detail.key as 'project' | 'group';
this.selectedScope = e.detail.key as 'all' | 'project' | 'group';
this.selectedScopeId = '__all__';
this.loadEntities();
this.loadSecrets();
}}
></dees-input-dropdown>
<dees-input-dropdown
.label=${this.selectedScope === 'project' ? 'Project' : 'Group'}
.options=${entityOptions}
.selectedOption=${entityOptions.find((o) => o.key === this.selectedScopeId) || entityOptions[0]}
@selectedOption=${(e: CustomEvent) => {
this.selectedScopeId = e.detail.key;
}}
></dees-input-dropdown>
${this.selectedScope !== 'all' ? html`
<dees-input-dropdown
.label=${this.selectedScope === 'project' ? 'Project' : 'Group'}
.options=${entityOptions}
.selectedOption=${entityOptions.find((o) => o.key === this.selectedScopeId) || entityOptions[0]}
@selectedOption=${(e: CustomEvent) => {
this.selectedScopeId = e.detail.key;
}}
></dees-input-dropdown>
` : ''}
<dees-button
.disabled=${isAllSelected}
@click=${() => this.addSecret()}
@@ -185,6 +196,7 @@ export class GitopsViewSecrets extends DeesElement {
private async loadEntities() {
if (!this.selectedConnectionId) return;
if (this.selectedScope === 'all') return;
if (this.selectedScope === 'project') {
await appstate.dataStatePart.dispatchAction(appstate.fetchProjectsAction, {
connectionId: this.selectedConnectionId,
@@ -198,14 +210,14 @@ export class GitopsViewSecrets extends DeesElement {
private async loadSecrets() {
if (!this.selectedConnectionId) return;
// Always fetch both scopes — client-side filtering handles the rest
await appstate.dataStatePart.dispatchAction(appstate.fetchAllSecretsAction, {
connectionId: this.selectedConnectionId,
scope: this.selectedScope,
});
}
private async addSecret() {
if (this.selectedScopeId === '__all__') return;
if (this.selectedScope === 'all' || this.selectedScopeId === '__all__') return;
await plugins.deesCatalog.DeesModal.createAndShow({
heading: 'Add Secret',
content: html`