feat(M35): dynamic target configuration with encrypted config, test connection, and GUI updates

Mirror M34's dynamic issuer config pattern for deployment targets: AES-256-GCM
encrypted config storage, sensitive field redaction in API responses, agent
heartbeat-based test connection endpoint, and full frontend updates including
test status indicators, source badges, and removal of stale hostname/status
fields from the Target interface.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shankar0123
2026-04-04 01:09:53 -04:00
parent e19b8c95fe
commit e6088c79a3
23 changed files with 849 additions and 151 deletions
+9
View File
@@ -36,6 +36,7 @@ import {
getTargets,
createTarget,
deleteTarget,
testTargetConnection,
getProfiles,
getProfile,
createProfile,
@@ -425,6 +426,14 @@ describe('API Client', () => {
expect(url).toBe('/api/v1/targets/t-nginx');
expect(init.method).toBe('DELETE');
});
it('testTargetConnection sends POST', async () => {
mockFetch.mockReturnValueOnce(mockJsonResponse({ status: 'success', message: 'Agent is online' }));
await testTargetConnection('t-nginx');
const [url, init] = mockFetch.mock.calls[0];
expect(url).toBe('/api/v1/targets/t-nginx/test');
expect(init.method).toBe('POST');
});
});
// ─── Approval ──────────────────────────────────────
+3
View File
@@ -232,6 +232,9 @@ export const updateTarget = (id: string, data: Partial<Target>) =>
export const deleteTarget = (id: string) =>
fetchJSON<{ message: string }>(`${BASE}/targets/${id}`, { method: 'DELETE' });
export const testTargetConnection = (id: string) =>
fetchJSON<{ status: string; message: string }>(`${BASE}/targets/${id}/test`, { method: 'POST' });
// Profiles
export const getProfiles = (params: Record<string, string> = {}) => {
const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString();
+4 -2
View File
@@ -156,10 +156,12 @@ export interface Target {
id: string;
name: string;
type: string;
hostname: string;
agent_id: string;
config: Record<string, unknown>;
status: string;
enabled: boolean;
last_tested_at?: string;
test_status?: string;
source?: string;
created_at: string;
updated_at?: string;
}