Models & Routes
This commit is contained in:
30
routes/auth.js
Normal file
30
routes/auth.js
Normal file
@ -0,0 +1,30 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const bcrypt = require('bcryptjs');
|
||||
const User = require('../models/User');
|
||||
|
||||
router.post('/register', async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
const hash = await bcrypt.hash(password, 10);
|
||||
try {
|
||||
const newUser = new User({ username, password: hash });
|
||||
await newUser.save();
|
||||
res.status(201).json({ success: true, message: 'User created' });
|
||||
} catch (err) {
|
||||
res.status(400).json({ success: false, message: 'User exists or error' });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
router.post('/login', async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
const user = await User.findOne({ username });
|
||||
if (!user) return res.status(400).json({ success: false, message: 'Invalid credentials' });
|
||||
|
||||
const isMatch = await bcrypt.compare(password, user.password);
|
||||
if (!isMatch) return res.status(400).json({ success: false, message: 'Invalid credentials' });
|
||||
|
||||
res.json({ success: true, message: 'Login successful' });
|
||||
});
|
||||
|
||||
module.exports = router;
|
22
routes/game.js
Normal file
22
routes/game.js
Normal file
@ -0,0 +1,22 @@
|
||||
// routes/game.js
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const GameSession = require('../models/GameSession');
|
||||
|
||||
router.post('/save-session', async (req, res) => {
|
||||
const { username, levelNumber, durationSeconds } = req.body;
|
||||
|
||||
if (!username || !levelNumber || !durationSeconds) {
|
||||
return res.status(400).json({ success: false, message: 'Missing data' });
|
||||
}
|
||||
|
||||
try {
|
||||
const session = new GameSession({ username, levelNumber, durationSeconds });
|
||||
await session.save();
|
||||
res.json({ success: true, message: 'Session saved' });
|
||||
} catch (err) {
|
||||
res.status(500).json({ success: false, message: 'Server error', error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
180
routes/reports.js
Normal file
180
routes/reports.js
Normal file
@ -0,0 +1,180 @@
|
||||
// routes/reports.js
|
||||
const express = require('express');
|
||||
require('dotenv').config();
|
||||
|
||||
const router = express.Router();
|
||||
const nodemailer = require('nodemailer');
|
||||
const GameSession = require('../models/GameSession');
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
service: 'gmail',
|
||||
auth: {
|
||||
user: process.env.EMAIL_USER,
|
||||
pass: process.env.EMAIL_PASS
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/generate-report', async (req, res) => {
|
||||
const { username, email } = req.body;
|
||||
|
||||
if (!username || !email) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Username and email are required'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Fetch all sessions for the user
|
||||
const sessions = await GameSession.find({ username }).sort({ createdAt: -1 });
|
||||
|
||||
if (sessions.length === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'No session data found for this user'
|
||||
});
|
||||
}
|
||||
|
||||
// Generate report content
|
||||
const report = generateReportContent(username, sessions);
|
||||
|
||||
// Email configuration
|
||||
const mailOptions = {
|
||||
from: 'shiharahimalshi@gmail.com',
|
||||
to: email,
|
||||
subject: `Game Session Report for ${username}`,
|
||||
html: report
|
||||
};
|
||||
|
||||
// Send email
|
||||
await transporter.sendMail(mailOptions);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Report sent successfully to ' + email
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error('Report generation error:', err);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to generate report',
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function generateReportContent(username, sessions) {
|
||||
// Calculate statistics
|
||||
const totalSessions = sessions.length;
|
||||
const totalPlayTime = sessions.reduce((sum, session) => sum + session.durationSeconds, 0);
|
||||
const averagePlayTime = Math.round(totalPlayTime / totalSessions);
|
||||
const levelsPlayed = [...new Set(sessions.map(s => s.levelNumber))].sort((a, b) => a - b);
|
||||
|
||||
// Format play time
|
||||
const formatTime = (seconds) => {
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
const secs = seconds % 60;
|
||||
return `${hours}h ${minutes}m ${secs}s`;
|
||||
};
|
||||
|
||||
// Generate professional report-style HTML
|
||||
let html = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', Arial, sans-serif; margin: 30px; background: #fff; color: #000; line-height: 1.6; }
|
||||
.header { text-align: center; margin-bottom: 30px; }
|
||||
.header h1 { margin: 0; font-size: 28px; font-weight: bold; }
|
||||
.header h2 { margin: 5px 0; font-size: 20px; font-weight: normal; }
|
||||
.header p { margin: 0; font-size: 14px; color: #555; }
|
||||
.section { margin-bottom: 30px; }
|
||||
.section h3 { border-bottom: 2px solid #333; padding-bottom: 5px; margin-bottom: 15px; font-size: 18px; }
|
||||
.stat-item { margin: 6px 0; font-size: 15px; }
|
||||
.level-container { display: flex; flex-wrap: wrap; gap: 20px; }
|
||||
.level-card { flex: 1 1 200px; border: 1px solid #ddd; padding: 12px; border-radius: 6px; background: #f9f9f9; }
|
||||
table { width: 100%; border-collapse: collapse; margin-top: 15px; font-size: 14px; }
|
||||
th, td { border: 1px solid #ddd; padding: 10px; text-align: center; }
|
||||
th { background: #f2f2f2; font-weight: bold; }
|
||||
tr:nth-child(even) { background: #fafafa; }
|
||||
.footer { text-align: center; margin-top: 40px; font-size: 13px; color: #555; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Game Session Report</h1>
|
||||
<h2>Player: ${username}</h2>
|
||||
<p>Generated on: ${new Date().toLocaleDateString()}</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Overall Statistics</h3>
|
||||
<div class="stat-item"><strong>Total Sessions:</strong> ${totalSessions}</div>
|
||||
<div class="stat-item"><strong>Total Play Time:</strong> ${formatTime(totalPlayTime)}</div>
|
||||
<div class="stat-item"><strong>Average Session Time:</strong> ${formatTime(averagePlayTime)}</div>
|
||||
<div class="stat-item"><strong>Levels Played:</strong> ${levelsPlayed.join(', ')}</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Level Statistics</h3>
|
||||
<div class="level-container">`;
|
||||
|
||||
// Level-specific stats
|
||||
levelsPlayed.forEach(level => {
|
||||
const levelSessions = sessions.filter(s => s.levelNumber === level);
|
||||
const levelTotalTime = levelSessions.reduce((sum, s) => sum + s.durationSeconds, 0);
|
||||
const levelAvgTime = Math.round(levelTotalTime / levelSessions.length);
|
||||
|
||||
html += `
|
||||
<div class="level-card">
|
||||
<strong>Level ${level}</strong><br>
|
||||
Sessions: ${levelSessions.length}<br>
|
||||
Total Time: ${formatTime(levelTotalTime)}<br>
|
||||
Average Time: ${formatTime(levelAvgTime)}
|
||||
</div>`;
|
||||
});
|
||||
|
||||
html += `
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Detailed Session History</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Level</th>
|
||||
<th>Duration</th>
|
||||
<th>Performance</th>
|
||||
</tr>`;
|
||||
|
||||
sessions.forEach(session => {
|
||||
const date = session.createdAt ? new Date(session.createdAt).toLocaleDateString() : 'N/A';
|
||||
const performance = session.durationSeconds < averagePlayTime ? 'Above Average' : 'Average';
|
||||
|
||||
html += `
|
||||
<tr>
|
||||
<td>${date}</td>
|
||||
<td>Level ${session.levelNumber}</td>
|
||||
<td>${formatTime(session.durationSeconds)}</td>
|
||||
<td>${performance}</td>
|
||||
</tr>`;
|
||||
});
|
||||
|
||||
html += `
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>This report was generated automatically by the game system.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
module.exports = router;
|
Reference in New Issue
Block a user