mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 15:51:30 +00:00
feat(M34): dynamic issuer configuration with encrypted config storage
Replace static env-var-based issuer wiring with GUI-driven dynamic configuration stored encrypted in PostgreSQL. Operators can now configure, test, enable/disable, and manage issuers from the dashboard without restarting the server. Key changes: - AES-256-GCM encryption for sensitive issuer config at rest (PBKDF2 key derivation with 100k iterations) - Dynamic IssuerRegistry with sync.RWMutex replacing static map - Connector factory pattern (issuerfactory.NewFromConfig) replacing 140 lines of static wiring in main.go - Migration 000009: encrypted_config, last_tested_at, test_status, source columns on issuers table - Env var seeding on first boot with ON CONFLICT DO NOTHING - Registry Rebuild() for atomic map swap after CRUD operations - Issuer type validation against domain constants on Create - Audit trail for test connection results - Conditional seeding for step-ca/OpenSSL (only when env vars set) - GUI: source badge, connection test status on issuer detail page Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -142,6 +142,12 @@ export interface Issuer {
|
||||
status: string;
|
||||
/** Backend returns enabled boolean; status is derived from this */
|
||||
enabled: boolean;
|
||||
/** Timestamp of last connection test */
|
||||
last_tested_at?: string;
|
||||
/** Result of last connection test: "untested", "success", or "failed" */
|
||||
test_status?: string;
|
||||
/** Config source: "database" (GUI-created) or "env" (env var seeded) */
|
||||
source?: string;
|
||||
created_at: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ export default function IssuerDetailPage() {
|
||||
|
||||
const testMutation = useMutation({
|
||||
mutationFn: () => testIssuerConnection(id!),
|
||||
onSuccess: () => refetch(),
|
||||
});
|
||||
|
||||
if (error) {
|
||||
@@ -128,6 +129,22 @@ export default function IssuerDetailPage() {
|
||||
<InfoRow label="Name" value={issuer.name} />
|
||||
<InfoRow label="Type" value={typeLabels[issuer.type] || issuer.type} />
|
||||
<InfoRow label="Status" value={<StatusBadge status={issuerStatus(issuer)} />} />
|
||||
<InfoRow label="Source" value={
|
||||
<span className={`text-xs px-2 py-0.5 rounded-full ${
|
||||
issuer.source === 'env' ? 'bg-amber-100 text-amber-700' : 'bg-blue-100 text-blue-700'
|
||||
}`}>
|
||||
{issuer.source === 'env' ? 'Environment Variable' : 'GUI Configured'}
|
||||
</span>
|
||||
} />
|
||||
<InfoRow label="Connection Test" value={
|
||||
issuer.test_status === 'success' ? (
|
||||
<span className="text-xs text-emerald-600 font-medium">Passed {issuer.last_tested_at ? formatDateTime(issuer.last_tested_at) : ''}</span>
|
||||
) : issuer.test_status === 'failed' ? (
|
||||
<span className="text-xs text-red-600 font-medium">Failed {issuer.last_tested_at ? formatDateTime(issuer.last_tested_at) : ''}</span>
|
||||
) : (
|
||||
<span className="text-xs text-ink-faint">Not tested</span>
|
||||
)
|
||||
} />
|
||||
<InfoRow label="Created" value={formatDateTime(issuer.created_at)} />
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user