feat(09-03): InspectTab UI + inspectCertAction + render tests
- Added inspectCertAction to actions.ts (JSON path for pemText, multipart path for file)
- Added CertDetails interface to actions.ts (mirrors API response shape)
- Implemented InspectTab: Analysieren button, loading state, grid-cols-2 result grid
- InspectTab handles wrong-password error (t('error.wrongPassword')) and generic error
- Added 2 new InspectTab tests: success grid (subject CN + SHA-256) and error (text-destructive)
- Fixed setup.ts: explicit expect.extend(matchers) for vitest@4.x compatibility
(Rule 1: @testing-library/jest-dom/vitest not extending global expect in vitest 4)
- Fixed existing test: getByText -> getAllByText for 'Analysieren' (now appears in tab nav + button)
- 9/9 cert-manager tests pass
This commit is contained in:
@@ -1,6 +1,64 @@
|
||||
export const API_URL =
|
||||
process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CertDetails — mirrors the API response shape from CertManagerService
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface CertDetails {
|
||||
subject: { cn: string; o: string; ou: string; c: string };
|
||||
issuer: { cn: string; o: string; c: string };
|
||||
validity: { notBefore: string; notAfter: string; isExpired: boolean; daysLeft: number };
|
||||
san: string[];
|
||||
keyType: string;
|
||||
keyBits: number;
|
||||
serialNumber: string;
|
||||
signatureAlgorithm: string;
|
||||
fingerprint: { sha1: string; sha256: string };
|
||||
pemPreview: string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// inspectCertAction — calls POST /modules/cert-manager/parse
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Call POST /modules/cert-manager/parse.
|
||||
* - If pemText is present → JSON body { pemText, password }
|
||||
* - Otherwise → multipart FormData with file + optional password
|
||||
*
|
||||
* T-09-02: password is never placed in URL, logged, or echoed.
|
||||
* T-09-04: credentials:'include' ensures JWT cookie is sent.
|
||||
*/
|
||||
export async function inspectCertAction(input: {
|
||||
file?: File | null;
|
||||
pemText?: string;
|
||||
password?: string;
|
||||
}): Promise<CertDetails> {
|
||||
const { file, pemText, password } = input;
|
||||
|
||||
if (pemText) {
|
||||
// JSON path — content-type must be application/json (not multipart)
|
||||
const response = await fetch(`${API_URL}/modules/cert-manager/parse`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ pemText, password }),
|
||||
credentials: 'include',
|
||||
});
|
||||
if (!response.ok) {
|
||||
const body = await response.text().catch(() => '');
|
||||
throw new Error(`${response.status} ${body}`.trim());
|
||||
}
|
||||
return response.json() as Promise<CertDetails>;
|
||||
} else {
|
||||
// Multipart path — let browser set Content-Type with boundary
|
||||
const form = new FormData();
|
||||
if (file) form.append('file', file);
|
||||
if (password) form.append('password', password);
|
||||
return postForm('parse', form) as Promise<CertDetails>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a base64-encoded file as a browser download.
|
||||
* T-09-02: password is never placed in URL, console.log, or filename.
|
||||
|
||||
Reference in New Issue
Block a user