119 lines
3.2 KiB
Markdown
119 lines
3.2 KiB
Markdown
# Phase 3: Filesystem Storage Migration
|
|
|
|
## Agent Assignment
|
|
**Primary Agent:** storage-agent
|
|
**Duration:** 30-40 minutes
|
|
|
|
## Prerequisites
|
|
- None (can start immediately)
|
|
|
|
## Objectives
|
|
1. Create filesystem storage adapter
|
|
2. Replace MinIO adapter with filesystem
|
|
3. Update documents feature to use filesystem
|
|
4. Verify document upload/download works
|
|
|
|
## Step-by-Step Instructions
|
|
|
|
### Step 1: Create Filesystem Adapter
|
|
Create `backend/src/core/storage/adapters/filesystem.adapter.ts`:
|
|
|
|
```typescript
|
|
import { StorageService } from '../storage.service';
|
|
import * as fs from 'fs/promises';
|
|
import * as path from 'path';
|
|
import { createReadStream } from 'fs';
|
|
|
|
export class FilesystemAdapter implements StorageService {
|
|
private basePath: string;
|
|
|
|
constructor(basePath: string = '/app/data/documents') {
|
|
this.basePath = basePath;
|
|
}
|
|
|
|
async putObject(bucket: string, key: string, body: Buffer, contentType?: string): Promise<void> {
|
|
const filePath = path.join(this.basePath, key);
|
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
await fs.writeFile(filePath, body);
|
|
}
|
|
|
|
async getObjectStream(bucket: string, key: string): Promise<NodeJS.ReadableStream> {
|
|
const filePath = path.join(this.basePath, key);
|
|
return createReadStream(filePath);
|
|
}
|
|
|
|
async deleteObject(bucket: string, key: string): Promise<void> {
|
|
const filePath = path.join(this.basePath, key);
|
|
await fs.unlink(filePath);
|
|
}
|
|
|
|
async headObject(bucket: string, key: string): Promise<any> {
|
|
const filePath = path.join(this.basePath, key);
|
|
const stats = await fs.stat(filePath);
|
|
return { size: stats.size, lastModified: stats.mtime };
|
|
}
|
|
|
|
async getSignedUrl(bucket: string, key: string): Promise<string> {
|
|
// Not needed for filesystem, return direct path
|
|
return `/api/documents/download/${key}`;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Step 2: Update Storage Service Factory
|
|
Modify `backend/src/core/storage/storage.service.ts`:
|
|
|
|
```typescript
|
|
import { FilesystemAdapter } from './adapters/filesystem.adapter';
|
|
|
|
export function getStorageService(): StorageService {
|
|
// Always use filesystem adapter
|
|
return new FilesystemAdapter('/app/data/documents');
|
|
}
|
|
```
|
|
|
|
### Step 3: Verify Documents Feature
|
|
No changes needed to documents.controller.ts - it uses StorageService interface.
|
|
|
|
Storage keys will be filesystem paths:
|
|
`documents/{userId}/{vehicleId}/{documentId}/{filename}`
|
|
|
|
### Step 4: Test Document Upload
|
|
```bash
|
|
# Rebuild backend
|
|
cd backend && npm run build
|
|
|
|
# Restart backend
|
|
docker compose restart mvp-backend
|
|
|
|
# Test upload (requires authentication)
|
|
curl -X POST https://admin.motovaultpro.com/api/documents \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-F "file=@test.pdf" \
|
|
-F "vehicleId=123"
|
|
|
|
# Verify file exists
|
|
docker compose exec mvp-backend ls -la /app/data/documents/
|
|
```
|
|
|
|
## Validation Criteria
|
|
- [ ] FilesystemAdapter created
|
|
- [ ] getStorageService returns FilesystemAdapter
|
|
- [ ] Document upload works
|
|
- [ ] Document download works
|
|
- [ ] Files stored in /app/data/documents/
|
|
- [ ] No MinIO references in code
|
|
|
|
**Validation Command:**
|
|
```bash
|
|
grep -r "minio\|MinIO" backend/src/ | wc -l
|
|
# Expected: 0
|
|
```
|
|
|
|
## Update EXECUTION-STATE.json
|
|
```json
|
|
{
|
|
"phases": {"3": {"status": "completed", "validation_passed": true}}
|
|
}
|
|
```
|