Major milestone: Frontend implementation complete for Shattered Void MMO FRONTEND IMPLEMENTATION: - React 18 + TypeScript + Vite development environment - Tailwind CSS with custom dark theme for sci-fi aesthetic - Zustand state management with authentication persistence - Socket.io WebSocket client with auto-reconnection - Protected routing with authentication guards - Responsive design with mobile-first approach AUTHENTICATION SYSTEM: - Login/register forms with comprehensive validation - JWT token management with localStorage persistence - Password strength validation and user feedback - Protected routes and authentication guards CORE GAME INTERFACE: - Colony management dashboard with real-time updates - Resource display with live production tracking - WebSocket integration for real-time game events - Navigation with connection status indicator - Toast notifications for user feedback BACKEND ENHANCEMENTS: - Complete Research System with technology tree (23 technologies) - Fleet Management System with ship designs and movement - Enhanced Authentication with email verification and password reset - Complete game tick integration for all systems - Advanced WebSocket events for real-time updates ARCHITECTURE FEATURES: - Type-safe TypeScript throughout - Component-based architecture with reusable UI elements - API client with request/response interceptors - Error handling and loading states - Performance optimized builds with code splitting Phase 2 Status: Frontend foundation complete (Week 1-2 objectives met) Ready for: Colony management, fleet operations, research interface Next: Enhanced gameplay features and admin interface 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
278 lines
No EOL
8.8 KiB
JavaScript
278 lines
No EOL
8.8 KiB
JavaScript
/**
|
|
* 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; |