seperate routes and middleware into seperate files

This commit is contained in:
Pinapelz
2025-06-29 22:06:39 -07:00
parent 1b66788e84
commit eda5691cfc
8 changed files with 251 additions and 232 deletions
+2 -2
View File
@@ -3,9 +3,9 @@ import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
await prisma.games.createMany({
await prisma.game.createMany({
data: [
{ gameName: 'Dancerush' },
{ name: 'Dancerush' },
],
});
console.log('Initial seed data inserted');
+17
View File
@@ -0,0 +1,17 @@
import { PrismaClient } from '@prisma/client';
export const prisma = new PrismaClient();
process.on('beforeExit', async () => {
await prisma.$disconnect();
});
process.on('SIGINT', async () => {
await prisma.$disconnect();
process.exit(0);
});
process.on('SIGTERM', async () => {
await prisma.$disconnect();
process.exit(0);
});
+12 -230
View File
@@ -1,10 +1,13 @@
import express from 'express';
import cors from 'cors';
import bcrypt from 'bcrypt';
import crypto from 'crypto';
import session from 'express-session';
import cookieParser from 'cookie-parser';
import { PrismaClient } from '@prisma/client';
import { requireAuth } from './middleware/requireAuth';
import { startSessionCleanup } from './utils/session';
// Routes
import * as authRoutes from './routes/authRoutes';
import * as userRoutes from './routes/userRoutes';
const app = express();
const port = 5000;
@@ -31,241 +34,20 @@ app.use(session({
}
}));
const prisma = new PrismaClient();
declare module 'express-session' {
interface SessionData {
userId: number;
}
}
// Middleware to check if user is authenticated
const requireAuth = async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (!req.session.userId) {
return res.status(401).json({ error: 'Authentication required' });
}
startSessionCleanup();
try {
const user = await prisma.user.findUnique({
where: { id: req.session.userId },
select: { id: true, username: true, email: true }
});
app.post('/api/register', authRoutes.handleRegistration);
app.post('/api/authenticate', authRoutes.handleAuthentication);
app.post('/api/logout', requireAuth, authRoutes.handleLogout);
if (!user) {
req.session.destroy((err) => {
if (err) console.error('Session destroy error:', err);
});
return res.status(401).json({ error: 'Invalid session' });
}
// Attach user to request object
(req as any).user = user;
next();
} catch (error) {
console.error('Auth middleware error:', error);
res.status(500).json({ error: 'Internal server error' });
}
};
// Create session in database
const createSession = async (userId: number): Promise<string> => {
const sessionId = crypto.randomUUID();
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours
await prisma.session.create({
data: {
id: sessionId,
userId,
expiresAt
}
});
return sessionId;
};
// Clean up expired sessions
const cleanupExpiredSessions = async () => {
try {
await prisma.session.deleteMany({
where: {
expiresAt: {
lt: new Date()
}
}
});
} catch (error) {
console.error('Session cleanup error:', error);
}
};
// Run cleanup every hour
setInterval(cleanupExpiredSessions, 60 * 60 * 1000);
app.post('/api/register', async (req, res) => {
try {
const { username, password, email } = req.body;
if (!username || !password || !email) {
return res.status(400).json({ error: 'All fields are required' });
}
const existingUser = await prisma.user.findFirst({
where: {
OR: [
{ username },
{ email }
]
}
});
if (existingUser) {
return res.status(400).json({ error: 'Username or email already exists' });
}
const salt = crypto.randomBytes(16).toString('hex');
const hashedPassword = await bcrypt.hash(password + salt, 12);
const user = await prisma.user.create({
data: {
username,
password: hashedPassword,
salt,
email,
}
});
// Create session for the new user
req.session.userId = user.id;
const sessionId = await createSession(user.id);
res.status(201).json({
id: user.id,
username: user.username,
email: user.email,
sessionId
});
} catch (error) {
console.error('Registration error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
app.post('/api/authenticate', async (req, res) => {
try {
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).json({ error: 'Username and password are required' });
}
const user = await prisma.user.findUnique({
where: { username }
});
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const isValidPassword = await bcrypt.compare(password + user.salt, user.password);
if (!isValidPassword) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Create session
req.session.userId = user.id;
const sessionId = await createSession(user.id);
res.json({
id: user.id,
username: user.username,
email: user.email,
sessionId
});
} catch (error) {
console.error('Login error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
app.post('/api/logout', requireAuth, async (req, res) => {
try {
const userId = req.session.userId;
// Remove all sessions for this user from database
await prisma.session.deleteMany({
where: { userId }
});
// Destroy the session
req.session.destroy((err) => {
if (err) {
console.error('Session destroy error:', err);
return res.status(500).json({ error: 'Logout failed' });
}
res.clearCookie('connect.sid'); // Clear the session cookie
res.json({ message: 'Logged out successfully' });
});
} catch (error) {
console.error('Logout error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
app.get('/api/me', requireAuth, async (req, res) => {
try {
const user = (req as any).user;
res.json(user);
} catch (error) {
console.error('Me endpoint error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
app.get('/api/users', async (req, res) => {
try {
const users = await prisma.user.findMany({
select: {
id: true,
username: true,
}
});
res.json(users);
} catch (error) {
console.error('Users endpoint error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Check session status
app.get('/api/session', async (req, res) => {
try {
if (!req.session.userId) {
return res.json({ authenticated: false });
}
const user = await prisma.user.findUnique({
where: { id: req.session.userId },
select: { id: true, username: true, email: true }
});
if (!user) {
req.session.destroy((err) => {
if (err) console.error('Session destroy error:', err);
});
return res.json({ authenticated: false });
}
res.json({
authenticated: true,
user
});
} catch (error) {
console.error('Session check error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
app.get('/api/me', userRoutes.handleMeRoute);
app.get('/api/session', userRoutes.handleGetCurrentSession);
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
+28
View File
@@ -0,0 +1,28 @@
import express from 'express';
import { prisma } from '../config/db';
export const requireAuth = async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (!req.session.userId) {
return res.status(401).json({ error: 'Authentication required' });
}
try {
const user = await prisma.user.findUnique({
where: { id: req.session.userId },
select: { id: true, username: true, email: true }
});
if (!user) {
req.session.destroy((err) => {
if (err) console.error('Session destroy error:', err);
});
return res.status(401).json({ error: 'Invalid session' });
}
(req as any).user = user;
next();
} catch (error) {
console.error('Auth middleware error:', error);
res.status(500).json({ error: 'Internal server error' });
}
};
+116
View File
@@ -0,0 +1,116 @@
import { prisma } from '../config/db';
import express from 'express';
import { createSession } from '../utils/session'
import bcrypt from 'bcrypt';
import crypto from 'crypto';
export const handleRegistration = async (req: express.Request, res: express.Response) => {
try {
const { username, password, email } = req.body;
if (!username || !password || !email) {
return res.status(400).json({ error: 'All fields are required' });
}
const existingUser = await prisma.user.findFirst({
where: {
OR: [
{ username },
{ email }
]
}
});
if (existingUser) {
return res.status(400).json({ error: 'Username or email already exists' });
}
const salt = crypto.randomBytes(16).toString('hex');
const hashedPassword = await bcrypt.hash(password + salt, 12);
const user = await prisma.user.create({
data: {
username,
password: hashedPassword,
salt,
email,
}
});
// Create session for the new user
req.session.userId = user.id;
const sessionId = await createSession(user.id);
res.status(201).json({
id: user.id,
username: user.username,
email: user.email,
sessionId
});
} catch (error) {
console.error('Registration error:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
export const handleAuthentication = async (req: express.Request, res: express.Response) => {
try {
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).json({ error: 'Username and password are required' });
}
const user = await prisma.user.findUnique({
where: { username }
});
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const isValidPassword = await bcrypt.compare(password + user.salt, user.password);
if (!isValidPassword) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Create session
req.session.userId = user.id;
const sessionId = await createSession(user.id);
res.json({
id: user.id,
username: user.username,
email: user.email,
sessionId
});
} catch (error) {
console.error('Login error:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
export const handleLogout = async (req: express.Request, res: express.Response,) => {
try {
const userId = req.session.userId;
// Remove all sessions for this user from database
await prisma.session.deleteMany({
where: { userId }
});
// Destroy the session
req.session.destroy((err) => {
if (err) {
console.error('Session destroy error:', err);
return res.status(500).json({ error: 'Logout failed' });
}
res.clearCookie('connect.sid'); // Clear the session cookie
res.json({ message: 'Logged out successfully' });
});
} catch (error) {
console.error('Logout error:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
+41
View File
@@ -0,0 +1,41 @@
// Routes about self (or users in general)
import express from 'express';
import { prisma } from '../config/db';
export const handleMeRoute = async (req: express.Request, res: express.Response) => {
try {
const user = (req as any).user;
res.json(user);
} catch (error) {
console.error('Me endpoint error:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
export const handleGetCurrentSession = async (req: express.Request, res: express.Response) => {
try {
if (!req.session.userId) {
return res.json({ authenticated: false });
}
const user = await prisma.user.findUnique({
where: { id: req.session.userId },
select: { id: true, username: true, email: true }
});
if (!user) {
req.session.destroy((err) => {
if (err) console.error('Session destroy error:', err);
});
return res.json({ authenticated: false });
}
res.json({
authenticated: true,
user
});
} catch (error) {
console.error('Session check error:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
+35
View File
@@ -0,0 +1,35 @@
import crypto from 'crypto';
import { prisma } from '../config/db';
export const createSession = async (userId: number): Promise<string> => {
const sessionId = crypto.randomUUID();
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours
await prisma.session.create({
data: {
id: sessionId,
userId,
expiresAt
}
});
return sessionId;
};
export const cleanupExpiredSessions = async () => {
try {
await prisma.session.deleteMany({
where: {
expiresAt: {
lt: new Date()
}
}
});
} catch (error) {
console.error('Session cleanup error:', error);
}
};
export const startSessionCleanup = () => {
setInterval(cleanupExpiredSessions, 60 * 60 * 1000);
};
View File