125 lines
4.4 KiB
TypeScript
125 lines
4.4 KiB
TypeScript
import express, { Request, Response } from 'express';
|
|
import multer from 'multer';
|
|
import { DIContainer } from '../../Application/Services/DIContainer';
|
|
import { authRequired } from '../../Application/Services/AuthMiddleware';
|
|
import { logRequest, logError, logWarning } from '../../Application/Services/Logger';
|
|
|
|
// Extend Express Request interface for file uploads
|
|
declare global {
|
|
namespace Express {
|
|
interface Request {
|
|
file?: Express.Multer.File;
|
|
}
|
|
}
|
|
}
|
|
|
|
const router = express.Router();
|
|
const container = DIContainer.getInstance();
|
|
|
|
// Configure multer for file uploads
|
|
const upload = multer({
|
|
storage: multer.memoryStorage(),
|
|
limits: {
|
|
fileSize: 10 * 1024 * 1024, // 10MB limit
|
|
},
|
|
fileFilter: (req: any, file: any, cb: any) => {
|
|
if (file.mimetype === 'application/json' || file.originalname.endsWith('.spr')) {
|
|
cb(null, true);
|
|
} else {
|
|
cb(new Error('Only JSON and .spr files are allowed'));
|
|
}
|
|
}
|
|
});
|
|
|
|
// Export deck to .spr file (encrypted) - users can only export their own decks
|
|
router.get('/export/:deckId', authRequired, async (req: Request, res: Response) => {
|
|
try {
|
|
const { deckId } = req.params;
|
|
const userId = (req as any).user.userId;
|
|
|
|
logRequest('Export deck endpoint accessed', req, res, { deckId, userId });
|
|
|
|
// Check if user owns the deck
|
|
const deck = await container.deckRepository.findById(deckId);
|
|
if (!deck) {
|
|
logWarning('Deck not found for export', { deckId, userId }, req, res);
|
|
return res.status(404).json({ error: 'Deck not found' });
|
|
}
|
|
|
|
// Users can only export their own decks
|
|
if (deck.userid !== userId) {
|
|
logWarning('Access denied - user attempted to export deck they do not own', {
|
|
deckId,
|
|
userId,
|
|
deckOwnerId: deck.userid
|
|
}, req, res);
|
|
return res.status(403).json({ error: 'Access denied - you can only export your own decks' });
|
|
}
|
|
|
|
const sprData = await container.deckImportExportService.exportDeckToSpr(deckId, userId);
|
|
|
|
res.setHeader('Content-Type', 'application/octet-stream');
|
|
res.setHeader('Content-Disposition', `attachment; filename="${deck.name || 'deck'}.spr"`);
|
|
|
|
logRequest('Deck exported successfully', req, res, {
|
|
deckId,
|
|
userId,
|
|
deckName: deck.name,
|
|
fileSize: sprData.length
|
|
});
|
|
|
|
res.send(sprData);
|
|
} catch (error) {
|
|
logError('Export deck endpoint error', error as Error, req, res);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
|
|
// Import deck from .spr file (encrypted) - imported deck will be owned by the importing user
|
|
router.post('/import', authRequired, upload.single('file'), async (req: Request, res: Response) => {
|
|
try {
|
|
const userId = (req as any).user.userId;
|
|
|
|
logRequest('Import deck endpoint accessed', req, res, {
|
|
userId,
|
|
hasFile: !!req.file,
|
|
fileName: req.file?.originalname,
|
|
fileSize: req.file?.size
|
|
});
|
|
|
|
if (!req.file) {
|
|
logWarning('No file uploaded for deck import', { userId }, req, res);
|
|
return res.status(400).json({ error: 'No file uploaded' });
|
|
}
|
|
|
|
const fileBuffer = req.file!.buffer;
|
|
|
|
// Import the deck and assign ownership to the current user
|
|
const result = await container.deckImportExportService.importDeckFromSpr(fileBuffer, userId);
|
|
|
|
logRequest('Deck imported successfully', req, res, {
|
|
userId,
|
|
deckId: result.id,
|
|
deckName: result.name || 'Unknown',
|
|
fileName: req.file.originalname,
|
|
fileSize: req.file.size
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Deck imported successfully and added to your collection',
|
|
deckId: result.id
|
|
});
|
|
} catch (error) {
|
|
logError('Import deck endpoint error', error as Error, req, res);
|
|
|
|
if (error instanceof Error && error.message.includes('Invalid')) {
|
|
return res.status(400).json({ error: 'Invalid file format or corrupted data' });
|
|
} else {
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
}
|
|
});
|
|
|
|
export default router;
|