improve: redesign SensitiveKeysPage with better layout and information hierarchy
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m29s
CI / docker (push) Successful in 1m11s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 37s

Show agent built-in defaults as reference Badge pills, separate editable keys
section with count badge, amber-highlighted push toggle, right-aligned save
button. Fix info text: keys add to defaults, not replace. Add ClaimMapping
controller to CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-14 19:03:45 +02:00
parent 9ac8e3604c
commit 92d7f5809b
3 changed files with 125 additions and 35 deletions

View File

@@ -105,6 +105,7 @@ java -jar cameleer3-server-app/target/cameleer3-server-app-1.0-SNAPSHOT.jar
- `MetricsController` — GET /api/v1/metrics, GET /timeseries
- `DiagramController` — GET /api/v1/diagrams/{id}, POST /
- `DiagramRenderController` — POST /api/v1/diagrams/render (ELK layout)
- `ClaimMappingAdminController` — CRUD /api/v1/admin/claim-mappings, POST /test (accepts inline rules + claims for preview without saving)
- `LicenseAdminController` — GET/POST /api/v1/admin/license
**runtime/** — Docker orchestration
@@ -219,7 +220,7 @@ The UI has 4 main tabs: **Exchanges**, **Dashboard**, **Runtime**, **Deployments
- Deployment progress: `ui/src/components/DeploymentProgress.tsx` (7-stage step indicator)
**Admin pages** (ADMIN-only, under `/admin/`):
- **Sensitive Keys** (`ui/src/pages/Admin/SensitiveKeysPage.tsx`) — global sensitive key masking config with tag/pill editor, push-to-agents toggle. Per-app additions shown in `AppConfigDetailPage.tsx` with read-only global pills (greyed Badge) + editable per-app pills (Tag with remove).
- **Sensitive Keys** (`ui/src/pages/Admin/SensitiveKeysPage.tsx`) — global sensitive key masking config. Shows agent built-in defaults as outlined Badge reference, editable Tag pills for custom keys, amber-highlighted push-to-agents toggle. Keys add to (not replace) agent defaults. Per-app additions shown in `AppConfigDetailPage.tsx` with read-only global pills (greyed Badge) + editable per-app pills (Tag with remove).
### Key UI Files
@@ -391,7 +392,7 @@ Mean processing time = `camel.route.policy.total_time / camel.route.policy.count
<!-- gitnexus:start -->
# GitNexus — Code Intelligence
This project is indexed by GitNexus as **cameleer3-server** (6155 symbols, 15501 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
This project is indexed by GitNexus as **cameleer3-server** (6195 symbols, 15647 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.

View File

@@ -1,27 +1,71 @@
.page {
display: flex;
flex-direction: column;
gap: var(--space-lg);
gap: var(--space-md);
max-width: 720px;
}
.infoBanner {
font-size: var(--font-size-sm);
.sectionTitle {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
font-weight: 600;
color: var(--text-secondary);
background: var(--surface-secondary);
padding: var(--space-md);
border-radius: var(--radius-md);
line-height: 1.5;
letter-spacing: 0.01em;
}
.defaultsList {
display: flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
.hint {
display: flex;
align-items: flex-start;
gap: 6px;
font-size: 12px;
color: var(--text-muted);
line-height: 1.4;
padding-top: 4px;
border-top: 1px solid var(--border-subtle);
}
.hint svg {
flex-shrink: 0;
margin-top: 1px;
}
.keyCount {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 18px;
height: 18px;
padding: 0 5px;
border-radius: 9px;
background: var(--bg-hover);
color: var(--text-muted);
font-size: 12px;
font-weight: 500;
}
.pillList {
display: flex;
flex-wrap: wrap;
gap: var(--space-xs);
gap: 6px;
min-height: 36px;
align-items: center;
}
.emptyState {
color: var(--text-tertiary);
font-size: 12px;
font-style: italic;
}
.inputRow {
display: flex;
gap: var(--space-sm);
@@ -32,8 +76,20 @@
flex: 1;
}
.footer {
display: flex;
gap: var(--space-sm);
align-items: center;
.inputHint {
font-size: 12px;
color: var(--text-muted);
line-height: 1.4;
}
.pushToggle {
background: color-mix(in srgb, var(--amber) 8%, transparent);
border: 1px solid color-mix(in srgb, var(--amber) 20%, transparent);
border-radius: var(--radius-md);
padding: 8px 12px;
}
.saveRow {
display: flex;
justify-content: flex-end;
}

View File

@@ -1,10 +1,16 @@
import { useState, useEffect, useCallback } from 'react';
import { Button, SectionHeader, Tag, Input, Toggle, Label, useToast } from '@cameleer/design-system';
import { Shield, Info } from 'lucide-react';
import { Button, SectionHeader, Tag, Badge, Input, Toggle, useToast } from '@cameleer/design-system';
import { PageLoader } from '../../components/PageLoader';
import { useSensitiveKeys, useUpdateSensitiveKeys } from '../../api/queries/admin/sensitive-keys';
import styles from './SensitiveKeysPage.module.css';
import sectionStyles from '../../styles/section-card.module.css';
const AGENT_DEFAULTS = [
'Authorization', 'Cookie', 'Set-Cookie',
'X-API-Key', 'X-Auth-Token', 'Proxy-Authorization',
];
export default function SensitiveKeysPage() {
const { data, isLoading } = useSensitiveKeys();
const updateKeys = useUpdateSensitiveKeys();
@@ -65,24 +71,46 @@ export default function SensitiveKeysPage() {
if (isLoading) return <PageLoader />;
const isConfigured = draft.length > 0;
return (
<div className={styles.page}>
<SectionHeader>Sensitive Keys</SectionHeader>
<div className={styles.infoBanner}>
Agents ship with built-in defaults (Authorization, Cookie, Set-Cookie, X-API-Key,
X-Auth-Token, Proxy-Authorization). Configuring keys here replaces agent defaults for
all applications. Leave unconfigured to use agent defaults.
{/* Agent defaults reference */}
<section className={sectionStyles.section}>
<div className={styles.sectionTitle}>
<Shield size={14} />
<span>Agent built-in defaults</span>
</div>
<div className={styles.defaultsList}>
{AGENT_DEFAULTS.map((key) => (
<Badge key={key} label={key} variant="outlined" />
))}
</div>
<div className={styles.hint}>
<Info size={12} />
<span>
{isConfigured
? 'Your keys below are added to these defaults for all applications.'
: 'These headers are masked automatically. Add keys below to extend the list.'}
</span>
</div>
</section>
{/* Editable keys */}
<section className={sectionStyles.section}>
<div className={styles.sectionTitle}>
<span>Global sensitive keys</span>
{isConfigured && <span className={styles.keyCount}>{draft.length}</span>}
</div>
<div className={sectionStyles.section}>
<Label>Global sensitive keys</Label>
<div className={styles.pillList}>
{draft.map((key, i) => (
<Tag key={`${key}-${i}`} label={key} onRemove={() => removeKey(i)} />
))}
{draft.length === 0 && (
<span style={{ color: 'var(--text-tertiary)', fontSize: 'var(--font-size-sm)' }}>
{!isConfigured && (
<span className={styles.emptyState}>
No keys configured agents use built-in defaults
</span>
)}
@@ -99,18 +127,23 @@ export default function SensitiveKeysPage() {
Add
</Button>
</div>
<div className={styles.inputHint}>
Supports exact header names and glob patterns. Per-app additions can be configured on each application's settings page.
</div>
<div className={styles.footer}>
<div className={styles.pushToggle}>
<Toggle
checked={pushToAgents}
onChange={(e) => setPushToAgents((e.target as HTMLInputElement).checked)}
label="Push to all connected agents immediately"
/>
</div>
<div className={styles.saveRow}>
<Button variant="primary" onClick={handleSave} loading={updateKeys.isPending}>
Save
</Button>
</div>
</section>
</div>
);
}