import express, { RequestHandler } from 'express'; import session from 'express-session'; import passport from 'passport'; import * as redis from 'redis'; import * as icynetstrat from 'passport-icynet'; import connectRedis from 'connect-redis'; import http from 'http'; import { join } from 'path'; import { Server, Socket } from 'socket.io'; import { IcyNetUser } from './types/user'; import { Canvas } from './object/canvas'; import { CanvasRecord, Placement } from '../common/types/canvas'; import { config } from './config'; const RedisStore = connectRedis(session); const redisClient = config.redis?.enabled ? redis.createClient() : undefined; const sessionMiddleware = session({ secret: config.server.sessionSecret, resave: false, saveUninitialized: false, cookie: { secure: process.env.NODE_ENV === 'production', sameSite: 'strict' }, store: config.redis?.enabled ? new RedisStore({ client: redisClient }) : undefined, }); // todo: store less info in session passport.serializeUser((user, done) => { done(null, user); }); passport.deserializeUser((obj: IcyNetUser, done) => { done(null, obj); }); passport.use( new icynetstrat.Strategy( { clientID: config.auth.clientID, clientSecret: config.auth.clientSecret, callbackURL: config.auth.callbackURL, scope: [], }, function ( accessToken: string, refreshToken: string, profile: any, done: Function, ) { console.log(profile); process.nextTick(function () { return done(null, profile); }); }, ), ); const app = express(); if (process.env.NODE_ENV === 'production') { app.enable('trust proxy'); } const server = http.createServer(app); const io = new Server(server); const checkAuth: RequestHandler = (req, res, next) => { if (req.isAuthenticated()) { return next(); } res.send('not logged in :('); }; app.use(sessionMiddleware); app.use(passport.initialize()); app.use(passport.session()); app.get( '/login', passport.authenticate('icynet', { scope: [] }), (req, res) => {}, ); app.get( '/callback', passport.authenticate('icynet', { failureRedirect: '/?login=false' }), (req, res) => { res.redirect('/?login=true'); }, // auth success ); app.get('/logout', (req, res) => { req.logout(); res.redirect('/'); }); app.get('/info', checkAuth, (req, res) => { res.json(req.user); }); app.use(express.static(join(__dirname, '..', 'public'))); /// const canvas = new Canvas(config.game.boardSize); const connections: Socket[] = []; const changes: CanvasRecord[] = []; io.use((socket, next) => sessionMiddleware(socket.request as any, {} as any, next as any), ); io.on('connection', (socket) => { const session = (socket.request as any).session; const user = session?.passport?.user as IcyNetUser; connections.push(socket); socket.emit( 'me', user ? { username: user.username, display_name: user.display_name, } : null, ); socket.emit('plane', { size: canvas.size, canvas: Object.values(canvas.getFullPlane()), }); socket.on('color', ({ c, x, y, t }: Placement) => { if (!user || c > 0xffffff || c < 0) { return; } canvas.setPixelAt(c, x, y, user.username); socket.emit('colorack', { c, x, y, t }); }); socket.on('disconnect', () => { connections.splice(connections.indexOf(socket), 1); }); }); canvas.registerChangeHandler((record) => { changes.push(record); }); setInterval(() => { if (changes.length && connections.length) { connections.forEach((socket) => socket.emit('changes', changes)); changes.length = 0; } }, 1000); /// canvas.initialize().then(() => server.listen(config.server.port, 'localhost', () => { console.log(`Listening at http://localhost:${config.server.port}/`); }), );