- Updated package.json scripts to include separate unit and integration tests before building. - Added a new script `test-build-test` to run tests before and after the build process. - Introduced a new local CI/CD script for streamlined local development and testing. - Refactored PlayerTab component to normalize skills data structure and added tooltips for Space Marine and Power Armour abilities. - Added named export for Tooltip in DeathwatchRoller for reuse in other components.
133 lines
4.1 KiB
JavaScript
Executable File
133 lines
4.1 KiB
JavaScript
Executable File
require('dotenv').config();
|
|
const express = require('express');
|
|
const cors = require('cors');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const playerRoutes = require('./routes/playerRoutes-sqlite');
|
|
const sessionRoutes = require('./routes/sessionRoutes-sqlite');
|
|
const shopRoutes = require('./routes/shopRoutes');
|
|
const rulesRoutes = require('./routes/rulesRoutes');
|
|
// const rulesRoutes = require('./routes/rulesRoutes-simple');
|
|
|
|
console.log('Routes loaded:', {
|
|
playerRoutes: typeof playerRoutes,
|
|
sessionRoutes: typeof sessionRoutes,
|
|
shopRoutes: typeof shopRoutes,
|
|
rulesRoutes: typeof rulesRoutes
|
|
});
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 5000;
|
|
|
|
// Middleware
|
|
app.use(express.json());
|
|
app.use(cors());
|
|
|
|
// API Routes (before static files)
|
|
console.log('Registering API routes...');
|
|
try {
|
|
console.log('Registering /api/players');
|
|
app.use('/api/players', playerRoutes);
|
|
console.log('Players routes registered');
|
|
} catch (e) {
|
|
console.error('Error mounting /api/players:', e && e.stack ? e.stack : e);
|
|
throw e;
|
|
}
|
|
try {
|
|
console.log('Registering /api/sessions');
|
|
app.use('/api/sessions', sessionRoutes);
|
|
console.log('Sessions routes registered');
|
|
} catch (e) {
|
|
console.error('Error mounting /api/sessions:', e && e.stack ? e.stack : e);
|
|
throw e;
|
|
}
|
|
try {
|
|
console.log('Registering /api/shop');
|
|
app.use('/api/shop', shopRoutes);
|
|
console.log('Shop routes registered');
|
|
} catch (e) {
|
|
console.error('Error mounting /api/shop:', e && e.stack ? e.stack : e);
|
|
throw e;
|
|
}
|
|
try {
|
|
console.log('Registering /api/rules');
|
|
app.use('/api/rules', rulesRoutes);
|
|
console.log('Rules routes registered');
|
|
} catch (e) {
|
|
console.error('Error mounting /api/rules:', e && e.stack ? e.stack : e);
|
|
throw e;
|
|
}
|
|
|
|
// Serve static files from build directory (React app)
|
|
const buildDir = path.join(__dirname, '..', 'build');
|
|
app.use(express.static(buildDir));
|
|
|
|
// Serve uploaded avatars from public/avatars
|
|
const avatarsDir = path.join(__dirname, '..', 'public', 'avatars');
|
|
if (!fs.existsSync(avatarsDir)) {
|
|
fs.mkdirSync(avatarsDir, { recursive: true });
|
|
}
|
|
app.use('/avatars', express.static(avatarsDir));
|
|
|
|
// Catch-all handler for React Router (must be after API routes)
|
|
// app.get('*', (req, res) => {
|
|
// res.sendFile(path.join(buildDir, 'index.html'));
|
|
// });
|
|
|
|
// Initialize SQLite database
|
|
const { db } = require('./sqlite-db');
|
|
|
|
// Graceful shutdown
|
|
process.on('SIGINT', () => {
|
|
console.log('Closing SQLite database...');
|
|
db.close();
|
|
process.exit(0);
|
|
});
|
|
|
|
process.on('SIGTERM', () => {
|
|
console.log('Closing SQLite database...');
|
|
db.close();
|
|
process.exit(0);
|
|
});
|
|
|
|
// If a React build exists, serve it at root and fallback to index.html for client-side routing
|
|
const indexHtml = path.join(buildDir, 'index.html');
|
|
if (fs.existsSync(indexHtml)) {
|
|
app.get('/', (req, res) => res.sendFile(indexHtml));
|
|
|
|
// Catch-all for client-side routes (must be after API routes)
|
|
// Middleware fallback for client-side routing: serve index.html for non-API and non-avatar paths.
|
|
// Use a plain middleware (no path) to avoid route string parsing by path-to-regexp.
|
|
app.use((req, res, next) => {
|
|
if (req.path.startsWith('/api/') || req.path.startsWith('/avatars/')) return next();
|
|
return res.sendFile(indexHtml);
|
|
});
|
|
} else {
|
|
// Root route for friendly message when no build is present
|
|
app.get('/', (req, res) => {
|
|
res.send('Deathwatch Roller API is running with SQLite. Use /api/players for player data.');
|
|
});
|
|
}
|
|
|
|
// Use routes (instrument mounts to debug invalid route patterns)
|
|
console.log('Mounting route: /api/shop');
|
|
app.use('/api/shop', shopRoutes);
|
|
console.log('Mounted /api/shop');
|
|
console.log('Mounting route: /api/players');
|
|
app.use('/api/players', playerRoutes);
|
|
console.log('Mounted /api/players');
|
|
console.log('Mounting route: /api/sessions');
|
|
app.use('/api/sessions', sessionRoutes);
|
|
console.log('Mounted /api/sessions');
|
|
console.log('Mounting route: /api/rules');
|
|
app.use('/api/rules', rulesRoutes);
|
|
console.log('Mounted /api/rules');
|
|
|
|
// Start Server
|
|
app.listen(PORT, '0.0.0.0', () => {
|
|
console.log(`Server running on http://0.0.0.0:${PORT}`);
|
|
console.log('Using SQLite database');
|
|
});
|
|
|
|
module.exports = app;
|