- Complete PostgreSQL database schema with 21+ tables - Express.js server with dual authentication (player/admin) - WebSocket support for real-time features - Comprehensive middleware (auth, validation, logging, security) - Game systems: colonies, resources, fleets, research, factions - Plugin-based combat architecture - Admin panel foundation - Production-ready logging and error handling - Docker support and CI/CD ready - Complete project structure following CLAUDE.md patterns 🤖 Generated with Claude Code (https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
298 lines
No EOL
7.1 KiB
JavaScript
298 lines
No EOL
7.1 KiB
JavaScript
/**
|
|
* Player Authentication Controller
|
|
* Handles player registration, login, and authentication-related endpoints
|
|
*/
|
|
|
|
const PlayerService = require('../../services/user/PlayerService');
|
|
const { asyncHandler } = require('../../middleware/error.middleware');
|
|
const logger = require('../../utils/logger');
|
|
|
|
const playerService = new PlayerService();
|
|
|
|
/**
|
|
* Register a new player
|
|
* POST /api/auth/register
|
|
*/
|
|
const register = asyncHandler(async (req, res) => {
|
|
const correlationId = req.correlationId;
|
|
const { email, username, password } = req.body;
|
|
|
|
logger.info('Player registration request received', {
|
|
correlationId,
|
|
email,
|
|
username
|
|
});
|
|
|
|
const player = await playerService.registerPlayer({
|
|
email,
|
|
username,
|
|
password
|
|
}, correlationId);
|
|
|
|
logger.info('Player registration successful', {
|
|
correlationId,
|
|
playerId: player.id,
|
|
email: player.email,
|
|
username: player.username
|
|
});
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
message: 'Player registered successfully',
|
|
data: {
|
|
player
|
|
},
|
|
correlationId
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Player login
|
|
* POST /api/auth/login
|
|
*/
|
|
const login = asyncHandler(async (req, res) => {
|
|
const correlationId = req.correlationId;
|
|
const { email, password } = req.body;
|
|
|
|
logger.info('Player login request received', {
|
|
correlationId,
|
|
email
|
|
});
|
|
|
|
const authResult = await playerService.authenticatePlayer({
|
|
email,
|
|
password
|
|
}, correlationId);
|
|
|
|
logger.info('Player login successful', {
|
|
correlationId,
|
|
playerId: authResult.player.id,
|
|
email: authResult.player.email,
|
|
username: authResult.player.username
|
|
});
|
|
|
|
// Set refresh token as httpOnly cookie
|
|
res.cookie('refreshToken', authResult.tokens.refreshToken, {
|
|
httpOnly: true,
|
|
secure: process.env.NODE_ENV === 'production',
|
|
sameSite: 'strict',
|
|
maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days
|
|
});
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Login successful',
|
|
data: {
|
|
player: authResult.player,
|
|
accessToken: authResult.tokens.accessToken
|
|
},
|
|
correlationId
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Player logout
|
|
* POST /api/auth/logout
|
|
*/
|
|
const logout = asyncHandler(async (req, res) => {
|
|
const correlationId = req.correlationId;
|
|
const playerId = req.user?.playerId;
|
|
|
|
logger.info('Player logout request received', {
|
|
correlationId,
|
|
playerId
|
|
});
|
|
|
|
// Clear refresh token cookie
|
|
res.clearCookie('refreshToken');
|
|
|
|
// TODO: Add token to blacklist if implementing token blacklisting
|
|
|
|
logger.info('Player logout successful', {
|
|
correlationId,
|
|
playerId
|
|
});
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Logout successful',
|
|
correlationId
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Refresh access token
|
|
* POST /api/auth/refresh
|
|
*/
|
|
const refresh = asyncHandler(async (req, res) => {
|
|
const correlationId = req.correlationId;
|
|
const refreshToken = req.cookies.refreshToken;
|
|
|
|
if (!refreshToken) {
|
|
logger.warn('Token refresh request without refresh token', {
|
|
correlationId
|
|
});
|
|
|
|
return res.status(401).json({
|
|
success: false,
|
|
message: 'Refresh token not provided',
|
|
correlationId
|
|
});
|
|
}
|
|
|
|
// TODO: Implement refresh token validation and new token generation
|
|
// For now, return error indicating feature not implemented
|
|
logger.warn('Token refresh requested but not implemented', {
|
|
correlationId
|
|
});
|
|
|
|
res.status(501).json({
|
|
success: false,
|
|
message: 'Token refresh feature not yet implemented',
|
|
correlationId
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Get current player profile
|
|
* GET /api/auth/me
|
|
*/
|
|
const getProfile = asyncHandler(async (req, res) => {
|
|
const correlationId = req.correlationId;
|
|
const playerId = req.user.playerId;
|
|
|
|
logger.info('Player profile request received', {
|
|
correlationId,
|
|
playerId
|
|
});
|
|
|
|
const profile = await playerService.getPlayerProfile(playerId, correlationId);
|
|
|
|
logger.info('Player profile retrieved', {
|
|
correlationId,
|
|
playerId,
|
|
username: profile.username
|
|
});
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Profile retrieved successfully',
|
|
data: {
|
|
player: profile
|
|
},
|
|
correlationId
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Update current player profile
|
|
* PUT /api/auth/me
|
|
*/
|
|
const updateProfile = asyncHandler(async (req, res) => {
|
|
const correlationId = req.correlationId;
|
|
const playerId = req.user.playerId;
|
|
const updateData = req.body;
|
|
|
|
logger.info('Player profile update request received', {
|
|
correlationId,
|
|
playerId,
|
|
updateFields: Object.keys(updateData)
|
|
});
|
|
|
|
const updatedProfile = await playerService.updatePlayerProfile(
|
|
playerId,
|
|
updateData,
|
|
correlationId
|
|
);
|
|
|
|
logger.info('Player profile updated successfully', {
|
|
correlationId,
|
|
playerId,
|
|
username: updatedProfile.username
|
|
});
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Profile updated successfully',
|
|
data: {
|
|
player: updatedProfile
|
|
},
|
|
correlationId
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Verify player token (for testing/debugging)
|
|
* GET /api/auth/verify
|
|
*/
|
|
const verifyToken = asyncHandler(async (req, res) => {
|
|
const correlationId = req.correlationId;
|
|
const user = req.user;
|
|
|
|
logger.info('Token verification request received', {
|
|
correlationId,
|
|
playerId: user.playerId,
|
|
username: user.username
|
|
});
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Token is valid',
|
|
data: {
|
|
user: {
|
|
playerId: user.playerId,
|
|
email: user.email,
|
|
username: user.username,
|
|
type: user.type,
|
|
tokenIssuedAt: new Date(user.iat * 1000),
|
|
tokenExpiresAt: new Date(user.exp * 1000)
|
|
}
|
|
},
|
|
correlationId
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Change player password
|
|
* POST /api/auth/change-password
|
|
*/
|
|
const changePassword = asyncHandler(async (req, res) => {
|
|
const correlationId = req.correlationId;
|
|
const playerId = req.user.playerId;
|
|
const { currentPassword, newPassword } = req.body;
|
|
|
|
logger.info('Password change request received', {
|
|
correlationId,
|
|
playerId
|
|
});
|
|
|
|
// TODO: Implement password change functionality
|
|
// This would involve:
|
|
// 1. Verify current password
|
|
// 2. Validate new password strength
|
|
// 3. Hash new password
|
|
// 4. Update in database
|
|
// 5. Optionally invalidate existing tokens
|
|
|
|
logger.warn('Password change requested but not implemented', {
|
|
correlationId,
|
|
playerId
|
|
});
|
|
|
|
res.status(501).json({
|
|
success: false,
|
|
message: 'Password change feature not yet implemented',
|
|
correlationId
|
|
});
|
|
});
|
|
|
|
module.exports = {
|
|
register,
|
|
login,
|
|
logout,
|
|
refresh,
|
|
getProfile,
|
|
updateProfile,
|
|
verifyToken,
|
|
changePassword
|
|
}; |