Files
InviCanvas/server/index.ts
ExilProductions cf066ef305 Initial Commit
2026-01-14 17:52:35 +01:00

130 lines
3.1 KiB
TypeScript

import express, { Request, Response } from 'express';
import http from 'http';
import { Server as SocketIOServer } from 'socket.io';
import cors from 'cors';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import passport from 'passport';
import path from 'path';
import { setupSocketIO } from './socket';
import { db } from './database';
import { config } from './config';
import {
securityHeaders,
createRateLimit,
requestLogger,
sanitizeInput
} from './middleware';
import { logger } from './logger';
import './auth';
const app = express();
const server = http.createServer(app);
const io = new SocketIOServer(server, {
cors: {
origin: config.security.corsOrigin,
methods: ['GET', 'POST'],
credentials: true
},
transports: ['polling', 'websocket']
});
// Security middleware
app.use(securityHeaders);
app.use(requestLogger);
app.use(sanitizeInput);
// Cookie parsing
app.use(cookieParser());
// Rate limiting
app.use('/api', createRateLimit);
// Session configuration
const SQLiteStore = require('connect-sqlite3')(session);
const sessionMiddleware = session({
store: new SQLiteStore({ db: 'sessions.db', dir: './data' }),
secret: process.env.NEXTAUTH_SECRET || 'fallback-secret-change-in-production',
resave: false,
saveUninitialized: false,
cookie: { secure: false, maxAge: 24 * 60 * 60 * 1000 } // 24 hours
});
app.use(sessionMiddleware);
// Passport initialization
app.use(passport.initialize());
app.use(passport.session());
// Body parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
// CORS with specific origin
app.use(cors({
origin: config.server.nodeEnv === 'development'
? ['http://localhost:3000', 'http://localhost:5173']
: config.security.corsOrigin,
credentials: true
}));
app.use(express.static(path.join(__dirname, '../client/dist')));
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// Auth routes
app.get('/auth/github', passport.authenticate('github'));
app.get('/auth/github/callback',
passport.authenticate('github', { failureRedirect: '/login' }),
(req, res) => {
res.redirect('/');
}
);
app.get('/api/user', (req, res) => {
if (req.user) {
res.json(req.user);
} else {
res.status(401).json({ error: 'Not authenticated' });
}
});
app.get('/api/socket-token', (req, res) => {
if (req.user) {
// Create a token containing user info
const user = req.user as any; // Use any to bypass typing issues
const token = Buffer.from(JSON.stringify({
userId: user.id,
username: user.username,
timestamp: Date.now()
})).toString('base64');
res.json({ token, user: req.user });
} else {
res.status(401).json({ error: 'Not authenticated' });
}
});
// SPA catch-all route
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../client/dist/index.html'));
});
io.use((socket: any, next: any) => {
next();
});
setupSocketIO(io);
const PORT = config.server.port;
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});