improve: redesign SensitiveKeysPage with better layout and information hierarchy
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:
@@ -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.
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user