aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--backend/prisma/seed.ts4
-rw-r--r--backend/src/config/db.ts17
-rw-r--r--backend/src/index.ts242
-rw-r--r--backend/src/middleware/requireAuth.ts28
-rw-r--r--backend/src/routes/authRoutes.ts116
-rw-r--r--backend/src/routes/userRoutes.ts41
-rw-r--r--backend/src/utils/session.ts35
-rw-r--r--frontend/src/pages/Import.tsx0
8 files changed, 251 insertions, 232 deletions
diff --git a/backend/prisma/seed.ts b/backend/prisma/seed.ts
index 9f98e81..d0e3c73 100644
--- a/backend/prisma/seed.ts
+++ b/backend/prisma/seed.ts
@@ -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');
diff --git a/backend/src/config/db.ts b/backend/src/config/db.ts
new file mode 100644
index 0000000..024a7ed
--- /dev/null
+++ b/backend/src/config/db.ts
@@ -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);
+});
diff --git a/backend/src/index.ts b/backend/src/index.ts
index 37b64a8..3e2c559 100644
--- a/backend/src/index.ts
+++ b/backend/src/index.ts
@@ -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' });
- }
-
- 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' });
- }
-
- // 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;
+startSessionCleanup();
- if (!username || !password) {
- return res.status(400).json({ error: 'Username and password are required' });
- }
+app.post('/api/register', authRoutes.handleRegistration);
+app.post('/api/authenticate', authRoutes.handleAuthentication);
+app.post('/api/logout', requireAuth, authRoutes.handleLogout);
- 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}`);
diff --git a/backend/src/middleware/requireAuth.ts b/backend/src/middleware/requireAuth.ts
new file mode 100644
index 0000000..915d52b
--- /dev/null
+++ b/backend/src/middleware/requireAuth.ts
@@ -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' });
+ }
+};
diff --git a/backend/src/routes/authRoutes.ts b/backend/src/routes/authRoutes.ts
new file mode 100644
index 0000000..4c6c374
--- /dev/null
+++ b/backend/src/routes/authRoutes.ts
@@ -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' });
+ }
+}
diff --git a/backend/src/routes/userRoutes.ts b/backend/src/routes/userRoutes.ts
new file mode 100644
index 0000000..a03ece0
--- /dev/null
+++ b/backend/src/routes/userRoutes.ts
@@ -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' });
+ }
+}
diff --git a/backend/src/utils/session.ts b/backend/src/utils/session.ts
new file mode 100644
index 0000000..cc1facd
--- /dev/null
+++ b/backend/src/utils/session.ts
@@ -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);
+};
diff --git a/frontend/src/pages/Import.tsx b/frontend/src/pages/Import.tsx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/frontend/src/pages/Import.tsx
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage