@mvp-factory/holy-upload
Version:
File upload processing system extracted from Holy Habit project with security validation and image optimization
501 lines (392 loc) • 12.9 kB
Markdown
# @mvp-factory/holy-upload
File upload processing system extracted from Holy Habit project with advanced security validation, image optimization, and Express.js integration.
## Features
- 🔐 **Advanced Security** - Comprehensive file validation with magic number verification
- 🖼️ **Image Optimization** - Automatic resizing, compression, and format conversion using Sharp
- 📁 **Multiple Storage** - Memory and disk storage with flexible configuration
- 🛡️ **Malicious Content Detection** - Script injection and virus pattern detection
- 📊 **Storage Management** - Usage tracking, cleanup utilities, and quota management
- 🚀 **Express Integration** - Ready-to-use middleware and route handlers
- 📤 **Multiple Upload Types** - Single, multiple, and field-based uploads
- 🎨 **Image Processing** - Thumbnail generation, WebP conversion, and EXIF removal
- 📦 **TypeScript Support** - Full TypeScript definitions and type safety
## Installation
```bash
npm install @mvp-factory/holy-upload
```
## Quick Start
### 1. Basic Setup
```typescript
import { HolyUpload } from '@mvp-factory/holy-upload';
// Create upload handler with default configuration
const uploadHandler = HolyUpload.createHandler({
uploadDir: './uploads',
maxSize: 10 * 1024 * 1024, // 10MB
allowedMimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
enableOptimization: true
});
// Upload a file
const result = await uploadHandler.uploadSingle(file, 'user123');
console.log(result); // { success: true, file: {...}, url: '/uploads/...' }
```
### 2. Express.js Integration
```typescript
import express from 'express';
import { HolyUpload } from '@mvp-factory/holy-upload';
const app = express();
// Create middleware
const uploadMiddleware = HolyUpload.createMiddleware({
uploadDir: './uploads',
maxSize: 5 * 1024 * 1024, // 5MB
allowedMimeTypes: ['image/jpeg', 'image/png', 'image/webp']
});
// Single file upload endpoint
const [singleUpload, singleHandler] = uploadMiddleware.createCompleteEndpoint('single', 'image');
app.post('/api/upload', ...singleUpload, singleHandler);
// Multiple files upload endpoint
const [multipleUpload, multipleHandler] = uploadMiddleware.createCompleteEndpoint('multiple', 'images');
app.post('/api/upload/multiple', ...multipleUpload, multipleHandler);
// Serve uploaded files
app.use('/uploads', express.static('./uploads'));
app.listen(3000);
```
### 3. Environment Configuration
Create a `.env` file:
```env
# Upload Configuration
UPLOAD_DIR=./uploads
UPLOAD_MAX_SIZE=10485760
UPLOAD_MAX_WIDTH=1920
UPLOAD_MAX_HEIGHT=1080
UPLOAD_ALLOWED_MIME_TYPES=image/jpeg,image/png,image/gif,image/webp
UPLOAD_ALLOWED_EXTENSIONS=jpg,jpeg,png,gif,webp
UPLOAD_ENABLE_OPTIMIZATION=true
UPLOAD_JPEG_QUALITY=85
UPLOAD_PNG_COMPRESSION=8
UPLOAD_WEBP_QUALITY=85
```
## Core Components
### UploadHandler
Main class for handling file uploads:
```typescript
import { UploadHandler } from '@mvp-factory/holy-upload';
const handler = new UploadHandler({
uploadDir: './uploads',
maxSize: 10 * 1024 * 1024,
enableOptimization: true
});
// Single file upload
const result = await handler.uploadSingle(file, 'user123');
// Multiple files upload
const results = await handler.uploadMultiple(files, 'user123', {
continueOnError: true
});
// Delete file
await handler.deleteFile('filename.jpg', 'user123');
// Get storage info
const storage = await handler.getStorageInfo('user123');
```
### Express Middleware
Complete Express.js integration:
```typescript
import { ExpressUploadMiddleware } from '@mvp-factory/holy-upload';
const middleware = new ExpressUploadMiddleware({
uploadDir: './uploads',
maxSize: 5 * 1024 * 1024
});
// Use middleware in routes
app.post('/upload',
middleware.single('image'),
async (req, res) => {
try {
const result = await req.uploadSingle!();
res.json(result);
} catch (error) {
res.status(400).json({ success: false, error: error.message });
}
}
);
```
### File Validation
Advanced security validation:
```typescript
import { FileValidator } from '@mvp-factory/holy-upload';
const validation = await FileValidator.validate(file, {
maxSize: 5 * 1024 * 1024,
allowedMimeTypes: ['image/jpeg', 'image/png'],
allowedExtensions: ['jpg', 'jpeg', 'png'],
checkMalicious: true
});
if (!validation.isValid) {
console.log('Validation errors:', validation.errors);
}
```
### Image Optimization
Powerful image processing:
```typescript
import { ImageOptimizer } from '@mvp-factory/holy-upload';
// Optimize image
const result = await ImageOptimizer.optimize('input.jpg', 'output.jpg', {
maxWidth: 1920,
maxHeight: 1080,
jpegQuality: 85
});
// Convert to WebP
const webpResult = await ImageOptimizer.convertToWebP('image.jpg', 'image.webp', 80);
// Generate thumbnails
const thumbnails = await ImageOptimizer.generateThumbnails('image.jpg', [
{ width: 150, height: 150, suffix: 'thumb' },
{ width: 300, height: 300, suffix: 'small' }
], './thumbnails');
```
## Configuration Options
### UploadConfig
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `uploadDir` | string | `'./uploads'` | Upload directory path |
| `maxSize` | number | `10 * 1024 * 1024` | Maximum file size (10MB) |
| `maxWidth` | number | `1920` | Maximum image width |
| `maxHeight` | number | `1080` | Maximum image height |
| `allowedMimeTypes` | string[] | `['image/jpeg', 'image/png', 'image/gif', 'image/webp']` | Allowed MIME types |
| `allowedExtensions` | string[] | `['jpg', 'jpeg', 'png', 'gif', 'webp']` | Allowed file extensions |
| `enableOptimization` | boolean | `true` | Enable image optimization |
| `jpegQuality` | number | `85` | JPEG compression quality (1-100) |
| `pngCompression` | number | `8` | PNG compression level (0-9) |
| `webpQuality` | number | `85` | WebP quality (1-100) |
## Security Features
### File Validation
- **Magic Number Verification**: Validates file headers against MIME types
- **Malicious Content Detection**: Scans for script injections and suspicious patterns
- **Path Traversal Prevention**: Blocks directory traversal attempts
- **Filename Sanitization**: Removes dangerous characters and patterns
- **Size Limits**: Enforces file size restrictions
### Security Patterns Detected
```typescript
// Automatically detects and blocks:
const maliciousPatterns = [
/<script[^>]*>/i, // Script tags
/javascript:/i, // JavaScript protocols
/vbscript:/i, // VBScript protocols
/<\?php/i, // PHP tags
/<!--#exec/i, // Server-side includes
/\.(exe|bat|cmd|scr)$/i // Executable extensions
];
```
## Advanced Usage
### Custom Validation
```typescript
const customValidator = async (file: Express.Multer.File) => {
// Custom validation logic
if (file.originalname.includes('temp')) {
return {
isValid: false,
errors: ['Temporary files not allowed'],
mimeType: file.mimetype,
extension: path.extname(file.originalname),
size: file.size
};
}
return { isValid: true, errors: [] };
};
const middleware = new ExpressUploadMiddleware({
customValidator
});
```
### Storage Management
```typescript
// Get storage information
const storage = await handler.getStorageInfo('user123');
console.log(`Used: ${storage.totalUsedFormatted}`);
console.log(`Usage: ${storage.usagePercentage}%`);
// Clean up old files
const cleanup = await handler.cleanupFiles({
olderThanDays: 30,
dryRun: false
});
console.log(`Deleted ${cleanup.deletedCount} files`);
console.log(`Freed ${cleanup.freedSpaceFormatted}`);
```
### Thumbnail Generation
```typescript
// Generate multiple thumbnail sizes
const thumbnails = await handler.generateThumbnails('image.jpg', [
{ width: 64, height: 64, suffix: 'tiny' },
{ width: 150, height: 150, suffix: 'thumb' },
{ width: 300, height: 300, suffix: 'small' },
{ width: 800, height: 600, suffix: 'medium' }
]);
thumbnails.forEach(thumb => {
console.log(`${thumb.width}x${thumb.height}: ${thumb.path}`);
});
```
### WebP Conversion
```typescript
// Convert to WebP for better compression
const webpResult = await handler.convertToWebP('image.jpg', 80);
if (webpResult.success) {
console.log(`Savings: ${webpResult.savings.toFixed(1)}%`);
console.log(`WebP file: ${webpResult.webpFilename}`);
}
```
## Error Handling
```typescript
import {
UploadError,
ValidationError,
FileSizeError,
FileTypeError,
StorageError
} from '@mvp-factory/holy-upload';
try {
const result = await handler.uploadSingle(file);
} catch (error) {
if (error instanceof FileSizeError) {
console.log('File too large');
} else if (error instanceof FileTypeError) {
console.log('Invalid file type');
} else if (error instanceof ValidationError) {
console.log('Validation failed:', error.message);
} else if (error instanceof StorageError) {
console.log('Storage issue:', error.message);
}
}
```
## Health Monitoring
```typescript
// Check system health
const health = await handler.healthCheck();
if (!health.healthy) {
console.log('Issues detected:', health.issues);
}
console.log('Storage usage:', health.storageInfo.usagePercentage + '%');
```
## Examples
### Complete Upload API
```typescript
import express from 'express';
import { HolyUpload } from '@mvp-factory/holy-upload';
const app = express();
// Configure upload middleware
const uploadMiddleware = HolyUpload.createMiddleware({
uploadDir: process.env.UPLOAD_DIR || './uploads',
maxSize: parseInt(process.env.MAX_FILE_SIZE || '10485760'),
allowedMimeTypes: ['image/jpeg', 'image/png', 'image/webp'],
enableOptimization: true
});
// Single image upload
app.post('/api/upload/image',
uploadMiddleware.single('image'),
async (req, res) => {
try {
const result = await req.uploadSingle!();
res.json(result);
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
}
);
// Multiple images upload
app.post('/api/upload/gallery',
uploadMiddleware.multiple('images', 10),
async (req, res) => {
try {
const result = await req.uploadMultiple!();
res.json(result);
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
}
);
// Delete file
app.delete('/api/upload/:filename',
uploadMiddleware.createDeleteEndpoint()
);
// Get storage info
app.get('/api/upload/storage',
uploadMiddleware.createStorageInfoEndpoint()
);
// Serve files
app.use('/uploads',
uploadMiddleware.createFileServing('/uploads'),
express.static('./uploads')
);
app.listen(3000, () => {
console.log('Upload API running on port 3000');
});
```
### Frontend Integration
```javascript
// HTML form
<form id="uploadForm" enctype="multipart/form-data">
<input type="file" name="image" accept="image/*" required>
<button type="submit">Upload</button>
</form>
// JavaScript upload
document.getElementById('uploadForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
try {
const response = await fetch('/api/upload/image', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
console.log('Upload successful:', result.url);
// Display uploaded image
const img = document.createElement('img');
img.src = result.url;
document.body.appendChild(img);
} else {
console.error('Upload failed:', result.error);
}
} catch (error) {
console.error('Upload error:', error);
}
});
```
## Development vs Production
### Development Configuration
```typescript
const devConfig = HolyUpload.createDevConfig();
// Uses:
// - ./uploads/dev directory
// - 5MB max file size
// - Optimization disabled for faster uploads
```
### Production Configuration
```typescript
const prodConfig = HolyUpload.createProdConfig();
// Uses:
// - /tmp/uploads directory (cloud-friendly)
// - 10MB max file size
// - Optimization enabled
// - Lower quality settings for smaller files
```
## Testing
```bash
# Run tests
npm test
# Run with coverage
npm run test:coverage
```
## Contributing
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
## License
MIT © MVP Factory
## Support
- 📧 Email: support@mvp-factory.dev
- 🐛 Issues: [GitHub Issues](https://github.com/mvp-factory/modules/issues)
- 📖 Documentation: [Full Documentation](https://docs.mvp-factory.dev/modules/holy-upload)
---
**Extracted from Holy Habit project** - Battle-tested upload system used in production with advanced security and optimization features.