Implement file storage layer with local provider, upload/download API, tests
- StorageProvider interface with LocalProvider (S3 placeholder) - File table with entity_type/entity_id references, content type, path - POST /v1/files (multipart upload), GET /v1/files (list by entity), GET /v1/files/:id (metadata), GET /v1/files/serve/* (content), DELETE /v1/files/:id - member_identifier drops base64 columns, uses file_id FKs - File validation: type whitelist, size limits, per-entity max - Fastify storage plugin injects provider into app - 6 API tests for upload, list, get, delete, validation - Test runner kills stale port before starting backend
This commit is contained in:
@@ -6,12 +6,14 @@ import { corsPlugin } from './plugins/cors.js'
|
||||
import { errorHandlerPlugin } from './plugins/error-handler.js'
|
||||
import { authPlugin } from './plugins/auth.js'
|
||||
import { devAuthPlugin } from './plugins/dev-auth.js'
|
||||
import { storagePlugin } from './plugins/storage.js'
|
||||
import { healthRoutes } from './routes/v1/health.js'
|
||||
import { authRoutes } from './routes/v1/auth.js'
|
||||
import { accountRoutes } from './routes/v1/accounts.js'
|
||||
import { inventoryRoutes } from './routes/v1/inventory.js'
|
||||
import { productRoutes } from './routes/v1/products.js'
|
||||
import { lookupRoutes } from './routes/v1/lookups.js'
|
||||
import { fileRoutes } from './routes/v1/files.js'
|
||||
|
||||
export async function buildApp() {
|
||||
const app = Fastify({
|
||||
@@ -28,6 +30,7 @@ export async function buildApp() {
|
||||
await app.register(databasePlugin)
|
||||
await app.register(redisPlugin)
|
||||
await app.register(rateLimit, { global: false })
|
||||
await app.register(storagePlugin)
|
||||
|
||||
// Auth — JWT in production/test, dev bypass only in development without JWT_SECRET
|
||||
if (process.env.JWT_SECRET) {
|
||||
@@ -46,6 +49,7 @@ export async function buildApp() {
|
||||
await app.register(inventoryRoutes, { prefix: '/v1' })
|
||||
await app.register(productRoutes, { prefix: '/v1' })
|
||||
await app.register(lookupRoutes, { prefix: '/v1' })
|
||||
await app.register(fileRoutes, { prefix: '/v1' })
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user