feat: split config into 4 tabs and fix JAR upload 413
Config sub-tabs are now: Monitoring | Traces & Taps | Route Recording | Resources (renamed from Agent/Infrastructure, with traces and recording as their own tabs). Also increase Spring multipart max-file-size and max-request-size to 200MB to fix HTTP 413 on JAR uploads. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,10 @@ server:
|
|||||||
port: 8081
|
port: 8081
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 200MB
|
||||||
|
max-request-size: 200MB
|
||||||
datasource:
|
datasource:
|
||||||
url: ${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/cameleer3?currentSchema=tenant_${cameleer.tenant.id}}
|
url: ${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/cameleer3?currentSchema=tenant_${cameleer.tenant.id}}
|
||||||
username: ${SPRING_DATASOURCE_USERNAME:cameleer}
|
username: ${SPRING_DATASOURCE_USERNAME:cameleer}
|
||||||
|
|||||||
@@ -471,7 +471,7 @@ function ConfigSubTab({ app, environment }: { app: App; environment?: Environmen
|
|||||||
const { data: processorToRoute = {} } = useProcessorRouteMapping(app.slug);
|
const { data: processorToRoute = {} } = useProcessorRouteMapping(app.slug);
|
||||||
const isProd = environment?.production ?? false;
|
const isProd = environment?.production ?? false;
|
||||||
const [editing, setEditing] = useState(false);
|
const [editing, setEditing] = useState(false);
|
||||||
const [configTab, setConfigTab] = useState<'agent' | 'infra'>('agent');
|
const [configTab, setConfigTab] = useState<'monitoring' | 'traces' | 'recording' | 'resources'>('monitoring');
|
||||||
|
|
||||||
const appRoutes: RouteSummary[] = useMemo(() => {
|
const appRoutes: RouteSummary[] = useMemo(() => {
|
||||||
if (!catalog) return [];
|
if (!catalog) return [];
|
||||||
@@ -664,78 +664,80 @@ function ConfigSubTab({ app, environment }: { app: App; environment?: Environmen
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={styles.subTabs}>
|
<div className={styles.subTabs}>
|
||||||
<button className={`${styles.subTab} ${configTab === 'agent' ? styles.subTabActive : ''}`} onClick={() => setConfigTab('agent')}>Agent</button>
|
<button className={`${styles.subTab} ${configTab === 'monitoring' ? styles.subTabActive : ''}`} onClick={() => setConfigTab('monitoring')}>Monitoring</button>
|
||||||
<button className={`${styles.subTab} ${configTab === 'infra' ? styles.subTabActive : ''}`} onClick={() => setConfigTab('infra')}>Infrastructure</button>
|
<button className={`${styles.subTab} ${configTab === 'traces' ? styles.subTabActive : ''}`} onClick={() => setConfigTab('traces')}>Traces & Taps</button>
|
||||||
|
<button className={`${styles.subTab} ${configTab === 'recording' ? styles.subTabActive : ''}`} onClick={() => setConfigTab('recording')}>Route Recording</button>
|
||||||
|
<button className={`${styles.subTab} ${configTab === 'resources' ? styles.subTabActive : ''}`} onClick={() => setConfigTab('resources')}>Resources</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{configTab === 'agent' && (
|
{configTab === 'monitoring' && (
|
||||||
<>
|
<div className={styles.configGrid}>
|
||||||
{/* Observability Settings */}
|
<span className={styles.configLabel}>Engine Level</span>
|
||||||
<SectionHeader>Observability</SectionHeader>
|
<Select disabled={!editing} value={engineLevel} onChange={(e) => setEngineLevel(e.target.value)}
|
||||||
<div className={styles.configGrid}>
|
options={[{ value: 'NONE', label: 'NONE' }, { value: 'MINIMAL', label: 'MINIMAL' }, { value: 'REGULAR', label: 'REGULAR' }, { value: 'COMPLETE', label: 'COMPLETE' }]} />
|
||||||
<span className={styles.configLabel}>Engine Level</span>
|
|
||||||
<Select disabled={!editing} value={engineLevel} onChange={(e) => setEngineLevel(e.target.value)}
|
|
||||||
options={[{ value: 'NONE', label: 'NONE' }, { value: 'MINIMAL', label: 'MINIMAL' }, { value: 'REGULAR', label: 'REGULAR' }, { value: 'COMPLETE', label: 'COMPLETE' }]} />
|
|
||||||
|
|
||||||
<span className={styles.configLabel}>Payload Capture</span>
|
<span className={styles.configLabel}>Payload Capture</span>
|
||||||
<Select disabled={!editing} value={payloadCapture} onChange={(e) => setPayloadCapture(e.target.value)}
|
<Select disabled={!editing} value={payloadCapture} onChange={(e) => setPayloadCapture(e.target.value)}
|
||||||
options={[{ value: 'NONE', label: 'NONE' }, { value: 'INPUT', label: 'INPUT' }, { value: 'OUTPUT', label: 'OUTPUT' }, { value: 'BOTH', label: 'BOTH' }]} />
|
options={[{ value: 'NONE', label: 'NONE' }, { value: 'INPUT', label: 'INPUT' }, { value: 'OUTPUT', label: 'OUTPUT' }, { value: 'BOTH', label: 'BOTH' }]} />
|
||||||
|
|
||||||
<span className={styles.configLabel}>Max Payload Size</span>
|
<span className={styles.configLabel}>Max Payload Size</span>
|
||||||
<div className={styles.configInline}>
|
<div className={styles.configInline}>
|
||||||
<Input disabled={!editing} value={payloadSize} onChange={(e) => setPayloadSize(e.target.value)} style={{ width: 70 }} />
|
<Input disabled={!editing} value={payloadSize} onChange={(e) => setPayloadSize(e.target.value)} style={{ width: 70 }} />
|
||||||
<Select disabled={!editing} value={payloadUnit} onChange={(e) => setPayloadUnit(e.target.value)} style={{ width: 90 }}
|
<Select disabled={!editing} value={payloadUnit} onChange={(e) => setPayloadUnit(e.target.value)} style={{ width: 90 }}
|
||||||
options={[{ value: 'bytes', label: 'bytes' }, { value: 'KB', label: 'KB' }, { value: 'MB', label: 'MB' }]} />
|
options={[{ value: 'bytes', label: 'bytes' }, { value: 'KB', label: 'KB' }, { value: 'MB', label: 'MB' }]} />
|
||||||
</div>
|
|
||||||
|
|
||||||
<span className={styles.configLabel}>App Log Level</span>
|
|
||||||
<Select disabled={!editing} value={appLogLevel} onChange={(e) => setAppLogLevel(e.target.value)}
|
|
||||||
options={['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'].map((l) => ({ value: l, label: l }))} />
|
|
||||||
|
|
||||||
<span className={styles.configLabel}>Agent Log Level</span>
|
|
||||||
<Select disabled={!editing} value={agentLogLevel} onChange={(e) => setAgentLogLevel(e.target.value)}
|
|
||||||
options={['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'].map((l) => ({ value: l, label: l }))} />
|
|
||||||
|
|
||||||
<span className={styles.configLabel}>Metrics</span>
|
|
||||||
<div className={styles.configInline}>
|
|
||||||
<Toggle checked={metricsEnabled} onChange={() => editing && setMetricsEnabled(!metricsEnabled)} disabled={!editing} />
|
|
||||||
<span className={metricsEnabled ? styles.toggleEnabled : styles.toggleDisabled}>{metricsEnabled ? 'Enabled' : 'Disabled'}</span>
|
|
||||||
<span className={styles.cellMeta} style={{ marginLeft: 8 }}>Interval</span>
|
|
||||||
<Input disabled={!editing} value={metricsInterval} onChange={(e) => setMetricsInterval(e.target.value)} style={{ width: 50 }} />
|
|
||||||
<span className={styles.cellMeta}>s</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span className={styles.configLabel}>Sampling Rate</span>
|
|
||||||
<Input disabled={!editing} value={samplingRate} onChange={(e) => setSamplingRate(e.target.value)} style={{ width: 80 }} />
|
|
||||||
|
|
||||||
<span className={styles.configLabel}>Compress Success</span>
|
|
||||||
<div className={styles.configInline}>
|
|
||||||
<Toggle checked={compressSuccess} onChange={() => editing && setCompressSuccess(!compressSuccess)} disabled={!editing} />
|
|
||||||
<span className={compressSuccess ? styles.toggleEnabled : styles.toggleDisabled}>{compressSuccess ? 'Enabled' : 'Disabled'}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span className={styles.configLabel}>Replay</span>
|
|
||||||
<div className={styles.configInline}>
|
|
||||||
<Toggle checked={replayEnabled} onChange={() => editing && setReplayEnabled(!replayEnabled)} disabled={!editing} />
|
|
||||||
<span className={replayEnabled ? styles.toggleEnabled : styles.toggleDisabled}>{replayEnabled ? 'Enabled' : 'Disabled'}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span className={styles.configLabel}>Route Control</span>
|
|
||||||
<div className={styles.configInline}>
|
|
||||||
<Toggle checked={routeControlEnabled} onChange={() => editing && setRouteControlEnabled(!routeControlEnabled)} disabled={!editing} />
|
|
||||||
<span className={routeControlEnabled ? styles.toggleEnabled : styles.toggleDisabled}>{routeControlEnabled ? 'Enabled' : 'Disabled'}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Traces & Taps */}
|
<span className={styles.configLabel}>App Log Level</span>
|
||||||
<SectionHeader>Traces & Taps</SectionHeader>
|
<Select disabled={!editing} value={appLogLevel} onChange={(e) => setAppLogLevel(e.target.value)}
|
||||||
|
options={['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'].map((l) => ({ value: l, label: l }))} />
|
||||||
|
|
||||||
|
<span className={styles.configLabel}>Agent Log Level</span>
|
||||||
|
<Select disabled={!editing} value={agentLogLevel} onChange={(e) => setAgentLogLevel(e.target.value)}
|
||||||
|
options={['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'].map((l) => ({ value: l, label: l }))} />
|
||||||
|
|
||||||
|
<span className={styles.configLabel}>Metrics</span>
|
||||||
|
<div className={styles.configInline}>
|
||||||
|
<Toggle checked={metricsEnabled} onChange={() => editing && setMetricsEnabled(!metricsEnabled)} disabled={!editing} />
|
||||||
|
<span className={metricsEnabled ? styles.toggleEnabled : styles.toggleDisabled}>{metricsEnabled ? 'Enabled' : 'Disabled'}</span>
|
||||||
|
<span className={styles.cellMeta} style={{ marginLeft: 8 }}>Interval</span>
|
||||||
|
<Input disabled={!editing} value={metricsInterval} onChange={(e) => setMetricsInterval(e.target.value)} style={{ width: 50 }} />
|
||||||
|
<span className={styles.cellMeta}>s</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className={styles.configLabel}>Sampling Rate</span>
|
||||||
|
<Input disabled={!editing} value={samplingRate} onChange={(e) => setSamplingRate(e.target.value)} style={{ width: 80 }} />
|
||||||
|
|
||||||
|
<span className={styles.configLabel}>Compress Success</span>
|
||||||
|
<div className={styles.configInline}>
|
||||||
|
<Toggle checked={compressSuccess} onChange={() => editing && setCompressSuccess(!compressSuccess)} disabled={!editing} />
|
||||||
|
<span className={compressSuccess ? styles.toggleEnabled : styles.toggleDisabled}>{compressSuccess ? 'Enabled' : 'Disabled'}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className={styles.configLabel}>Replay</span>
|
||||||
|
<div className={styles.configInline}>
|
||||||
|
<Toggle checked={replayEnabled} onChange={() => editing && setReplayEnabled(!replayEnabled)} disabled={!editing} />
|
||||||
|
<span className={replayEnabled ? styles.toggleEnabled : styles.toggleDisabled}>{replayEnabled ? 'Enabled' : 'Disabled'}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className={styles.configLabel}>Route Control</span>
|
||||||
|
<div className={styles.configInline}>
|
||||||
|
<Toggle checked={routeControlEnabled} onChange={() => editing && setRouteControlEnabled(!routeControlEnabled)} disabled={!editing} />
|
||||||
|
<span className={routeControlEnabled ? styles.toggleEnabled : styles.toggleDisabled}>{routeControlEnabled ? 'Enabled' : 'Disabled'}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{configTab === 'traces' && (
|
||||||
|
<>
|
||||||
<span className={styles.sectionSummary}>{tracedCount} traced · {tapCount} taps</span>
|
<span className={styles.sectionSummary}>{tracedCount} traced · {tapCount} taps</span>
|
||||||
{tracedTapRows.length > 0
|
{tracedTapRows.length > 0
|
||||||
? <DataTable<TracedTapRow> columns={tracedTapColumns} data={tracedTapRows} pageSize={20} flush />
|
? <DataTable<TracedTapRow> columns={tracedTapColumns} data={tracedTapRows} pageSize={20} flush />
|
||||||
: <p className={styles.emptyNote}>No processor traces or taps configured.</p>}
|
: <p className={styles.emptyNote}>No processor traces or taps configured.</p>}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Route Recording */}
|
{configTab === 'recording' && (
|
||||||
<SectionHeader>Route Recording</SectionHeader>
|
<>
|
||||||
<span className={styles.sectionSummary}>{recordingCount} of {routeRecordingRows.length} routes recording</span>
|
<span className={styles.sectionSummary}>{recordingCount} of {routeRecordingRows.length} routes recording</span>
|
||||||
{routeRecordingRows.length > 0
|
{routeRecordingRows.length > 0
|
||||||
? <DataTable<RouteRecordingRow> columns={routeRecordingColumns} data={routeRecordingRows} pageSize={20} flush />
|
? <DataTable<RouteRecordingRow> columns={routeRecordingColumns} data={routeRecordingRows} pageSize={20} flush />
|
||||||
@@ -743,7 +745,7 @@ function ConfigSubTab({ app, environment }: { app: App; environment?: Environmen
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{configTab === 'infra' && (
|
{configTab === 'resources' && (
|
||||||
<>
|
<>
|
||||||
{/* Container Resources */}
|
{/* Container Resources */}
|
||||||
<SectionHeader>Container Resources</SectionHeader>
|
<SectionHeader>Container Resources</SectionHeader>
|
||||||
|
|||||||
Reference in New Issue
Block a user