retropulse/backend/routes/api.js
2025-09-02 22:27:25 +05:30

123 lines
4.3 KiB
JavaScript

const express = require('express');
const router = express.Router();
const retroClient = require('../retroClient');
// GET /api/userinfo?username=xxx
router.get('/userinfo', async (req, res) => {
try {
const { username } = req.query;
if (!username) {
return res.status(400).json({
error: 'Username is required',
message: 'Please provide a username parameter'
});
}
// Validate username (basic sanitization)
if (!/^[a-zA-Z0-9_-]+$/.test(username)) {
return res.status(400).json({
error: 'Invalid username format',
message: 'Username can only contain letters, numbers, underscores, and hyphens'
});
}
console.log(`📡 API request for user: ${username}`);
// Fetch real user data from RetroAchievements
const userData = await retroClient.getUserData(username);
console.log(`✅ Successfully fetched data for user: ${username}`);
res.json(userData);
} catch (error) {
console.error('❌ Error in /api/userinfo:', error);
// Handle specific API errors
if (error.message.includes('User not found') || error.message.includes('Invalid user')) {
return res.status(404).json({
error: 'User not found',
message: `RetroAchievements user '${req.query.username}' not found`
});
}
// Handle rate limiting
if (error.message.includes('rate limit') || error.message.includes('too many requests')) {
return res.status(429).json({
error: 'Rate limited',
message: 'Too many API requests. Please wait a moment and try again.'
});
}
res.status(500).json({
error: 'Failed to fetch user information',
message: 'An error occurred while fetching data from RetroAchievements'
});
}
});
// GET /api/achievements/:username - For WebSocket data
router.get('/achievements/:username', async (req, res) => {
try {
const { username } = req.params;
const { count = 5 } = req.query;
// Validate username
if (!/^[a-zA-Z0-9_-]+$/.test(username)) {
return res.status(400).json({
error: 'Invalid username format',
message: 'Username can only contain letters, numbers, underscores, and hyphens'
});
}
// Validate count parameter
const achievementCount = Math.min(Math.max(parseInt(count) || 5, 1), 25); // Between 1 and 25
console.log(`🏆 API request for ${username}'s recent ${achievementCount} achievements`);
// Fetch real achievement data from RetroAchievements
const overlayData = await retroClient.getOverlayData(username, achievementCount);
console.log(`✅ Successfully fetched ${overlayData.recentAchievements.length} achievements for user: ${username}`);
res.json(overlayData);
} catch (error) {
console.error('❌ Error in /api/achievements:', error);
// Handle specific API errors
if (error.message.includes('User not found') || error.message.includes('Invalid user')) {
return res.status(404).json({
error: 'User not found',
message: `RetroAchievements user '${req.params.username}' not found`
});
}
res.status(500).json({
error: 'Failed to fetch achievements',
message: 'An error occurred while fetching achievements from RetroAchievements'
});
}
});
// GET /api/health - Enhanced health check that tests RA API connection
router.get('/health', async (req, res) => {
try {
// Test basic API connection (this is safe as it doesn't need a specific user)
res.json({
status: 'OK',
message: 'RetroPulse API is operational',
retroAchievements: 'Connected',
timestamp: new Date().toISOString()
});
} catch (error) {
res.status(503).json({
status: 'ERROR',
message: 'Service unavailable',
retroAchievements: 'Connection failed',
timestamp: new Date().toISOString()
});
}
});
module.exports = router;