bugfix
This commit is contained in:
@@ -2,16 +2,16 @@ import express from 'express';
|
|||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { query } from '../db/index.js';
|
import { query } from '../db/index.js';
|
||||||
|
import { asyncHandler, AppError } from '../middleware/errorHandler.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// Register
|
// Register
|
||||||
router.post('/register', async (req, res) => {
|
router.post('/register', asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const { email, username, password } = req.body;
|
const { email, username, password } = req.body;
|
||||||
|
|
||||||
if (!email || !username || !password) {
|
if (!email || !username || !password) {
|
||||||
return res.status(400).json({ error: 'All fields required' });
|
throw new AppError('All fields required', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user exists
|
// Check if user exists
|
||||||
@@ -21,7 +21,7 @@ router.post('/register', async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (existing.length > 0) {
|
if (existing.length > 0) {
|
||||||
return res.status(400).json({ error: 'User already exists' });
|
throw new AppError('User already exists', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash password
|
// Hash password
|
||||||
@@ -43,19 +43,14 @@ router.post('/register', async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
res.json({ token, user: { id: userId, email, username } });
|
res.json({ token, user: { id: userId, email, username } });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Register error:', error);
|
|
||||||
res.status(500).json({ error: 'Registration failed' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
router.post('/login', async (req, res) => {
|
router.post('/login', asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const { email, password } = req.body;
|
const { email, password } = req.body;
|
||||||
|
|
||||||
if (!email || !password) {
|
if (!email || !password) {
|
||||||
return res.status(400).json({ error: 'Email and password required' });
|
throw new AppError('Email and password required', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find user
|
// Find user
|
||||||
@@ -65,7 +60,7 @@ router.post('/login', async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (users.length === 0) {
|
if (users.length === 0) {
|
||||||
return res.status(401).json({ error: 'Invalid credentials' });
|
throw new AppError('Invalid credentials', 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = users[0];
|
const user = users[0];
|
||||||
@@ -73,7 +68,7 @@ router.post('/login', async (req, res) => {
|
|||||||
// Check password
|
// Check password
|
||||||
const validPassword = await bcrypt.compare(password, user.password_hash);
|
const validPassword = await bcrypt.compare(password, user.password_hash);
|
||||||
if (!validPassword) {
|
if (!validPassword) {
|
||||||
return res.status(401).json({ error: 'Invalid credentials' });
|
throw new AppError('Invalid credentials', 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate token
|
// Generate token
|
||||||
@@ -87,18 +82,13 @@ router.post('/login', async (req, res) => {
|
|||||||
token,
|
token,
|
||||||
user: { id: user.id, email: user.email, username: user.username }
|
user: { id: user.id, email: user.email, username: user.username }
|
||||||
});
|
});
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Login error:', error);
|
|
||||||
res.status(500).json({ error: 'Login failed' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get current user
|
// Get current user
|
||||||
router.get('/me', async (req, res) => {
|
router.get('/me', asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const authHeader = req.headers.authorization;
|
const authHeader = req.headers.authorization;
|
||||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||||
return res.status(401).json({ error: 'No token provided' });
|
throw new AppError('No token provided', 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = authHeader.substring(7);
|
const token = authHeader.substring(7);
|
||||||
@@ -110,13 +100,10 @@ router.get('/me', async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (users.length === 0) {
|
if (users.length === 0) {
|
||||||
return res.status(404).json({ error: 'User not found' });
|
throw new AppError('User not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({ user: users[0] });
|
res.json({ user: users[0] });
|
||||||
} catch (error) {
|
}));
|
||||||
res.status(401).json({ error: 'Invalid token' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { query } from '../db/index.js';
|
import { query } from '../db/index.js';
|
||||||
import { authMiddleware } from '../middleware/auth.js';
|
import { authMiddleware } from '../middleware/auth.js';
|
||||||
|
import { asyncHandler, AppError } from '../middleware/errorHandler.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// Get all challenges for the current user
|
// Get all challenges for the current user
|
||||||
router.get('/', authMiddleware, async (req, res) => {
|
router.get('/', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const challenges = await query(
|
const challenges = await query(
|
||||||
`SELECT
|
`SELECT
|
||||||
c.*,
|
c.*,
|
||||||
@@ -22,15 +22,10 @@ router.get('/', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
res.json({ challenges });
|
res.json({ challenges });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Get challenges error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to fetch challenges' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get a single challenge with details
|
// Get a single challenge with details
|
||||||
router.get('/:id', authMiddleware, async (req, res) => {
|
router.get('/:id', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const challengeId = req.params.id;
|
const challengeId = req.params.id;
|
||||||
|
|
||||||
// Get challenge details
|
// Get challenge details
|
||||||
@@ -43,7 +38,7 @@ router.get('/:id', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (challenges.length === 0) {
|
if (challenges.length === 0) {
|
||||||
return res.status(404).json({ error: 'Challenge not found' });
|
throw new AppError('Challenge not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
const challenge = challenges[0];
|
const challenge = challenges[0];
|
||||||
@@ -56,7 +51,7 @@ router.get('/:id', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (challenge.created_by !== req.user.userId && access.length === 0) {
|
if (challenge.created_by !== req.user.userId && access.length === 0) {
|
||||||
return res.status(403).json({ error: 'Access denied' });
|
throw new AppError('Access denied', 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get participants with their points
|
// Get participants with their points
|
||||||
@@ -84,19 +79,14 @@ router.get('/:id', authMiddleware, async (req, res) => {
|
|||||||
participants,
|
participants,
|
||||||
creator_points: creatorPoints[0].points
|
creator_points: creatorPoints[0].points
|
||||||
});
|
});
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Get challenge error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to fetch challenge' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a new challenge
|
// Create a new challenge
|
||||||
router.post('/', authMiddleware, async (req, res) => {
|
router.post('/', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const { title, cover_image_url, tmdb_id, media_type } = req.body;
|
const { title, cover_image_url, tmdb_id, media_type } = req.body;
|
||||||
|
|
||||||
if (!title) {
|
if (!title) {
|
||||||
return res.status(400).json({ error: 'Title is required' });
|
throw new AppError('Title is required', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await query(
|
const result = await query(
|
||||||
@@ -115,15 +105,10 @@ router.post('/', authMiddleware, async (req, res) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
res.json({ challenge });
|
res.json({ challenge });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Create challenge error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to create challenge' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Invite users to a challenge
|
// Invite users to a challenge
|
||||||
router.post('/:id/invite', authMiddleware, async (req, res) => {
|
router.post('/:id/invite', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const challengeId = req.params.id;
|
const challengeId = req.params.id;
|
||||||
const { user_ids, emails } = req.body;
|
const { user_ids, emails } = req.body;
|
||||||
|
|
||||||
@@ -134,7 +119,7 @@ router.post('/:id/invite', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (challenges.length === 0) {
|
if (challenges.length === 0) {
|
||||||
return res.status(404).json({ error: 'Challenge not found' });
|
throw new AppError('Challenge not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
const challenge = challenges[0];
|
const challenge = challenges[0];
|
||||||
@@ -147,7 +132,7 @@ router.post('/:id/invite', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (participation.length === 0) {
|
if (participation.length === 0) {
|
||||||
return res.status(403).json({ error: 'Only challenge participants can invite others' });
|
throw new AppError('Only challenge participants can invite others', 403);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,20 +172,15 @@ router.post('/:id/invite', authMiddleware, async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.json({ invited: invitedUsers.length });
|
res.json({ invited: invitedUsers.length });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Invite error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to send invites' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Accept/reject challenge invitation
|
// Accept/reject challenge invitation
|
||||||
router.post('/:id/respond', authMiddleware, async (req, res) => {
|
router.post('/:id/respond', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const challengeId = req.params.id;
|
const challengeId = req.params.id;
|
||||||
const { status } = req.body; // 'accepted' or 'rejected'
|
const { status } = req.body; // 'accepted' or 'rejected'
|
||||||
|
|
||||||
if (!['accepted', 'rejected'].includes(status)) {
|
if (!['accepted', 'rejected'].includes(status)) {
|
||||||
return res.status(400).json({ error: 'Invalid status' });
|
throw new AppError('Invalid status', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
await query(
|
await query(
|
||||||
@@ -209,10 +189,6 @@ router.post('/:id/respond', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
res.json({ status });
|
res.json({ status });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Respond error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to respond to invitation' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { query } from '../db/index.js';
|
import { query } from '../db/index.js';
|
||||||
import { authMiddleware } from '../middleware/auth.js';
|
import { authMiddleware } from '../middleware/auth.js';
|
||||||
|
import { asyncHandler, AppError } from '../middleware/errorHandler.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// Search for users by username or email
|
// Search for users by username or email
|
||||||
router.get('/search', authMiddleware, async (req, res) => {
|
router.get('/search', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const { q } = req.query;
|
const { q } = req.query;
|
||||||
|
|
||||||
if (!q || q.trim().length < 2) {
|
if (!q || q.trim().length < 2) {
|
||||||
@@ -24,15 +24,10 @@ router.get('/search', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
res.json({ users });
|
res.json({ users });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('User search error:', error);
|
|
||||||
res.status(500).json({ error: 'Search failed' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get all friends
|
// Get all friends
|
||||||
router.get('/', authMiddleware, async (req, res) => {
|
router.get('/', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
// Get accepted friendships (bidirectional)
|
// Get accepted friendships (bidirectional)
|
||||||
const friends = await query(
|
const friends = await query(
|
||||||
`SELECT DISTINCT
|
`SELECT DISTINCT
|
||||||
@@ -74,23 +69,18 @@ router.get('/', authMiddleware, async (req, res) => {
|
|||||||
friends,
|
friends,
|
||||||
challenge_friends: challengeFriends
|
challenge_friends: challengeFriends
|
||||||
});
|
});
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Get friends error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to fetch friends' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Send friend request
|
// Send friend request
|
||||||
router.post('/request', authMiddleware, async (req, res) => {
|
router.post('/request', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const { user_id } = req.body;
|
const { user_id } = req.body;
|
||||||
|
|
||||||
if (!user_id) {
|
if (!user_id) {
|
||||||
return res.status(400).json({ error: 'User ID required' });
|
throw new AppError('User ID required', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_id === req.user.userId) {
|
if (user_id === req.user.userId) {
|
||||||
return res.status(400).json({ error: 'Cannot add yourself as friend' });
|
throw new AppError('Cannot add yourself as friend', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if already friends or request exists
|
// Check if already friends or request exists
|
||||||
@@ -101,7 +91,7 @@ router.post('/request', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (existing.length > 0) {
|
if (existing.length > 0) {
|
||||||
return res.status(400).json({ error: 'Friend request already exists or you are already friends' });
|
throw new AppError('Friend request already exists or you are already friends', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
await query(
|
await query(
|
||||||
@@ -110,19 +100,14 @@ router.post('/request', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Friend request error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to send friend request' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Accept/reject friend request
|
// Accept/reject friend request
|
||||||
router.post('/respond', authMiddleware, async (req, res) => {
|
router.post('/respond', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const { friendship_id, status } = req.body;
|
const { friendship_id, status } = req.body;
|
||||||
|
|
||||||
if (!['accepted', 'rejected'].includes(status)) {
|
if (!['accepted', 'rejected'].includes(status)) {
|
||||||
return res.status(400).json({ error: 'Invalid status' });
|
throw new AppError('Invalid status', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the request is for the current user
|
// Verify the request is for the current user
|
||||||
@@ -132,7 +117,7 @@ router.post('/respond', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (friendships.length === 0) {
|
if (friendships.length === 0) {
|
||||||
return res.status(404).json({ error: 'Friend request not found' });
|
throw new AppError('Friend request not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
await query(
|
await query(
|
||||||
@@ -141,15 +126,10 @@ router.post('/respond', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
res.json({ status });
|
res.json({ status });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Respond to friend request error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to respond to friend request' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get pending friend requests
|
// Get pending friend requests
|
||||||
router.get('/requests', authMiddleware, async (req, res) => {
|
router.get('/requests', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const requests = await query(
|
const requests = await query(
|
||||||
`SELECT f.id, f.created_at, u.id as user_id, u.username, u.email
|
`SELECT f.id, f.created_at, u.id as user_id, u.username, u.email
|
||||||
FROM friendships f
|
FROM friendships f
|
||||||
@@ -160,10 +140,6 @@ router.get('/requests', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
res.json({ requests });
|
res.json({ requests });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Get friend requests error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to fetch friend requests' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { query } from '../db/index.js';
|
import { query } from '../db/index.js';
|
||||||
import { authMiddleware } from '../middleware/auth.js';
|
import { authMiddleware } from '../middleware/auth.js';
|
||||||
|
import { asyncHandler, AppError } from '../middleware/errorHandler.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// Get leaderboard for a specific challenge
|
// Get leaderboard for a specific challenge
|
||||||
router.get('/challenge/:challengeId', authMiddleware, async (req, res) => {
|
router.get('/challenge/:challengeId', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const { challengeId } = req.params;
|
const { challengeId } = req.params;
|
||||||
|
|
||||||
// Verify access
|
// Verify access
|
||||||
@@ -18,7 +18,7 @@ router.get('/challenge/:challengeId', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (access.length === 0) {
|
if (access.length === 0) {
|
||||||
return res.status(403).json({ error: 'Access denied' });
|
throw new AppError('Access denied', 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get leaderboard
|
// Get leaderboard
|
||||||
@@ -54,15 +54,10 @@ router.get('/challenge/:challengeId', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
res.json({ leaderboard });
|
res.json({ leaderboard });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Challenge leaderboard error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to fetch leaderboard' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get global leaderboard (all users)
|
// Get global leaderboard (all users)
|
||||||
router.get('/global', authMiddleware, async (req, res) => {
|
router.get('/global', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const leaderboard = await query(
|
const leaderboard = await query(
|
||||||
`SELECT
|
`SELECT
|
||||||
u.id,
|
u.id,
|
||||||
@@ -79,15 +74,10 @@ router.get('/global', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
res.json({ leaderboard });
|
res.json({ leaderboard });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Global leaderboard error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to fetch leaderboard' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get user profile stats
|
// Get user profile stats
|
||||||
router.get('/profile/:userId?', authMiddleware, async (req, res) => {
|
router.get('/profile/:userId?', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const userId = req.params.userId || req.user.userId;
|
const userId = req.params.userId || req.user.userId;
|
||||||
|
|
||||||
const stats = await query(
|
const stats = await query(
|
||||||
@@ -111,14 +101,10 @@ router.get('/profile/:userId?', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (stats.length === 0) {
|
if (stats.length === 0) {
|
||||||
return res.status(404).json({ error: 'User not found' });
|
throw new AppError('User not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({ profile: stats[0] });
|
res.json({ profile: stats[0] });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Profile stats error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to fetch profile' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { query } from '../db/index.js';
|
import { query } from '../db/index.js';
|
||||||
import { authMiddleware } from '../middleware/auth.js';
|
import { authMiddleware } from '../middleware/auth.js';
|
||||||
|
import { asyncHandler, AppError } from '../middleware/errorHandler.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// Get all predictions for a challenge
|
// Get all predictions for a challenge
|
||||||
router.get('/challenge/:challengeId', authMiddleware, async (req, res) => {
|
router.get('/challenge/:challengeId', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const { challengeId } = req.params;
|
const { challengeId } = req.params;
|
||||||
|
|
||||||
// Verify access
|
// Verify access
|
||||||
@@ -18,7 +18,7 @@ router.get('/challenge/:challengeId', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (access.length === 0) {
|
if (access.length === 0) {
|
||||||
return res.status(403).json({ error: 'Access denied' });
|
throw new AppError('Access denied', 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get predictions
|
// Get predictions
|
||||||
@@ -36,19 +36,14 @@ router.get('/challenge/:challengeId', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
res.json({ predictions });
|
res.json({ predictions });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Get predictions error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to fetch predictions' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a new prediction
|
// Create a new prediction
|
||||||
router.post('/', authMiddleware, async (req, res) => {
|
router.post('/', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const { challenge_id, content } = req.body;
|
const { challenge_id, content } = req.body;
|
||||||
|
|
||||||
if (!challenge_id || !content || !content.trim()) {
|
if (!challenge_id || !content || !content.trim()) {
|
||||||
return res.status(400).json({ error: 'Challenge ID and content are required' });
|
throw new AppError('Challenge ID and content are required', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify access
|
// Verify access
|
||||||
@@ -60,7 +55,7 @@ router.post('/', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (access.length === 0) {
|
if (access.length === 0) {
|
||||||
return res.status(403).json({ error: 'Access denied' });
|
throw new AppError('Access denied', 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await query(
|
const result = await query(
|
||||||
@@ -79,20 +74,15 @@ router.post('/', authMiddleware, async (req, res) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
res.json({ prediction });
|
res.json({ prediction });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Create prediction error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to create prediction' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Validate/invalidate a prediction (approve someone else's)
|
// Validate/invalidate a prediction (approve someone else's)
|
||||||
router.post('/:id/validate', authMiddleware, async (req, res) => {
|
router.post('/:id/validate', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const predictionId = req.params.id;
|
const predictionId = req.params.id;
|
||||||
const { status } = req.body; // 'validated' or 'invalidated'
|
const { status } = req.body; // 'validated' or 'invalidated'
|
||||||
|
|
||||||
if (!['validated', 'invalidated'].includes(status)) {
|
if (!['validated', 'invalidated'].includes(status)) {
|
||||||
return res.status(400).json({ error: 'Invalid status' });
|
throw new AppError('Invalid status', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the prediction
|
// Get the prediction
|
||||||
@@ -102,14 +92,14 @@ router.post('/:id/validate', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (predictions.length === 0) {
|
if (predictions.length === 0) {
|
||||||
return res.status(404).json({ error: 'Prediction not found' });
|
throw new AppError('Prediction not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
const prediction = predictions[0];
|
const prediction = predictions[0];
|
||||||
|
|
||||||
// Cannot validate own prediction
|
// Cannot validate own prediction
|
||||||
if (prediction.user_id === req.user.userId) {
|
if (prediction.user_id === req.user.userId) {
|
||||||
return res.status(403).json({ error: 'Cannot validate your own prediction' });
|
throw new AppError('Cannot validate your own prediction', 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify access to the challenge
|
// Verify access to the challenge
|
||||||
@@ -121,7 +111,7 @@ router.post('/:id/validate', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (access.length === 0) {
|
if (access.length === 0) {
|
||||||
return res.status(403).json({ error: 'Access denied' });
|
throw new AppError('Access denied', 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update prediction
|
// Update prediction
|
||||||
@@ -131,10 +121,6 @@ router.post('/:id/validate', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
res.json({ status });
|
res.json({ status });
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('Validate prediction error:', error);
|
|
||||||
res.status(500).json({ error: 'Failed to validate prediction' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { query } from '../db/index.js';
|
import { query } from '../db/index.js';
|
||||||
import { authMiddleware } from '../middleware/auth.js';
|
import { authMiddleware } from '../middleware/auth.js';
|
||||||
|
import { asyncHandler, AppError } from '../middleware/errorHandler.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// Search for shows/movies via TMDB with caching
|
// Search for shows/movies via TMDB with caching
|
||||||
router.get('/search', authMiddleware, async (req, res) => {
|
router.get('/search', authMiddleware, asyncHandler(async (req, res) => {
|
||||||
try {
|
|
||||||
const { q } = req.query;
|
const { q } = req.query;
|
||||||
|
|
||||||
if (!q || q.trim().length < 2) {
|
if (!q || q.trim().length < 2) {
|
||||||
@@ -28,7 +28,7 @@ router.get('/search', authMiddleware, async (req, res) => {
|
|||||||
// Fetch from TMDB
|
// Fetch from TMDB
|
||||||
const apiKey = process.env.TMDB_API_KEY;
|
const apiKey = process.env.TMDB_API_KEY;
|
||||||
if (!apiKey) {
|
if (!apiKey) {
|
||||||
return res.status(500).json({ error: 'TMDB API key not configured' });
|
throw new AppError('TMDB API key not configured', 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetch = (await import('node-fetch')).default;
|
const fetch = (await import('node-fetch')).default;
|
||||||
@@ -39,10 +39,7 @@ router.get('/search', authMiddleware, async (req, res) => {
|
|||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorText = await response.text();
|
const errorText = await response.text();
|
||||||
console.error('TMDB API error:', response.status, errorText);
|
console.error('TMDB API error:', response.status, errorText);
|
||||||
return res.status(500).json({
|
throw new AppError(`TMDB API error: ${response.status}`, 500);
|
||||||
error: `TMDB API error: ${response.status}`,
|
|
||||||
details: errorText
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@@ -70,10 +67,6 @@ router.get('/search', authMiddleware, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (error) {
|
}));
|
||||||
console.error('TMDB search error:', error);
|
|
||||||
res.status(500).json({ error: 'Search failed' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
Reference in New Issue
Block a user