/** * Player API Routes * Defines all player-facing API endpoints with proper middleware and validation */ const express = require('express'); const router = express.Router(); // Import middleware const { authenticatePlayer, optionalPlayerAuth, requireOwnership, injectPlayerId } = require('../middleware/auth.middleware'); const { authenticateToken } = require('../middleware/auth'); // Standardized auth const { rateLimiters } = require('../middleware/rateLimit.middleware'); const { validators, validateRequest } = require('../middleware/validation.middleware'); const { accountLockoutProtection, rateLimiter, passwordStrengthValidator, requireEmailVerification, sanitizeInput } = require('../middleware/security.middleware'); const { validateRequest: validateAuthRequest, validateRegistrationUniqueness, registerPlayerSchema, loginPlayerSchema, verifyEmailSchema, resendVerificationSchema, requestPasswordResetSchema, resetPasswordSchema, changePasswordSchema } = require('../validators/auth.validators'); const corsMiddleware = require('../middleware/cors.middleware'); // Use standardized authentication for players const authenticatePlayerToken = authenticateToken('player'); const optionalPlayerToken = require('../middleware/auth').optionalAuth('player'); // Import controllers const authController = require('../controllers/api/auth.controller'); const playerController = require('../controllers/api/player.controller'); // Apply CORS to all API routes router.use(corsMiddleware); // Apply general API rate limiting router.use(rateLimiters.player); /** * API Status and Information */ router.get('/', (req, res) => { res.json({ name: 'Shattered Void - Player API', version: process.env.npm_package_version || '0.1.0', status: 'operational', timestamp: new Date().toISOString(), correlationId: req.correlationId, endpoints: { authentication: '/api/auth', player: '/api/player', game: { colonies: '/api/colonies', fleets: '/api/fleets', research: '/api/research', galaxy: '/api/galaxy', combat: '/api/combat' } } }); }); /** * Authentication Routes * /api/auth/* */ const authRoutes = express.Router(); // Public authentication endpoints (with stricter rate limiting) authRoutes.post('/register', rateLimiter({ maxRequests: 3, windowMinutes: 60, action: 'registration' }), sanitizeInput(['email', 'username']), validateAuthRequest(registerPlayerSchema), validateRegistrationUniqueness(), passwordStrengthValidator('password'), authController.register ); authRoutes.post('/login', rateLimiter({ maxRequests: 5, windowMinutes: 15, action: 'login' }), accountLockoutProtection, sanitizeInput(['email']), validateAuthRequest(loginPlayerSchema), authController.login ); // Protected authentication endpoints authRoutes.post('/logout', authenticatePlayerToken, authController.logout ); authRoutes.post('/refresh', rateLimiters.auth, authController.refresh ); authRoutes.get('/me', authenticatePlayerToken, authController.getProfile ); authRoutes.put('/me', authenticatePlayerToken, requireEmailVerification, rateLimiter({ maxRequests: 5, windowMinutes: 60, action: 'profile_update' }), sanitizeInput(['username', 'displayName', 'bio']), validateRequest(require('joi').object({ username: require('joi').string().alphanum().min(3).max(20).optional(), displayName: require('joi').string().min(1).max(50).optional(), bio: require('joi').string().max(500).optional() }), 'body'), authController.updateProfile ); authRoutes.get('/verify', authenticatePlayerToken, authController.verifyToken ); authRoutes.post('/change-password', authenticatePlayerToken, rateLimiter({ maxRequests: 3, windowMinutes: 60, action: 'password_change' }), validateAuthRequest(changePasswordSchema), passwordStrengthValidator('newPassword'), authController.changePassword ); // Email verification endpoints authRoutes.post('/verify-email', rateLimiter({ maxRequests: 5, windowMinutes: 15, action: 'email_verification' }), validateAuthRequest(verifyEmailSchema), authController.verifyEmail ); authRoutes.post('/resend-verification', rateLimiter({ maxRequests: 3, windowMinutes: 60, action: 'resend_verification' }), sanitizeInput(['email']), validateAuthRequest(resendVerificationSchema), authController.resendVerification ); // Password reset endpoints authRoutes.post('/request-password-reset', rateLimiter({ maxRequests: 3, windowMinutes: 60, action: 'password_reset_request' }), sanitizeInput(['email']), validateAuthRequest(requestPasswordResetSchema), authController.requestPasswordReset ); authRoutes.post('/reset-password', rateLimiter({ maxRequests: 3, windowMinutes: 60, action: 'password_reset' }), validateAuthRequest(resetPasswordSchema), passwordStrengthValidator('newPassword'), authController.resetPassword ); // Security utility endpoints authRoutes.post('/check-password-strength', rateLimiter({ maxRequests: 10, windowMinutes: 5, action: 'password_check' }), authController.checkPasswordStrength ); authRoutes.get('/security-status', authenticatePlayerToken, authController.getSecurityStatus ); // Mount authentication routes router.use('/auth', authRoutes); /** * Player Management Routes * /api/player/* */ const playerManagementRoutes = express.Router(); // All player routes require authentication playerManagementRoutes.use(authenticatePlayerToken); playerManagementRoutes.get('/dashboard', playerController.getDashboard); playerManagementRoutes.get('/resources', playerController.getResources); playerManagementRoutes.get('/stats', playerController.getStats); playerManagementRoutes.put('/settings', validateRequest(require('joi').object({ // TODO: Define settings schema notifications: require('joi').object({ email: require('joi').boolean().optional(), push: require('joi').boolean().optional(), battles: require('joi').boolean().optional(), colonies: require('joi').boolean().optional() }).optional(), ui: require('joi').object({ theme: require('joi').string().valid('light', 'dark').optional(), language: require('joi').string().valid('en', 'es', 'fr', 'de').optional() }).optional() }), 'body'), playerController.updateSettings ); playerManagementRoutes.get('/activity', validators.validatePagination, playerController.getActivity ); playerManagementRoutes.get('/notifications', validateRequest(require('joi').object({ unreadOnly: require('joi').boolean().default(false) }), 'query'), playerController.getNotifications ); playerManagementRoutes.put('/notifications/read', validateRequest(require('joi').object({ notificationIds: require('joi').array().items( require('joi').number().integer().positive() ).min(1).required() }), 'body'), playerController.markNotificationsRead ); // Mount player management routes (separate from game feature routes) router.use('/player', playerManagementRoutes); /** * Combat Routes * /api/combat/* */ router.use('/combat', require('./api/combat')); /** * Game Feature Routes * Connect to existing working player route modules */ // Import existing player route modules for game features const playerGameRoutes = require('./player'); // Mount player game routes under /player-game prefix to avoid conflicts // These contain the actual game functionality (colonies, resources, fleets, etc.) router.use('/player-game', playerGameRoutes); // Direct mount of specific game features for convenience (these are duplicates of what's in /player/*) // These provide direct access without the /player prefix for backwards compatibility router.use('/colonies', authenticatePlayerToken, require('./player/colonies')); router.use('/resources', authenticatePlayerToken, require('./player/resources')); router.use('/fleets', authenticatePlayerToken, require('./player/fleets')); router.use('/research', authenticatePlayerToken, require('./player/research')); router.use('/galaxy', optionalPlayerToken, require('./player/galaxy')); router.use('/notifications', authenticatePlayerToken, require('./player/notifications')); router.use('/events', authenticatePlayerToken, require('./player/events')); /** * Error handling for API routes */ router.use('*', (req, res) => { res.status(404).json({ success: false, error: 'API endpoint not found', message: `The endpoint ${req.method} ${req.originalUrl} does not exist`, correlationId: req.correlationId, timestamp: new Date().toISOString() }); }); module.exports = router;