mySQL
This commit is contained in:
25
server/app.js
Normal file
25
server/app.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const express = require('express');
|
||||
const session = require('express-session');
|
||||
const app = express();
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
// Ensure that SESSION_SECRET is defined
|
||||
if (!process.env.SESSIONS_SECRET) {
|
||||
console.error('FATAL ERROR: SESSIONS_SECRET is not defined.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Middleware
|
||||
app.use(express.json());
|
||||
app.use(
|
||||
session({
|
||||
secret: process.env.SESSIONS_SECRET,
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
})
|
||||
);
|
||||
|
||||
module.exports = app;
|
||||
151
server/authRoutes.js
Normal file
151
server/authRoutes.js
Normal file
@@ -0,0 +1,151 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const passport = require('passport');
|
||||
const GoogleStrategy = require('passport-google-oauth20').Strategy;
|
||||
const jwt = require('jsonwebtoken');
|
||||
const db = require('./db');
|
||||
const { generateUniqueId } = require('../shared/utils/crypto');
|
||||
|
||||
// Check user session (GET)
|
||||
router.get('/api/auth/session', (req, res) => {
|
||||
const token = req.cookies.token;
|
||||
|
||||
if (!token) {
|
||||
return res.json({ userId: null });
|
||||
}
|
||||
|
||||
try {
|
||||
// Verify the token
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
res.json({ userId: decoded.userId });
|
||||
} catch (error) {
|
||||
console.error('Invalid token:', error);
|
||||
res.status(401).json({ userId: null, error: 'Invalid token' });
|
||||
}
|
||||
});
|
||||
|
||||
// Sign in anonymously (POST)
|
||||
router.post('/api/auth/signin-anonymous', async (req, res) => {
|
||||
const userId = generateUniqueId(); // Generate unique ID for anonymous user
|
||||
|
||||
try {
|
||||
// Insert the anonymous user into the database (no sensitive data, just ID)
|
||||
await db.query('INSERT INTO users (id, provider) VALUES (?, ?)', [
|
||||
userId,
|
||||
'anonymous',
|
||||
]);
|
||||
|
||||
// Generate JWT token for the anonymous user
|
||||
const token = jwt.sign(
|
||||
{ userId, provider: 'anonymous' },
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: '1h' }
|
||||
);
|
||||
|
||||
// Set the JWT in an httpOnly cookie for better security
|
||||
res.cookie('token', token, { httpOnly: true });
|
||||
|
||||
res.status(200).json({ message: 'Anonymous user signed in' });
|
||||
} catch (error) {
|
||||
console.error('Error creating anonymous user:', error);
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/api/auth/refresh-token', (req, res) => {
|
||||
const refreshToken = req.cookies.refreshToken;
|
||||
if (!refreshToken) return res.status(403).send('Refresh token required');
|
||||
|
||||
// Verify the refresh token and issue a new JWT
|
||||
try {
|
||||
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
|
||||
const newToken = jwt.sign(
|
||||
{ userId: decoded.userId },
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: '1h' }
|
||||
);
|
||||
res.cookie('token', newToken, { httpOnly: true });
|
||||
res.json({ message: 'Token refreshed' });
|
||||
} catch (error) {
|
||||
res.status(403).send('Invalid refresh token');
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize Passport
|
||||
router.use(passport.initialize());
|
||||
|
||||
// Google OAuth Strategy
|
||||
passport.use(
|
||||
new GoogleStrategy(
|
||||
{
|
||||
clientID: process.env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
callbackURL: '/api/auth/google/callback',
|
||||
},
|
||||
async (accessToken, refreshToken, profile, done) => {
|
||||
const googleId = profile.id; // This is the unique ID from Google
|
||||
|
||||
try {
|
||||
// Insert the Google user into the database if they don't exist
|
||||
await db.query(
|
||||
'INSERT INTO users (id, provider) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = id',
|
||||
[googleId, 'google']
|
||||
);
|
||||
|
||||
// Generate JWT token for the Google user
|
||||
const token = jwt.sign(
|
||||
{ userId: googleId, provider: 'google' },
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: '1h' }
|
||||
);
|
||||
|
||||
// Pass the token back
|
||||
return done(null, token);
|
||||
} catch (error) {
|
||||
return done(error);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Start Google OAuth login
|
||||
router.get(
|
||||
'/api/auth/google',
|
||||
passport.authenticate('google', { scope: ['profile', 'email'] })
|
||||
);
|
||||
|
||||
// Callback route for Google OAuth
|
||||
router.get(
|
||||
'/api/auth/google/callback',
|
||||
passport.authenticate('google', { failureRedirect: '/' }),
|
||||
(req, res) => {
|
||||
// After successful authentication, generate the JWT (which is in req.user)
|
||||
const token = req.user; // Passport's Google strategy should return the JWT
|
||||
|
||||
// Set the JWT in an httpOnly cookie
|
||||
res.cookie('token', token, { httpOnly: true });
|
||||
|
||||
// Redirect to the frontend dashboard or a secure page without the token in the URL
|
||||
res.redirect('/dashboard');
|
||||
}
|
||||
);
|
||||
|
||||
// Example of a protected route (no need for query token)
|
||||
router.get('/dashboard', (req, res) => {
|
||||
// Extract token from cookies (JWT is automatically sent as a cookie)
|
||||
const token = req.cookies.token;
|
||||
|
||||
if (!token) {
|
||||
return res.status(403).send('Token is required');
|
||||
}
|
||||
|
||||
try {
|
||||
// Verify the token and decode it
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
res.send(`Welcome, user with ID: ${decoded.userId}`);
|
||||
} catch (err) {
|
||||
res.status(403).send('Invalid token');
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
15
server/db.js
Normal file
15
server/db.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
// Create a MySQL connection pool with promises
|
||||
const db = mysql.createPool({
|
||||
host: process.env.MYSQL_HOST,
|
||||
user: process.env.MYSQL_USER,
|
||||
password: process.env.MYSQL_PASSWORD,
|
||||
database: process.env.MYSQL_DATABASE,
|
||||
});
|
||||
|
||||
module.exports = db;
|
||||
19
server/package.json
Normal file
19
server/package.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "fibonacci-fold-server",
|
||||
"version": "1.0.0",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"crypto-js": "^4.2.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"express-session": "^1.18.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mysql2": "^2.3.3",
|
||||
"passport": "^0.7.0",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"uuid": "^10.0.0"
|
||||
}
|
||||
}
|
||||
113
server/server.js
Normal file
113
server/server.js
Normal file
@@ -0,0 +1,113 @@
|
||||
const authRoutes = require('./authRoutes');
|
||||
const app = require('./app');
|
||||
const db = require('./db');
|
||||
|
||||
// Routes
|
||||
app.use(authRoutes); // Use the auth routes
|
||||
|
||||
// Create new session
|
||||
app.post('/api/session', async (req, res) => {
|
||||
const sessionId = req.body.sessionId;
|
||||
const query = 'INSERT INTO sessions (id) VALUES (?)';
|
||||
await db.query(query, [sessionId], (err, result) => {
|
||||
if (err) return res.status(500).json({ error: err });
|
||||
res.status(200).json({ sessionId });
|
||||
});
|
||||
});
|
||||
|
||||
// Add user to session
|
||||
app.post('/api/session/:sessionId/user', async (req, res) => {
|
||||
const { sessionId } = req.params;
|
||||
const { userId, provider } = req.body;
|
||||
|
||||
// Insert the user into the `users` table if they don't already exist
|
||||
const userQuery = `
|
||||
INSERT INTO users (id, provider)
|
||||
VALUES (?, ?)
|
||||
ON DUPLICATE KEY UPDATE id = id`; // If the user already exists, do nothing
|
||||
|
||||
// Insert the user into the `session_users` table for this session
|
||||
const sessionUserQuery = `
|
||||
INSERT INTO session_users (session_id, user_id, provider)
|
||||
VALUES (?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE session_id = session_id`; // If the user is already in the session, do nothing
|
||||
|
||||
// Insert the initial vote (-1) for the user in this session
|
||||
const voteQuery = `
|
||||
INSERT INTO votes (session_id, user_id, vote)
|
||||
VALUES (?, ?, -1)
|
||||
ON DUPLICATE KEY UPDATE vote = -1`; // If a vote already exists, reset it to -1
|
||||
|
||||
try {
|
||||
// Insert the user into the `users` table
|
||||
await db.query(userQuery, [userId, provider]);
|
||||
|
||||
// Add the user to the `session_users` table
|
||||
await db.query(sessionUserQuery, [sessionId, userId, provider]);
|
||||
|
||||
// Set the initial vote for the user in the session
|
||||
await db.query(voteQuery, [sessionId, userId]);
|
||||
|
||||
res.status(200).json({ userId });
|
||||
} catch (err) {
|
||||
console.error('Error adding user to session:', err);
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get users in a session
|
||||
app.get('/api/session/:sessionId/users', async (req, res) => {
|
||||
const { sessionId } = req.params;
|
||||
const query = `
|
||||
SELECT users.id, users.username
|
||||
FROM votes
|
||||
JOIN users ON votes.user_id = users.id
|
||||
WHERE session_id = ?
|
||||
`;
|
||||
|
||||
try {
|
||||
const [results] = await db.query(query, [sessionId]);
|
||||
console.log('Query results:', results); // Log query result
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
console.error('Error fetching session users:', err);
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/api/session/:sessionId/votes/:userId', async (req, res) => {
|
||||
const { sessionId, userId } = req.params;
|
||||
const query = 'DELETE FROM votes WHERE session_id = ? AND user_id = ?';
|
||||
|
||||
try {
|
||||
const [result] = await db.query(query, [sessionId, userId]);
|
||||
res
|
||||
.status(200)
|
||||
.json({ success: true, message: 'Vote removed successfully' });
|
||||
} catch (error) {
|
||||
console.error('Error removing vote:', error);
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/api/session/:sessionId/users/:userId', async (req, res) => {
|
||||
const { sessionId, userId } = req.params;
|
||||
const query =
|
||||
'DELETE FROM session_users WHERE session_id = ? AND user_id = ?';
|
||||
|
||||
try {
|
||||
const [result] = await db.query(query, [sessionId, userId]);
|
||||
res
|
||||
.status(200)
|
||||
.json({ success: true, message: 'User removed successfully' });
|
||||
} catch (error) {
|
||||
console.error('Error removing user:', error);
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Start the server
|
||||
const PORT = process.env.PORT || 5000;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server is running on port ${PORT}`);
|
||||
});
|
||||
1267
server/stderr.log
Normal file
1267
server/stderr.log
Normal file
File diff suppressed because it is too large
Load Diff
0
server/tmp/restart.txt
Normal file
0
server/tmp/restart.txt
Normal file
Reference in New Issue
Block a user