feat(09-02): build cert-manager page shell, components, and client helpers
- CertManagerPage: 'use client', useTranslations('certManager'), max-w-4xl layout
- Shared input card with DropZone, OR divider, PEM textarea, conditional PasswordField
- PasswordField renders null when show=false (T-09-02 threat mitigation)
- Tab nav: Analysieren / Aufteilen / Zusammenfuehren / Konvertieren
- Tab stubs: InspectTab, SplitTab, MergeTab, ConvertTab (empty state only)
- actions.ts: API_URL const, downloadBase64(atob->Blob->URL), postForm(credentials:'include')
- File/paste mutual exclusion: selecting one clears the other
- No shadcn/Radix; Tailwind utilities only; inline SVG eye icon
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
export const API_URL =
|
||||
process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
Reference in New Issue
Block a user