112 lines
4.1 KiB
Markdown
112 lines
4.1 KiB
Markdown
---
|
|
title: File API
|
|
tags: [api, file, reference]
|
|
---
|
|
|
|
# File API
|
|
|
|
Endpoints live under `/api/files/*`. The router is [`backend/src/services/file/fileRoutes.ts`](../../backend/src/services/file/fileRoutes.ts), delegating to `fileController` and `fileService`. Multer is used for multipart parsing and uploaded files are written under `uploads/<subfolder>`.
|
|
|
|
All endpoints require `Bearer JWT`. Static serving is wired in `app.ts` at `/uploads/*` with `helmet({ crossOriginResourcePolicy: { policy: "cross-origin" } })` so files can be embedded from the frontend domain.
|
|
|
|
## Multer configuration
|
|
|
|
`fileService.getUploadMiddleware(options)` produces a Multer instance per route. Options:
|
|
|
|
- `subfolder` - where the file lands under `uploads/`
|
|
- `fieldName` - form field name
|
|
- `fileTypes` - allowed MIME types (default: any)
|
|
- `maxFiles` - default 1
|
|
- `maxFileSizeMB` - default 10 MB
|
|
|
|
Global body limits in `app.ts` are `10mb` for `express.json` and `express.urlencoded`.
|
|
|
|
## Upload
|
|
|
|
### POST /api/files/upload/avatar
|
|
|
|
**Description:** Upload an avatar image. Lands in `uploads/temp/` and is then moved when the user persists it on their profile.
|
|
**Auth required:** Bearer JWT
|
|
**Form fields:** `avatar` (image; JPEG / PNG / GIF / WebP)
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"url": "/uploads/temp/avatar-1716459200000.jpg",
|
|
"filename": "avatar-1716459200000.jpg",
|
|
"mimeType": "image/jpeg",
|
|
"size": 51234
|
|
}
|
|
}
|
|
```
|
|
**Errors:** `400` bad type / too large, `401` not authenticated.
|
|
**Side effects:** None — caller is responsible for `PUT /api/user/profile` to persist the URL.
|
|
|
|
### POST /api/files/upload/file
|
|
|
|
**Description:** Generic single-file upload (any MIME), lands in `uploads/temp/`.
|
|
**Auth required:** Bearer JWT
|
|
**Form fields:** `file`
|
|
**Response 200:** `{ success, data: { url, filename, mimeType, size } }`
|
|
|
|
### POST /api/files/upload/files
|
|
|
|
**Description:** Multi-file upload (up to 5).
|
|
**Auth required:** Bearer JWT
|
|
**Form fields:** `files` (repeated)
|
|
**Response 200:** `{ success, data: { files: [{ url, filename, ... }] } }`
|
|
|
|
### POST /api/files/upload/request-template-images
|
|
|
|
**Description:** Up to 10 images for a [[RequestTemplate]]. Lands in `uploads/request-templates/`.
|
|
**Auth required:** Bearer JWT
|
|
**Form fields:** `images` (repeated; JPEG / PNG / GIF / WebP)
|
|
**Response 200:** `{ success, data: { files: [...] } }`
|
|
|
|
### POST /api/files/upload/blog-images
|
|
|
|
**Description:** Up to 10 images for a [[BlogPost]]. Lands in `uploads/blog/`.
|
|
**Auth required:** Bearer JWT
|
|
**Form fields:** `images` (repeated; JPEG / PNG / GIF / WebP)
|
|
**Response 200:** `{ success, data: { files: [...] } }`
|
|
|
|
## Delete
|
|
|
|
### DELETE /api/files/delete
|
|
|
|
**Description:** Delete a file by relative path.
|
|
**Auth required:** Bearer JWT
|
|
**Request body:** `{ filePath: string }` (e.g. `"temp/avatar-1716459200000.jpg"`)
|
|
**Response 200:** `{ success, message: "File deleted" }`
|
|
**Errors:** `400` invalid path (must stay inside `uploads/`), `404` file missing.
|
|
|
|
## Inspect
|
|
|
|
### GET /api/files/info/:filePath
|
|
|
|
**Description:** Returns metadata for a file (URL-encoded `filePath` segment).
|
|
**Auth required:** Bearer JWT
|
|
**Response 200:** `{ success, data: { url, size, mimeType, createdAt } }`
|
|
|
|
### GET /api/files/stats
|
|
|
|
**Description:** Aggregate upload statistics (counts and sizes per subfolder).
|
|
**Auth required:** Bearer JWT (admin gating planned per TODO in source)
|
|
**Response 200:** `{ success, data: { subfolders: [{ name, count, sizeBytes }], total: { count, sizeBytes } } }`
|
|
|
|
## Serving
|
|
|
|
- `GET /uploads/<path>` - static file delivery (no auth, public read). Suitable for embedding avatars and blog/template images.
|
|
- The server logs `❌ File not found:` for missing paths and `✅ Serving file:` on hits — useful when debugging frontend image refs.
|
|
|
|
The on-disk root resolves from `config.uploadPath`. In production this defaults to `/app/uploads`; in development to `<repo>/uploads`. Both are normalised to an absolute path before being passed to `express.static`.
|
|
|
|
## Related
|
|
|
|
- [[File Storage Architecture]]
|
|
- [[User API]] (avatar consumer)
|
|
- [[Marketplace API]] (template image consumer)
|
|
- [[Blog API]] (blog image consumer)
|
|
- [[Chat API]] (message attachments use a separate multipart route under chat)
|