import { Router } from 'express'; import { authRequired } from '../../Application/Services/AuthMiddleware'; import { container } from '../../Application/Services/DIContainer'; import { ErrorResponseService } from '../../Application/Services/ErrorResponseService'; import { ValidationMiddleware } from '../../Application/Services/ValidationMiddleware'; import { GeneralSearchService } from '../../Application/Search/Generalsearch'; import { logRequest, logError, logWarning } from '../../Application/Services/Logger'; const deckRouter = Router(); // Create search service that isn't in the container yet const searchService = new GeneralSearchService(container.userRepository, container.organizationRepository, container.deckRepository); // Authenticated routes - Get decks with pagination (RECOMMENDED) deckRouter.get('/page/:from/:to', authRequired, async (req, res) => { try { const userId = (req as any).user.userId; const userOrgId = (req as any).user.orgId; const isAdmin = (req as any).user.authLevel === 1; const from = parseInt(req.params.from); const to = parseInt(req.params.to); if (isNaN(from) || isNaN(to) || from < 0 || to < from) { return res.status(400).json({ error: 'Invalid page parameters. "from" and "to" must be valid numbers with to >= from >= 0' }); } logRequest('Get decks by page endpoint accessed', req, res, { userId, userOrgId, isAdmin, from, to }); // Use paginated query handler for memory efficiency const result = await container.getDecksByPageQueryHandler.execute({ userId, userOrgId, isAdmin, from, to }); logRequest('Get decks page completed successfully', req, res, { userId, from, to, returnedCount: result.decks.length, totalCount: result.totalCount }); res.json(result); } catch (error) { logError('Get decks by page endpoint error', error as Error, req, res); res.status(500).json({ error: 'Internal server error' }); } }); deckRouter.post('/', authRequired, async (req, res) => { try { const userId = (req as any).user.userId; logRequest('Create deck endpoint accessed', req, res, { name: req.body.name, userId }); req.body.userid = userId; // Set userId in request body const result = await container.createDeckCommandHandler.execute(req.body); logRequest('Deck created successfully', req, res, { deckId: result.id, name: req.body.name, userId }); res.json(result); } catch (error) { logError('Create deck endpoint error', error as Error, req, res); if (error instanceof Error && (error.message.includes('duplicate') || error.message.includes('unique constraint'))) { return res.status(409).json({ error: 'Deck with this name already exists' }); } if (error instanceof Error && error.message.includes('validation')) { return res.status(400).json({ error: 'Invalid input data', details: error.message }); } res.status(500).json({ error: 'Internal server error' }); } }); deckRouter.get('/search', authRequired, async (req, res) => { try { const { query, limit, offset } = req.query; logRequest('Search decks endpoint accessed', req, res, { query, limit, offset }); if (!query || typeof query !== 'string') { logWarning('Deck search attempted without query', { query, hasQuery: !!query }, req, res); return res.status(400).json({ error: 'Search query is required' }); } const searchQuery = { query: query.trim(), limit: limit ? parseInt(limit as string) : 20, offset: offset ? parseInt(offset as string) : 0 }; // Validate pagination parameters if (searchQuery.limit < 1 || searchQuery.limit > 100) { logWarning('Invalid deck search limit parameter', { limit: searchQuery.limit }, req, res); return res.status(400).json({ error: 'Limit must be between 1 and 100' }); } if (searchQuery.offset < 0) { logWarning('Invalid deck search offset parameter', { offset: searchQuery.offset }, req, res); return res.status(400).json({ error: 'Offset must be non-negative' }); } const result = await searchService.searchFromUrl(req.originalUrl, searchQuery); logRequest('Deck search completed successfully', req, res, { query: searchQuery.query, resultCount: Array.isArray(result) ? result.length : 0 }); res.json(result); } catch (error) { logError('Search decks endpoint error', error as Error, req, res); res.status(500).json({ error: 'Internal server error' }); } }); deckRouter.get('/:id', authRequired, async (req, res) => { try { const deckId = req.params.id; logRequest('Get deck by id endpoint accessed', req, res, { deckId }); const result = await container.getDeckByIdQueryHandler.execute({ id: deckId }); if (!result) { logWarning('Deck not found', { deckId }, req, res); return res.status(404).json({ error: 'Deck not found' }); } logRequest('Deck retrieved successfully', req, res, { deckId }); res.json(result); } catch (error) { logError('Get deck by id endpoint error', error as Error, req, res); res.status(500).json({ error: 'Internal server error' }); } }); deckRouter.patch('/:id', authRequired, async (req, res) => { try { const deckId = req.params.id; const userId = (req as any).user.userId; logRequest('Update deck endpoint accessed', req, res, { deckId, userId, updateFields: Object.keys(req.body) }); const result = await container.updateDeckCommandHandler.execute({ id: deckId, ...req.body }); logRequest('Deck updated successfully', req, res, { deckId, userId }); res.json(result); } catch (error) { logError('Update deck endpoint error', error as Error, req, res); if (error instanceof Error && error.message.includes('not found')) { return res.status(404).json({ error: 'Deck not found' }); } if (error instanceof Error && (error.message.includes('duplicate') || error.message.includes('unique constraint'))) { return res.status(409).json({ error: 'Deck with this name already exists' }); } if (error instanceof Error && error.message.includes('validation')) { return res.status(400).json({ error: 'Invalid input data', details: error.message }); } if (error instanceof Error && error.message.includes('admin')) { return res.status(403).json({ error: 'Forbidden: ' + error.message }); } res.status(500).json({ error: 'Internal server error' }); } }); deckRouter.delete('/:id', authRequired, async (req, res) => { try { const deckId = req.params.id; const userId = (req as any).user.userId; logRequest('Soft delete deck endpoint accessed', req, res, { deckId, userId }); const result = await container.deleteDeckCommandHandler.execute({ id: deckId, soft: true }); logRequest('Deck soft delete successful', req, res, { deckId, userId, success: result }); res.json({ success: result }); } catch (error) { logError('Soft delete deck endpoint error', error as Error, req, res); if (error instanceof Error && error.message.includes('not found')) { return res.status(404).json({ error: 'Deck not found' }); } res.status(500).json({ error: 'Internal server error' }); } }); export default deckRouter;