diff --git a/.gitignore b/.gitignore index 08a141a..188c88a 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,7 @@ certctl-agent certctl-cli /server /agent +/cli # Private strategy docs roadmap.md diff --git a/README.md b/README.md index a02d314..8abed9a 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ timeline | [Migrate from acme.sh](docs/migrate-from-acmesh.md) | Migration guide for acme.sh users with DNS-01 scripts | | [certctl for cert-manager Users](docs/certctl-for-cert-manager-users.md) | Using certctl alongside cert-manager for non-Kubernetes infrastructure | -> **Next release:** v2.1.0 will be tagged after the full V2 feature suite passes manual QA across all 34 sections of the [testing guide](docs/testing-guide.md). Automated CI (1,471 Go tests + 193 frontend tests) gates every commit; the manual playbook covers integration, deployment, and UX verification that unit tests can't reach. +> **Next release:** v2.1.0 will be tagged after the full V2 feature suite passes manual QA across all 34 sections of the [testing guide](docs/testing-guide.md). Automated CI (1,300+ Go tests + 211 frontend tests) gates every commit; the manual playbook covers integration, deployment, and UX verification that unit tests can't reach. ## Why certctl Exists @@ -59,8 +59,8 @@ For a detailed comparison with CertKit, KeyTalk, and enterprise platforms (Venaf certctl gives you a single pane of glass for every TLS certificate in your organization: -- **Web dashboard** — 22 operational pages: certificate inventory, deployment timeline with TLS verification, bulk operations (renew/revoke/reassign), discovery triage, network scan management, approval workflows, audit trail with CSV/JSON export, agent fleet overview with OS/arch grouping, short-lived credential monitoring, digest email preview -- **REST API** — 99 endpoints under `/api/v1/` + `/.well-known/est/` for complete automation, with sparse fields, sort, cursor pagination, and time-range filters +- **Web dashboard** — 24 operational pages: certificate inventory, deployment timeline with TLS verification, bulk operations (renew/revoke/reassign), discovery triage, network scan management, approval workflows, audit trail with CSV/JSON export, agent fleet overview with OS/arch grouping, short-lived credential monitoring, digest email preview +- **REST API** — 97 endpoints under `/api/v1/` + `/.well-known/est/` for complete automation, with sparse fields, sort, cursor pagination, and time-range filters - **Agents** — generate private keys locally (ECDSA P-256), discover existing certs on disk (PEM/DER), submit CSRs only (private keys never leave your servers) - **Network scanner** — discovers certificates on TLS endpoints across CIDR ranges without requiring agents, concurrent scanning with configurable timeouts - **Certificate export** — PEM (JSON or file download) and PKCS#12 formats, with audit trail; private keys never included @@ -131,7 +131,7 @@ All connectors are pluggable — build your own by implementing the [connector i Policies
Policies
Ownership, lifetime, renewal rules Profiles
Profiles
Key types, max TTL, crypto constraints -Issuers
Issuers
Local CA, ACME, step-ca connectors +Issuers
Issuers
Local CA, ACME, step-ca, Vault PKI, DigiCert Targets
Targets
NGINX, Apache, HAProxy, Traefik, Caddy deployment @@ -145,7 +145,7 @@ All connectors are pluggable — build your own by implementing the [connector i -> **22 operational GUI pages** covering the full certificate lifecycle: dashboard, certificates (list + detail with EKU badges, deployment timeline, TLS verification status), agents, fleet overview, jobs (with approval workflow), notifications, policies, profiles, issuers, targets (wizard with NGINX/Apache/HAProxy/Traefik/Caddy/F5/IIS), owners, teams, agent groups, audit trail, short-lived credentials, discovery triage, and network scan management. +> **24 operational GUI pages** covering the full certificate lifecycle: dashboard, certificates (list + detail with EKU badges, deployment timeline, TLS verification status), agents, fleet overview, jobs (list + detail with approval workflow), notifications, policies, profiles, issuers (catalog + detail), targets (list + detail + wizard), owners, teams, agent groups, audit trail, short-lived credentials, discovery triage, network scan management, digest email preview, and observability metrics. ## Quick Start @@ -166,7 +166,7 @@ docker compose -f deploy/docker-compose.yml up -d --build Wait ~30 seconds, then open **http://localhost:8443** in your browser. -The dashboard comes pre-loaded with 35 demo certificates across 5 issuers, 8 agents, 90 days of job history, discovery scan data, and network scan targets — a realistic snapshot of a certificate inventory that looks like it's been running for months. +The dashboard comes pre-loaded with 32 demo certificates across 7 issuers, 8 agents, 180 days of job history, discovery scan data, and network scan targets — a realistic snapshot of a certificate inventory that looks like it's been running for months. Verify the API: ```bash @@ -174,7 +174,7 @@ curl http://localhost:8443/health # {"status":"healthy"} curl -s http://localhost:8443/api/v1/certificates | jq '.total' -# 35 +# 32 ``` ### Agent Install (One-Liner) @@ -374,7 +374,7 @@ make docker-clean # Stop + remove volumes ## API Overview -99 endpoints under `/api/v1/` + `/.well-known/est/`, all returning JSON. List endpoints support pagination, sparse field selection (`?fields=`), sort (`?sort=-notAfter`), time-range filters, and cursor-based pagination. Full request/response schemas in the [OpenAPI 3.1 spec](api/openapi.yaml). +97 endpoints under `/api/v1/` + `/.well-known/est/`, all returning JSON. List endpoints support pagination, sparse field selection (`?fields=`), sort (`?sort=-notAfter`), time-range filters, and cursor-based pagination. Full request/response schemas in the [OpenAPI 3.1 spec](api/openapi.yaml). ### Key Endpoints ``` @@ -451,7 +451,7 @@ certctl-cli certs list --format json # JSON output (default: table) ## MCP Server (AI Integration) -certctl ships a standalone MCP (Model Context Protocol) server that exposes all 78 API endpoints as tools for AI assistants — Claude, Cursor, Windsurf, OpenClaw, VS Code Copilot, and any MCP-compatible client. +certctl ships a standalone MCP (Model Context Protocol) server that exposes all 80 API endpoints as tools for AI assistants — Claude, Cursor, Windsurf, OpenClaw, VS Code Copilot, and any MCP-compatible client. ```bash # Install @@ -487,7 +487,7 @@ Core lifecycle management — Local CA + ACME v2 issuers, NGINX target connector ### V2: Operational Maturity -30 milestones complete, 1500+ tests. See the [Feature Inventory](docs/features.md) for details on every capability. +30+ milestones complete, 1,500+ tests. See the [Feature Inventory](docs/features.md) for details on every capability. **What shipped (all ✅):** @@ -499,7 +499,7 @@ Core lifecycle management — Local CA + ACME v2 issuers, NGINX target connector - **Observability** — Prometheus + JSON metrics, 5 stats API endpoints, dashboard charts (heatmap, trends, distribution), agent fleet overview, structured logging - **EST Server** (RFC 7030) — device/WiFi certificate enrollment, PKCS#7 wire format, configurable issuer + profile binding - **MCP Server** — 78 API operations as AI tools for Claude, Cursor, and any MCP-compatible client -- **CLI** — 12 subcommands (list/get/renew/revoke certs, agents, jobs, import, status), JSON/table output +- **CLI** — 10 subcommands (list/get/renew/revoke certs, list agents/jobs, import, status, health, metrics), JSON/table output - **Notifications** — Email (SMTP), Webhooks, Slack, Microsoft Teams, PagerDuty, OpsGenie connectors - **API Enhancements** — sparse fields, sort, time-range filters, cursor pagination, immutable API audit logging - **Compliance Mapping** — SOC 2 Type II, PCI-DSS 4.0, NIST SP 800-57 alignment guides @@ -512,14 +512,17 @@ Core lifecycle management — Local CA + ACME v2 issuers, NGINX target connector - **Scheduled Certificate Digest** — HTML email digests with certificate stats, expiration timeline, job trends, and agent health; configurable daily/hourly/weekly briefings via SMTP - **Helm Chart** — Production-ready Kubernetes with server Deployment, PostgreSQL StatefulSet with PVC, Agent DaemonSet, security contexts, resource limits, optional Ingress -**Coming in v2.1.0:** -- Dynamic issuer and target configuration via GUI (no env var restarts) +**Also shipped:** - Issuer catalog page (see all supported CAs, configure from dashboard) -- First-run onboarding wizard +- Vault PKI and DigiCert CertCentral issuer connectors (Beta) - Turnkey deployment examples (ACME+NGINX, wildcard+DNS-01, private CA+Traefik, step-ca+HAProxy, multi-issuer) - Migration guides (Certbot, acme.sh, cert-manager complement) - One-line agent install script with cross-compiled binaries +**Coming in v2.1.0:** +- Dynamic issuer and target configuration via GUI (no env var restarts) +- First-run onboarding wizard + ### V3: certctl Pro Team access controls, identity provider integration, enterprise deployment targets, compliance and risk scoring, advanced fleet operations, event-driven architecture, advanced search, real-time operational views. diff --git a/cli b/cli deleted file mode 100755 index 39eed02..0000000 Binary files a/cli and /dev/null differ diff --git a/docs/architecture.md b/docs/architecture.md index 235a410..d7ecaa1 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -80,13 +80,16 @@ flowchart TB CA2["ACME\n(HTTP-01 + DNS-01 + DNS-PERSIST-01)\n(EAB, ZeroSSL auto-EAB)"] CA3["step-ca\n(/sign API)"] CA4["OpenSSL / Custom CA\n(script-based)"] - CA6["Vault PKI\n(planned)"] + CA6["Vault PKI\n(token auth, /sign API)"] + CA7["DigiCert CertCentral\n(async order model)"] end subgraph "Target Systems" T1["NGINX\n(file write + reload)"] T4["Apache httpd\n(file write + reload)"] T5["HAProxy\n(combined PEM + reload)"] + T6["Traefik\n(file provider)"] + T7["Caddy\n(admin API / file)"] T2["F5 BIG-IP\n(proxy agent + iControl REST, planned)"] T3["IIS\n(agent-local PowerShell, planned)"] end @@ -96,7 +99,7 @@ flowchart TB SVC --> REPO REPO --> PG SCHED --> SVC - SVC -->|"Issue/Renew"| CA1 & CA2 & CA3 + SVC -->|"Issue/Renew"| CA1 & CA2 & CA3 & CA4 & CA6 & CA7 A1 & A2 & A3 -->|"CSR + Heartbeat"| API API -->|"Cert + Chain\n(NO private key)"| A1 & A2 & A3 @@ -506,7 +509,8 @@ flowchart TB II --> ACME["ACME v2"] II --> SC["step-ca"] II --> OC["OpenSSL / Custom CA"] - II --> VP["Vault PKI (planned)"] + II --> VP["Vault PKI"] + II --> DC["DigiCert CertCentral"] end subgraph "Target Connectors" diff --git a/docs/features.md b/docs/features.md index 25a09a4..474f443 100644 --- a/docs/features.md +++ b/docs/features.md @@ -1469,8 +1469,8 @@ Each guide includes an evidence summary table mapping specific criteria to certc | **Bulk revocation** | ✗ | ✓ | Planned V3 (paid) | | **Certificate health scores** | ✗ | ✓ | Planned V3 | | **Compliance scoring** | ✗ | ✓ | Planned V3 | -| **DigiCert issuer** | ✗ | ✓ | Planned V2.1 (free) | -| **Vault PKI issuer** | ✗ | ✓ | Planned V2.1 (free) | +| **DigiCert issuer** | ✗ | ✓ | Implemented (Beta) | +| **Vault PKI issuer** | ✗ | ✓ | Implemented (Beta) | --- diff --git a/docs/testing-guide.md b/docs/testing-guide.md index 54829c2..8c92a85 100644 --- a/docs/testing-guide.md +++ b/docs/testing-guide.md @@ -45,6 +45,7 @@ Comprehensive manual testing playbook. Every test has a concrete command, an exp - [Part 38: Vault PKI Connector (M32)](#part-38-vault-pki-connector-m32) - [Part 39: DigiCert Connector (M37)](#part-39-digicert-connector-m37) - [Part 40: Issuer Catalog Page (M33)](#part-40-issuer-catalog-page-m33) +- [Part 41: Frontend Audit Fixes](#part-41-frontend-audit-fixes) - [Release Sign-Off](#release-sign-off) --- @@ -5455,6 +5456,107 @@ cd web && npx vitest run 2>&1 | grep -qE 'Tests.*passed' --- +## Part 41: Frontend Audit Fixes + +Comprehensive frontend coverage audit closed 60 gaps between backend capabilities and GUI surfaces. This part validates the critical fixes. + +### Automated Tests (qa-smoke-test.sh Part 41) + +| # | Test | Assertion | +|---|------|-----------| +| 41.1 | Certificate TS type has lifecycle fields | `types.ts` contains `last_renewal_at`, `last_deployment_at`, `target_ids` | +| 41.2 | API client has new endpoint functions | `client.ts` exports `updateIssuer`, `updateTarget`, `getCertificateDeployments`, `getCRL`, `getOCSPStatus`, `getPolicy` | +| 41.3 | CertificatesPage has filter dropdowns | Contains `issuerFilter`, `ownerFilter`, `profileFilter` state vars | +| 41.4 | CertificatesPage shows last_renewal_at | Column renders `last_renewal_at` field | +| 41.5 | JobsPage shows error_message | Error column displays first 80 chars for failed jobs | +| 41.6 | ProfilesPage has key algorithm fields | Create form includes `allowed_key_algorithms` with add/remove rows | +| 41.7 | ProfilesPage has EKU checkboxes | Create form includes `allowed_ekus` checkbox group | +| 41.8 | DiscoveryPage shows is_ca badge | CA badge renders for discovered CA certificates | +| 41.9 | TargetDetailPage has Edit functionality | Edit button wired to `updateTarget` API call | +| 41.10 | CertificatesPage has tags field | Create form includes tags input (key=value pairs) | +| 41.11 | AgentFleetPage maps darwin to macOS | OS display mapping applied to pie chart and platform headers | +| 41.12 | Frontend builds after audit fixes | `npm run build` succeeds | + +### Manual Tests + +**41.M1: Profile Create Form — Key Algorithm Configuration** + +1. Navigate to Profiles page, click "+ New Profile" +2. Verify default algorithms shown: ECDSA 256+, RSA 2048+ +3. Click "Remove" on RSA row — verify it disappears +4. Click "+ Add" — verify Ed25519 appears (with "fixed" instead of size dropdown) +5. Submit form, verify profile created with correct `allowed_key_algorithms` array + +**PASS if** algorithms are configurable and persisted correctly. + +**41.M2: Profile Create Form — EKU Selection** + +1. In Create Profile modal, verify EKU checkboxes visible (serverAuth checked by default) +2. Check "Email Protection (S/MIME)" and "Client Authentication" +3. Submit, verify profile has `allowed_ekus: ["serverAuth", "emailProtection", "clientAuth"]` + +**PASS if** EKUs are selectable and sent to backend. + +**41.M3: Certificate Create Form — Tags** + +1. Navigate to Certificates page, click "+ New Certificate" +2. Enter tags: `env=prod, team=platform, app=api` +3. Submit, verify certificate created with `tags: {"env": "prod", "team": "platform", "app": "api"}` + +**PASS if** tags are parsed and persisted as key-value pairs. + +**41.M4: Jobs Table — Error Message Column** + +1. Navigate to Jobs page, filter to "Failed" status +2. Verify "Error" column shows truncated error message (max 80 chars with "...") +3. Hover over truncated message, verify full text in tooltip + +**PASS if** error messages visible for failed jobs. + +**41.M5: Certificates Table — Lifecycle Columns** + +1. Navigate to Certificates page +2. Verify "Last Renewal" and "Last Deploy" columns visible +3. Verify dates shown for certs with data, "—" for certs without + +**PASS if** lifecycle timestamps displayed. + +**41.M6: Certificate Filters — Issuer/Owner/Profile Dropdowns** + +1. Navigate to Certificates page +2. Verify Issuer, Owner, Profile dropdown filters visible +3. Select an issuer — verify table filters to matching certificates +4. Clear filter, select a profile — verify filtering works + +**PASS if** all three filter dropdowns functional. + +**41.M7: Target Detail — Edit Button** + +1. Navigate to a target detail page +2. Click "Edit" button +3. Modify name, click "Save" +4. Verify name updated on the page + +**PASS if** target edit persists via API. + +**41.M8: Discovery Table — CA Badge** + +1. Navigate to Discovery page +2. Verify "Key" column shows algorithm + key size +3. For CA certificates, verify purple "CA" badge displayed + +**PASS if** CA certificates visually distinguished. + +**41.M9: Fleet Overview — macOS Display** + +1. Navigate to Fleet Overview page +2. Verify OS pie chart shows "macOS" instead of "darwin" +3. Verify platform section headers show "macOS / amd64" (not "darwin / amd64") + +**PASS if** darwin correctly mapped to macOS in all locations. + +--- + ## Release Sign-Off All tests below must pass before tagging v2.1.0. Each row is one individual test from the guide above. The **Method** column indicates whether `qa-smoke-test.sh` covers the test automatically (**Auto**) or requires hands-on verification (**Manual**). @@ -6054,14 +6156,41 @@ These must be green before starting manual QA: | 40.m5 | Config detail modal shows full redacted config | Manual | ☐ | | | | 40.m6 | Issuer type filter works | Manual | ☐ | | | +### Part 41: Frontend Audit Fixes + +| Test | Description | Method | Pass? | Date | Notes | +|------|-------------|--------|-------|------|-------| +| 41.s1 | Certificate TS type has lifecycle fields | Auto | ☐ | | qa-smoke-test.sh 41.1 | +| 41.s2 | API client has new endpoint functions | Auto | ☐ | | qa-smoke-test.sh 41.2 | +| 41.s3 | CertificatesPage has filter dropdowns | Auto | ☐ | | qa-smoke-test.sh 41.3 | +| 41.s4 | CertificatesPage shows last_renewal_at | Auto | ☐ | | qa-smoke-test.sh 41.4 | +| 41.s5 | JobsPage shows error_message | Auto | ☐ | | qa-smoke-test.sh 41.5 | +| 41.s6 | ProfilesPage has key algorithm fields | Auto | ☐ | | qa-smoke-test.sh 41.6 | +| 41.s7 | ProfilesPage has EKU checkboxes | Auto | ☐ | | qa-smoke-test.sh 41.7 | +| 41.s8 | DiscoveryPage shows is_ca badge | Auto | ☐ | | qa-smoke-test.sh 41.8 | +| 41.s9 | TargetDetailPage has Edit functionality | Auto | ☐ | | qa-smoke-test.sh 41.9 | +| 41.s10 | CertificatesPage has tags field | Auto | ☐ | | qa-smoke-test.sh 41.10 | +| 41.s11 | AgentFleetPage maps darwin to macOS | Auto | ☐ | | qa-smoke-test.sh 41.11 | +| 41.s12 | Frontend builds after audit fixes | Auto | ☐ | | qa-smoke-test.sh 41.12 | +| 41.m1 | Profile create form — key algorithm config | Manual | ☐ | | | +| 41.m2 | Profile create form — EKU selection | Manual | ☐ | | | +| 41.m3 | Certificate create form — tags | Manual | ☐ | | | +| 41.m4 | Jobs table — error message column | Manual | ☐ | | | +| 41.m5 | Certificates table — lifecycle columns | Manual | ☐ | | | +| 41.m6 | Certificate filters — issuer/owner/profile | Manual | ☐ | | | +| 41.m7 | Target detail — edit button | Manual | ☐ | | | +| 41.m8 | Discovery table — CA badge | Manual | ☐ | | | +| 41.m9 | Fleet overview — macOS display | Manual | ☐ | | | + ### Summary | Category | Count | |----------|-------| | ☑ Auto (passed in `qa-smoke-test.sh`) | 144 | +| ☐ Auto (not yet run) | 12 | | — Skipped (preconditions not met in demo) | 5 | -| ☐ Manual (requires hands-on verification) | 232 | -| **Total** | **381** | +| ☐ Manual (requires hands-on verification) | 241 | +| **Total** | **402** | **Automated tests must also be green.** CI passing is necessary but not sufficient — this manual QA catches integration issues that isolated unit tests miss. diff --git a/web/src/api/client.test.ts b/web/src/api/client.test.ts index 3273a4a..e0b05e9 100644 --- a/web/src/api/client.test.ts +++ b/web/src/api/client.test.ts @@ -83,6 +83,12 @@ import { getIssuer, getTarget, getPrometheusMetrics, + getCertificateDeployments, + getCRL, + getOCSPStatus, + updateIssuer, + updateTarget, + getPolicy, } from './client'; // Mock global fetch @@ -1150,4 +1156,53 @@ describe('API Client', () => { expect(init.headers['Authorization']).toBe('Bearer prom-key'); }); }); + + describe('Frontend Audit: New API Functions', () => { + it('getCertificateDeployments sends GET with cert ID', async () => { + mockFetch.mockReturnValueOnce(mockJsonResponse({ data: [], total: 0 })); + await getCertificateDeployments('mc-1'); + expect(mockFetch.mock.calls[0][0]).toContain('/api/v1/certificates/mc-1/deployments'); + }); + + it('getCRL sends GET to /crl', async () => { + mockFetch.mockReturnValueOnce(mockJsonResponse({ entries: [], total: 0 })); + await getCRL(); + expect(mockFetch.mock.calls[0][0]).toBe('/api/v1/crl'); + }); + + it('getOCSPStatus sends GET with issuer and serial', async () => { + const buf = new ArrayBuffer(8); + mockFetch.mockReturnValueOnce( + Promise.resolve({ + ok: true, + status: 200, + arrayBuffer: () => Promise.resolve(buf), + } as Response) + ); + await getOCSPStatus('iss-local', 'ABC123'); + expect(mockFetch.mock.calls[0][0]).toBe('/api/v1/ocsp/iss-local/ABC123'); + }); + + it('updateIssuer sends PUT with data', async () => { + mockFetch.mockReturnValueOnce(mockJsonResponse({ id: 'iss-1', name: 'Updated' })); + await updateIssuer('iss-1', { name: 'Updated' }); + const [url, init] = mockFetch.mock.calls[0]; + expect(url).toBe('/api/v1/issuers/iss-1'); + expect(init.method).toBe('PUT'); + }); + + it('updateTarget sends PUT with data', async () => { + mockFetch.mockReturnValueOnce(mockJsonResponse({ id: 't-1', name: 'Updated' })); + await updateTarget('t-1', { name: 'Updated' }); + const [url, init] = mockFetch.mock.calls[0]; + expect(url).toBe('/api/v1/targets/t-1'); + expect(init.method).toBe('PUT'); + }); + + it('getPolicy sends GET with policy ID', async () => { + mockFetch.mockReturnValueOnce(mockJsonResponse({ id: 'pol-1', name: 'Test' })); + await getPolicy('pol-1'); + expect(mockFetch.mock.calls[0][0]).toBe('/api/v1/policies/pol-1'); + }); + }); }); diff --git a/web/src/api/client.ts b/web/src/api/client.ts index a7f9e6c..80dab73 100644 --- a/web/src/api/client.ts +++ b/web/src/api/client.ts @@ -122,6 +122,26 @@ export const exportCertificatePKCS12 = (id: string, password: string = '') => { }); }; +// Certificate Deployments +export const getCertificateDeployments = (id: string, params: Record = {}) => { + const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); + return fetchJSON>(`${BASE}/certificates/${id}/deployments?${qs}`); +}; + +// CRL / OCSP +export const getCRL = () => + fetchJSON<{ version: number; entries: unknown[]; total: number; generated_at: string }>(`${BASE}/crl`); + +export const getOCSPStatus = (issuerId: string, serial: string) => { + const headers: Record = {}; + if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`; + return fetch(`${BASE}/ocsp/${issuerId}/${serial}`, { headers }) + .then(r => { + if (!r.ok) throw new Error(`OCSP request failed: ${r.status}`); + return r.arrayBuffer(); + }); +}; + // Agents export const getAgents = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); @@ -170,6 +190,9 @@ export const createPolicy = (data: Partial) => export const updatePolicy = (id: string, data: Partial) => fetchJSON(`${BASE}/policies/${id}`, { method: 'PUT', body: JSON.stringify(data) }); +export const getPolicy = (id: string) => + fetchJSON(`${BASE}/policies/${id}`); + export const deletePolicy = (id: string) => fetchJSON<{ message: string }>(`${BASE}/policies/${id}`, { method: 'DELETE' }); @@ -188,6 +211,9 @@ export const createIssuer = (data: Partial) => export const testIssuerConnection = (id: string) => fetchJSON<{ message: string }>(`${BASE}/issuers/${id}/test`, { method: 'POST' }); +export const updateIssuer = (id: string, data: Partial) => + fetchJSON(`${BASE}/issuers/${id}`, { method: 'PUT', body: JSON.stringify(data) }); + export const deleteIssuer = (id: string) => fetchJSON<{ message: string }>(`${BASE}/issuers/${id}`, { method: 'DELETE' }); @@ -200,6 +226,9 @@ export const getTargets = (params: Record = {}) => { export const createTarget = (data: Partial) => fetchJSON(`${BASE}/targets`, { method: 'POST', body: JSON.stringify(data) }); +export const updateTarget = (id: string, data: Partial) => + fetchJSON(`${BASE}/targets/${id}`, { method: 'PUT', body: JSON.stringify(data) }); + export const deleteTarget = (id: string) => fetchJSON<{ message: string }>(`${BASE}/targets/${id}`, { method: 'DELETE' }); diff --git a/web/src/api/types.ts b/web/src/api/types.ts index ba43e71..41b2115 100644 --- a/web/src/api/types.ts +++ b/web/src/api/types.ts @@ -18,7 +18,10 @@ export interface Certificate { expires_at: string; revoked_at?: string; revocation_reason?: string; + target_ids?: string[]; tags: Record; + last_renewal_at?: string; + last_deployment_at?: string; created_at: string; updated_at: string; } @@ -45,6 +48,8 @@ export interface CertificateVersion { csr_pem: string; not_before: string; not_after: string; + key_algorithm?: string; + key_size?: number; created_at: string; } @@ -138,6 +143,7 @@ export interface Issuer { /** Backend returns enabled boolean; status is derived from this */ enabled: boolean; created_at: string; + updated_at?: string; } export interface Target { @@ -149,6 +155,7 @@ export interface Target { config: Record; status: string; created_at: string; + updated_at?: string; } export interface KeyAlgorithmRule { diff --git a/web/src/components/Layout.tsx b/web/src/components/Layout.tsx index a2c7ea2..be25f8e 100644 --- a/web/src/components/Layout.tsx +++ b/web/src/components/Layout.tsx @@ -71,7 +71,7 @@ export default function Layout() {
- v2.0.14 + v2.0.20 {authRequired && (