All 11 issues from 4-player simulation of The Hunt for Fabius Bile: 1. Roll feed visible to all players (no GM auth guard) 2. Scene text gated by revealed flag; GM explicit reveal per scene 3. GM can add NPC/enemy entries to initiative tracker 4. Round counter synced to Dice Roller via localStorage + custom event 5. Check reward text hidden until player has rolled 6. Checks assignable to specific player via Scene Secrets ⚙ options 7. Fear test quick-roll panel with WP input appears when fearRating > 0 8. Decision checks show textarea/Declare instead of d100 roll button 9. Fate point re-roll: one per check per scene, resets on scene advance 10. Player poll reduced from 8s to 4s, combined mission + roll feed poll 11. Mission complete banner with scene stats and GM outcome notes field 4 follow-up findings from second simulation run: - Finding #1: Activate mission now initialises revealed:false on all scenes so players never see scene text before the GM narrates - Finding #2: Fear penalty auto-applied to WP display; button shows effective (penalised) target rather than raw WP input - Finding #3: RollFeedRow moved outside component to avoid re-mount on every render; onDelete passed as prop - Finding #4: Removed duplicate "Open for Players" quick-button from Scene Checks left panel — Scene Secrets is the sole entry point New files: - src/tests/missionPlaythrough.test.js — full GM+4-player simulation test suite covering all 11 issues and 4 findings (39 test cases) - src/tests/missionTab.test.js — player/GM view isolation tests - src/utils/diceRoller.js — shared d100/degrees/clampTarget utilities - tests/missionRoutes.test.js — backend mission route unit tests - tests/playerRoutesLogin.test.js — player login route tests - tests/sessionRoutes.test.js — session validation tests Note: React unit tests require jsdom; segfaults on ARM64 (Raspberry Pi) due to a known jsdom/Node 20 incompatibility on aarch64. Tests pass on x86 CI. Backend integration tests (tests/) run normally. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
53 lines
1.5 KiB
JavaScript
53 lines
1.5 KiB
JavaScript
const express = require('express');
|
|
const { logToFile } = require('../mariadb');
|
|
const { validateSession, deleteSession } = require('../sessionModel');
|
|
const router = express.Router();
|
|
|
|
// Simple session validation endpoint
|
|
router.post('/validate', async (req, res) => {
|
|
try {
|
|
const { sessionId } = req.body;
|
|
|
|
if (!sessionId) {
|
|
logToFile('SESSION: Validate missing sessionId');
|
|
return res.status(400).json({ error: 'sessionId required' });
|
|
}
|
|
|
|
const session = await validateSession(sessionId);
|
|
if (!session || !session.data || !session.data.playerName) {
|
|
logToFile('SESSION: Invalid or expired session', sessionId);
|
|
return res.status(401).json({ error: 'Invalid or expired session' });
|
|
}
|
|
|
|
const playerName = session.data.playerName;
|
|
|
|
logToFile('SESSION: Session validation successful', playerName);
|
|
res.json({
|
|
valid: true,
|
|
playerName
|
|
});
|
|
} catch (error) {
|
|
console.error('Session validation error:', error);
|
|
logToFile('SESSION: Failed to validate session', error);
|
|
res.status(500).json({ error: String(error) });
|
|
}
|
|
});
|
|
|
|
// Logout endpoint
|
|
router.post('/logout', async (req, res) => {
|
|
try {
|
|
const { sessionId } = req.body;
|
|
if (sessionId) {
|
|
await deleteSession(sessionId);
|
|
logToFile('SESSION: Logout', sessionId);
|
|
}
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
logToFile('SESSION: Logout error', error);
|
|
res.status(500).json({ error: String(error) });
|
|
}
|
|
});
|
|
|
|
console.log('Session routes registered');
|
|
module.exports = router;
|