mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(api): add multipart file upload, workspace management, and download routes
- Register @fastify/multipart plugin with size limits from env config - Workspace manager: createWorkspace, getWorkspacePath, cleanupWorkspace - File validation: magic byte detection, format check, megapixel limit - POST /api/v1/upload: multipart upload with validation, returns jobId + file metadata - GET /api/v1/download/:jobId/:filename: serve files with Content-Disposition - Path traversal guards on all file-serving endpoints - Add @stirling-image/image-engine and sharp as API dependencies - Add apiUpload, getDownloadUrl, apiDownloadBlob to web client
This commit is contained in:
@@ -32,3 +32,37 @@ export function setToken(token: string) {
|
||||
export function clearToken() {
|
||||
localStorage.removeItem("stirling-token");
|
||||
}
|
||||
|
||||
// ── File Upload / Download ──────────────────────────────────────
|
||||
|
||||
export async function apiUpload(
|
||||
files: File[],
|
||||
): Promise<{
|
||||
jobId: string;
|
||||
files: Array<{ name: string; size: number; format: string }>;
|
||||
}> {
|
||||
const formData = new FormData();
|
||||
files.forEach((f) => formData.append("files", f));
|
||||
const res = await fetch("/api/v1/upload", {
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${getToken()}` },
|
||||
body: formData,
|
||||
});
|
||||
if (!res.ok) throw new Error(`Upload failed: ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export function getDownloadUrl(jobId: string, filename: string): string {
|
||||
return `/api/v1/download/${jobId}/${filename}`;
|
||||
}
|
||||
|
||||
export async function apiDownloadBlob(
|
||||
jobId: string,
|
||||
filename: string,
|
||||
): Promise<Blob> {
|
||||
const res = await fetch(getDownloadUrl(jobId, filename), {
|
||||
headers: { Authorization: `Bearer ${getToken()}` },
|
||||
});
|
||||
if (!res.ok) throw new Error(`Download failed: ${res.status}`);
|
||||
return res.blob();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user