64a8e725e7
- 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
110 lines
3.5 KiB
TypeScript
110 lines
3.5 KiB
TypeScript
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.
|
|
*/
|
|
export function downloadBase64(
|
|
filename: string,
|
|
content: string,
|
|
mimeType: string,
|
|
): void {
|
|
const bytes = atob(content);
|
|
const byteArray = new Uint8Array(bytes.length);
|
|
for (let i = 0; i < bytes.length; i++) {
|
|
byteArray[i] = bytes.charCodeAt(i);
|
|
}
|
|
const blob = new Blob([byteArray], { type: mimeType });
|
|
const url = URL.createObjectURL(blob);
|
|
const anchor = document.createElement('a');
|
|
anchor.href = url;
|
|
anchor.download = filename;
|
|
anchor.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
/**
|
|
* POST a FormData payload to a cert-manager endpoint.
|
|
* T-09-04: credentials:'include' ensures JWT cookie is sent for ModuleGuard.
|
|
* No manual Content-Type header — browser sets multipart boundary automatically.
|
|
*/
|
|
export async function postForm(
|
|
endpoint: string,
|
|
form: FormData,
|
|
): Promise<unknown> {
|
|
const response = await fetch(
|
|
`${API_URL}/modules/cert-manager/${endpoint}`,
|
|
{
|
|
method: 'POST',
|
|
body: form,
|
|
credentials: 'include',
|
|
},
|
|
);
|
|
|
|
if (!response.ok) {
|
|
const body = await response.text().catch(() => '');
|
|
throw new Error(`${response.status} ${body}`.trim());
|
|
}
|
|
|
|
return response.json();
|
|
}
|