@mvp-factory/holy-upload
v1.0.0
Published
File upload processing system extracted from Holy Habit project with security validation and image optimization
Downloads
8
Maintainers
Readme
@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
npm install @mvp-factory/holy-uploadQuick Start
1. Basic Setup
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
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:
# 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=85Core Components
UploadHandler
Main class for handling file uploads:
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:
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:
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:
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
// 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
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
// 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
// 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
// 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
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
// 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
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
// 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
const devConfig = HolyUpload.createDevConfig();
// Uses:
// - ./uploads/dev directory
// - 5MB max file size
// - Optimization disabled for faster uploadsProduction Configuration
const prodConfig = HolyUpload.createProdConfig();
// Uses:
// - /tmp/uploads directory (cloud-friendly)
// - 10MB max file size
// - Optimization enabled
// - Lower quality settings for smaller filesTesting
# Run tests
npm test
# Run with coverage
npm run test:coverageContributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
MIT © MVP Factory
Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 📖 Documentation: Full Documentation
Extracted from Holy Habit project - Battle-tested upload system used in production with advanced security and optimization features.
