feat: add import service and API layer (refs #26)
Implements Milestone 3: Backend import service and API with: Service Layer (user-import.service.ts): - generatePreview(): extract archive, validate, detect VIN conflicts - executeMerge(): chunk-based import (100 records/batch), UPDATE existing by VIN, INSERT new via batchInsert - executeReplace(): transactional DELETE all user data, batchInsert all records - Conflict detection: VIN duplicates in vehicles - Error handling: collect errors per record, continue, report in summary - File handling: copy vehicle images and documents from archive to storage - Cleanup: delete temp directory in finally block API Layer: - POST /api/user/import: multipart upload, mode selection (merge/replace) - POST /api/user/import/preview: preview without executing import - Authentication: fastify.authenticate preHandler - Content-Type validation: application/gzip or application/x-gzip - Magic byte validation: FileType.fromBuffer verifies tar.gz - Request validation: Zod schema for mode selection - Response: ImportResult with success, mode, summary, warnings Files Created: - backend/src/features/user-import/domain/user-import.service.ts - backend/src/features/user-import/api/user-import.controller.ts - backend/src/features/user-import/api/user-import.routes.ts - backend/src/features/user-import/api/user-import.validation.ts Files Updated: - backend/src/app.ts: register userImportRoutes with /api prefix Quality: - Type-check: PASS (0 errors) - Linting: PASS (0 errors, 470 warnings - all pre-existing) - Repository pattern: snake_case→camelCase conversion - User-scoped: all queries filter by user_id - Transaction boundaries: Replace mode atomic, Merge mode per-batch Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,7 @@ import { userProfileRoutes } from './features/user-profile';
|
||||
import { onboardingRoutes } from './features/onboarding';
|
||||
import { userPreferencesRoutes } from './features/user-preferences';
|
||||
import { userExportRoutes } from './features/user-export';
|
||||
import { userImportRoutes } from './features/user-import/api/user-import.routes';
|
||||
import { pool } from './core/config/database';
|
||||
import { configRoutes } from './core/config/config.routes';
|
||||
|
||||
@@ -92,7 +93,7 @@ async function buildApp(): Promise<FastifyInstance> {
|
||||
status: 'healthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
environment: process.env['NODE_ENV'],
|
||||
features: ['admin', 'auth', 'config', 'onboarding', 'vehicles', 'documents', 'fuel-logs', 'stations', 'maintenance', 'platform', 'notifications', 'user-profile', 'user-preferences', 'user-export']
|
||||
features: ['admin', 'auth', 'config', 'onboarding', 'vehicles', 'documents', 'fuel-logs', 'stations', 'maintenance', 'platform', 'notifications', 'user-profile', 'user-preferences', 'user-export', 'user-import']
|
||||
});
|
||||
});
|
||||
|
||||
@@ -102,7 +103,7 @@ async function buildApp(): Promise<FastifyInstance> {
|
||||
status: 'healthy',
|
||||
scope: 'api',
|
||||
timestamp: new Date().toISOString(),
|
||||
features: ['admin', 'auth', 'config', 'onboarding', 'vehicles', 'documents', 'fuel-logs', 'stations', 'maintenance', 'platform', 'notifications', 'user-profile', 'user-preferences', 'user-export']
|
||||
features: ['admin', 'auth', 'config', 'onboarding', 'vehicles', 'documents', 'fuel-logs', 'stations', 'maintenance', 'platform', 'notifications', 'user-profile', 'user-preferences', 'user-export', 'user-import']
|
||||
});
|
||||
});
|
||||
|
||||
@@ -143,6 +144,7 @@ async function buildApp(): Promise<FastifyInstance> {
|
||||
await app.register(userProfileRoutes, { prefix: '/api' });
|
||||
await app.register(userPreferencesRoutes, { prefix: '/api' });
|
||||
await app.register(userExportRoutes, { prefix: '/api' });
|
||||
await app.register(userImportRoutes, { prefix: '/api' });
|
||||
await app.register(configRoutes, { prefix: '/api' });
|
||||
|
||||
// 404 handler
|
||||
|
||||
Reference in New Issue
Block a user