campverse-backend / Services /localStorageService.js
CampVerse CI
Deploy backend from GitHub Actions
c904036
Raw History Blame Contribute Delete
5.03 kB
/**
* Local Storage Service
* Replacement for Firebase/Drive storage - stores files locally
* This is a temporary solution - files will be lost on container restart
*/
const fs = require('fs').promises;
const path = require('path');
const { randomUUID } = require('crypto');
const { logger } = require('../Middleware/errorHandler');
class LocalStorageService {
constructor() {
this.baseDir = path.join(__dirname, '..', 'uploads');
this.initialized = false;
this.init();
}
async init() {
try {
// Create uploads directory if it doesn't exist
await fs.mkdir(this.baseDir, { recursive: true });
await fs.mkdir(path.join(this.baseDir, 'events'), { recursive: true });
await fs.mkdir(path.join(this.baseDir, 'profiles'), { recursive: true });
this.initialized = true;
logger.info('Local storage service initialized');
} catch (error) {
logger.error('Failed to initialize local storage service:', error);
}
}
/**
* Generate file path
*/
generateFilePath(category, filename) {
const timestamp = Date.now();
const uniqueId = randomUUID().substring(0, 8);
const fileExtension = path.extname(filename);
const baseName = path.basename(filename, fileExtension);
const cleanFilename = `${timestamp}_${uniqueId}_${baseName}${fileExtension}`;
return path.join(this.baseDir, category, cleanFilename);
}
/**
* Upload event image (logo or banner)
*/
async uploadEventImage(fileBuffer, filename, type, _mimetype) {
try {
if (!this.initialized) {
throw new Error('Local storage service not initialized');
}
const filePath = this.generateFilePath('events', filename);
await fs.writeFile(filePath, fileBuffer);
// Return a relative URL that could be served by express.static
const relativePath = path.relative(this.baseDir, filePath);
const url = `/uploads/${relativePath.replace(/\\/g, '/')}`;
logger.info(`Event ${type} uploaded locally: ${url}`);
return url;
} catch (error) {
logger.error(`Failed to upload event ${type}:`, error);
throw error;
}
}
/**
* Upload profile photo
*/
async uploadProfilePhoto(fileBuffer, filename, userId, _mimetype) {
try {
if (!this.initialized) {
throw new Error('Local storage service not initialized');
}
const filePath = this.generateFilePath('profiles', filename);
await fs.writeFile(filePath, fileBuffer);
const relativePath = path.relative(this.baseDir, filePath);
const url = `/uploads/${relativePath.replace(/\\/g, '/')}`;
logger.info(`Profile photo uploaded locally for user ${userId}: ${url}`);
return url;
} catch (error) {
logger.error('Failed to upload profile photo:', error);
throw error;
}
}
/**
* Delete file (placeholder - files are small and cleanup can be manual)
*/
async deleteEventImage(url) {
try {
if (!url || !url.startsWith('/uploads/')) {
return; // Not a local file or invalid URL
}
const relativePath = url.replace('/uploads/', '');
const filePath = path.join(this.baseDir, relativePath);
await fs.unlink(filePath);
logger.info(`Deleted local file: ${url}`);
} catch (error) {
logger.warn(`Failed to delete local file ${url}:`, error.message);
// Don't throw - deletion failures shouldn't break the app
}
}
/**
* Delete profile photo
*/
async deleteProfilePhoto(url) {
return this.deleteEventImage(url); // Same logic
}
/**
* Health check
*/
async healthCheck() {
try {
if (!this.initialized) {
return {
status: 'error',
message: 'Local storage service not initialized',
initialized: false
};
}
// Check if uploads directory exists and is writable
const fs = require('fs').promises;
await fs.access(this.baseDir, fs.constants.W_OK);
return {
status: 'healthy',
message: 'Local storage service is operational',
baseDir: this.baseDir,
initialized: this.initialized
};
} catch (error) {
return {
status: 'error',
message: error.message,
baseDir: this.baseDir,
initialized: this.initialized
};
}
}
}
// Create singleton instance
const localStorageService = new LocalStorageService();
module.exports = {
uploadEventImage: (fileBuffer, filename, type, mimetype) =>
localStorageService.uploadEventImage(fileBuffer, filename, type, mimetype),
uploadProfilePhoto: (fileBuffer, filename, userId, mimetype) =>
localStorageService.uploadProfilePhoto(fileBuffer, filename, userId, mimetype),
deleteEventImage: (url) =>
localStorageService.deleteEventImage(url),
deleteProfilePhoto: (url) =>
localStorageService.deleteProfilePhoto(url),
healthCheck: () =>
localStorageService.healthCheck(),
localStorageService
};