You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
5.4 KiB
210 lines
5.4 KiB
import 'regenerator-runtime/runtime'
|
|
import path from 'path'
|
|
import dotenv from 'dotenv'
|
|
|
|
dotenv.config({ path: path.join(__dirname, '../.env') })
|
|
|
|
import http from 'http'
|
|
import express from 'express'
|
|
import proxy from 'express-http-proxy'
|
|
import bodyParser from 'body-parser'
|
|
import cookieParser from 'cookie-parser'
|
|
import session from 'express-session'
|
|
import connectRedis from 'connect-redis'
|
|
|
|
import { Server as SocketIO } from 'socket.io'
|
|
import { createAdapter } from 'socket.io-redis'
|
|
import passportSocketIo from 'passport.socketio'
|
|
|
|
import morgan from 'morgan'
|
|
|
|
import logger, { morganStream } from './logger'
|
|
|
|
import passport from 'passport'
|
|
import mongoose from 'mongoose'
|
|
import UserModel from './models/user'
|
|
|
|
import { getMuseumDocument } from './museumStats'
|
|
|
|
import redisClient, {
|
|
asyncHEXISTS,
|
|
asyncHGET,
|
|
asyncHSET,
|
|
asyncHDEL,
|
|
asyncHGETALL,
|
|
asyncDEL,
|
|
} from './redis'
|
|
|
|
import { authRouter, initPassport } from './auth/passport'
|
|
|
|
const RedisStore = connectRedis(session)
|
|
|
|
const mongoHost = process.env.MONGODB_HOST ?? 'localhost'
|
|
|
|
mongoose.connect(
|
|
`mongodb://${mongoHost}:27017`,
|
|
{
|
|
useCreateIndex: true,
|
|
useNewUrlParser: true,
|
|
useUnifiedTopology: true,
|
|
user: process.env.MONGODB_USER,
|
|
pass: process.env.MONGODB_PASSWORD,
|
|
dbName: 'museum',
|
|
},
|
|
() => {
|
|
logger.info('Connection to MongoDB has been established successfully.')
|
|
},
|
|
)
|
|
|
|
const app = express()
|
|
app.set('trust proxy', 1)
|
|
const server = http.createServer(app)
|
|
|
|
asyncDEL('socket')
|
|
asyncDEL('transform')
|
|
const io = new SocketIO(server, { serveClient: false })
|
|
|
|
const pubClient = redisClient
|
|
const subClient = pubClient.duplicate()
|
|
io.adapter(
|
|
createAdapter({
|
|
pubClient,
|
|
subClient,
|
|
}),
|
|
)
|
|
|
|
app.use(bodyParser.json())
|
|
app.use(bodyParser.urlencoded({ extended: false }))
|
|
app.use(cookieParser(process.env.SESSION_SECRET))
|
|
app.use(
|
|
session({
|
|
store: new RedisStore({ client: redisClient }),
|
|
secret: process.env.SESSION_SECRET,
|
|
resave: false,
|
|
saveUninitialized: false,
|
|
}),
|
|
)
|
|
|
|
app.use(passport.initialize())
|
|
app.use(passport.session())
|
|
initPassport()
|
|
app.use('/auth', authRouter)
|
|
|
|
app.use(morgan('short', { stream: morganStream }))
|
|
|
|
async function onAuthorizeSuccess(data, accept) {
|
|
if (data.user._id) {
|
|
const userExists = await asyncHEXISTS('socket', String(data.user._id))
|
|
|
|
if (userExists === 1) {
|
|
logger.debug(
|
|
`${data.user._id} (${data.user.email}) is already logged in. Kicking...`,
|
|
)
|
|
const socketId = await asyncHGET('socket', String(data.user._id))
|
|
const socket = io.of('/').sockets.get(socketId)
|
|
if (socket) {
|
|
logger.debug(`Disconnecting ${socketId}`)
|
|
socket.disconnect()
|
|
}
|
|
}
|
|
logger.debug(
|
|
`Successful connection to socket.io from ${data.user._id} (${data.user.email})`,
|
|
)
|
|
accept(null, true)
|
|
}
|
|
}
|
|
|
|
function onAuthorizeFail(_, message, error, accept) {
|
|
if (error) throw new Error(message)
|
|
logger.debug('Client failed to connect to socket.io:', message)
|
|
accept(null, false)
|
|
}
|
|
|
|
io.use(
|
|
passportSocketIo.authorize({
|
|
// @ts-ignore
|
|
cookieParser: cookieParser,
|
|
secret: process.env.SESSION_SECRET,
|
|
store: new RedisStore({ client: redisClient }),
|
|
success: onAuthorizeSuccess,
|
|
fail: onAuthorizeFail,
|
|
}),
|
|
)
|
|
|
|
io.on('connection', async (socket) => {
|
|
await asyncHSET('socket', String(socket.request.user._id), String(socket.id))
|
|
logger.debug(`Assigned ${socket.id} to ${socket.request.user._id}`)
|
|
|
|
socket.on('transform', async (payload) => {
|
|
await asyncHSET('transform', String(socket.id), JSON.stringify(payload))
|
|
})
|
|
|
|
socket.on('disconnect', async () => {
|
|
logger.debug(`User with id ${socket.id} disconnected`)
|
|
const user = await UserModel.findById(socket.request.user._id)
|
|
|
|
if (user) {
|
|
const transform = await asyncHGET('transform', String(socket.id))
|
|
user.lastLocation = JSON.parse(transform)
|
|
user.save()
|
|
}
|
|
|
|
await asyncHDEL('transform', String(socket.id))
|
|
await asyncHDEL('socket', String(socket.request.user._id))
|
|
})
|
|
|
|
socket.on('register-visit', async () => {
|
|
logger.debug(`Registering new new visit from: ${socket.request.user.name}`)
|
|
const museumStats = await getMuseumDocument()
|
|
museumStats.visitCount = museumStats.visitCount + 1
|
|
|
|
const socketCount = io.of('/').sockets.size
|
|
|
|
if (museumStats.maxSimultaneousVisitsCount < socketCount) {
|
|
museumStats.maxSimultaneousVisitsCount = socketCount
|
|
}
|
|
museumStats.save()
|
|
|
|
const user = await UserModel.findById(socket.request.user._id)
|
|
user.visitCount = user.visitCount + 1
|
|
user.save()
|
|
})
|
|
|
|
const user = await UserModel.findById(socket.request.user._id)
|
|
if (user) {
|
|
socket.emit('initial-transform', user.lastLocation)
|
|
}
|
|
})
|
|
|
|
async function broadcastTransforms() {
|
|
const transforms = await asyncHGETALL('transform')
|
|
|
|
if (transforms) {
|
|
let parsed = {}
|
|
Object.entries(transforms).forEach(([key, value]) => {
|
|
parsed = { ...parsed, [key]: { ...JSON.parse(value) } }
|
|
})
|
|
|
|
io.sockets.emit('broadcast-transforms', parsed)
|
|
}
|
|
|
|
setTimeout(broadcastTransforms, 30)
|
|
}
|
|
|
|
broadcastTransforms()
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
app.use('/', proxy('http://localhost:4000/'))
|
|
} else {
|
|
const buildPath = path.resolve(__dirname, '../../client/build')
|
|
const indexHtml = path.join(buildPath, 'index.html')
|
|
|
|
app.use(express.static(buildPath))
|
|
app.get('*', (_, res) => res.sendFile(indexHtml))
|
|
}
|
|
|
|
const port = process.env.PORT ?? 3000
|
|
server.listen(port, () => {
|
|
logger.info(`Server ready on port ${port}`)
|
|
})
|