diff --git a/GameSession.js b/models/GameSession.js similarity index 97% rename from GameSession.js rename to models/GameSession.js index f54f512..67a224c 100644 --- a/GameSession.js +++ b/models/GameSession.js @@ -1,10 +1,10 @@ -const mongoose = require('mongoose'); - -const gameSessionSchema = new mongoose.Schema({ - username: { type: String, required: true }, // can be ObjectId if you prefer - levelNumber: { type: Number, required: true }, - durationSeconds: { type: Number, required: true }, - playedAt: { type: Date, default: Date.now } -}); - -module.exports = mongoose.model('GameSession', gameSessionSchema); +const mongoose = require('mongoose'); + +const gameSessionSchema = new mongoose.Schema({ + username: { type: String, required: true }, // can be ObjectId if you prefer + levelNumber: { type: Number, required: true }, + durationSeconds: { type: Number, required: true }, + playedAt: { type: Date, default: Date.now } +}); + +module.exports = mongoose.model('GameSession', gameSessionSchema); diff --git a/User.js b/models/User.js similarity index 100% rename from User.js rename to models/User.js diff --git a/routes/auth.js b/routes/auth.js new file mode 100644 index 0000000..d8addf4 --- /dev/null +++ b/routes/auth.js @@ -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; diff --git a/routes/game.js b/routes/game.js new file mode 100644 index 0000000..de2f3b3 --- /dev/null +++ b/routes/game.js @@ -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; diff --git a/routes/reports.js b/routes/reports.js new file mode 100644 index 0000000..d799c9c --- /dev/null +++ b/routes/reports.js @@ -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 = ` + + + + + + +
+

Game Session Report

+

Player: ${username}

+

Generated on: ${new Date().toLocaleDateString()}

+
+ +
+

Overall Statistics

+
Total Sessions: ${totalSessions}
+
Total Play Time: ${formatTime(totalPlayTime)}
+
Average Session Time: ${formatTime(averagePlayTime)}
+
Levels Played: ${levelsPlayed.join(', ')}
+
+ +
+

Level Statistics

+
`; + + // 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 += ` +
+ Level ${level}
+ Sessions: ${levelSessions.length}
+ Total Time: ${formatTime(levelTotalTime)}
+ Average Time: ${formatTime(levelAvgTime)} +
`; + }); + + html += ` +
+
+ +
+

Detailed Session History

+ + + + + + + `; + + sessions.forEach(session => { + const date = session.createdAt ? new Date(session.createdAt).toLocaleDateString() : 'N/A'; + const performance = session.durationSeconds < averagePlayTime ? 'Above Average' : 'Average'; + + html += ` + + + + + + `; + }); + + html += ` +
DateLevelDurationPerformance
${date}Level ${session.levelNumber}${formatTime(session.durationSeconds)}${performance}
+
+ + + + `; + + return html; +} + +module.exports = router; \ No newline at end of file