200 lines
6.2 KiB
JavaScript
200 lines
6.2 KiB
JavaScript
import yargs from 'yargs/yargs'
|
|
import { hideBin } from 'yargs/helpers'
|
|
import mysql from 'mysql2/promise'
|
|
import { WebSocketServer } from 'ws'
|
|
import fs from 'fs'
|
|
import {RedisConnexion} from './redisConnexion.js'
|
|
import {wssServer} from './wssServer.js'
|
|
import {configHelper} from './configHelper.js'
|
|
import express from 'express'
|
|
import session from 'express-session'
|
|
import connectMySQL from 'express-mysql-session'
|
|
|
|
const argv = yargs(hideBin(process.argv)).command('wssGateway', 'Redis <=> Websocket message bus gateway', {})
|
|
.options({
|
|
'argv.debug': {
|
|
description: 'shows debug info',
|
|
alias: 'd',
|
|
type: 'boolean'
|
|
},
|
|
}).help().version('1.1').argv
|
|
|
|
const debug = Boolean(process.env.DEBUG) || argv.debug
|
|
|
|
const MySQLStore = connectMySQL(session)
|
|
|
|
const app = express();
|
|
app.set('trust proxy', 1) // trust first proxy (nginx), so we serve http to nginx, but we still behave as if we're in https
|
|
app.use(express.json())
|
|
|
|
const mysqlCreds = {
|
|
// host: '127.0.0.1',
|
|
// port: 3306,
|
|
socketPath: '/var/run/mysqld/mysqld.sock',
|
|
user: 'p42',
|
|
password: 'C3h=V9!r>Mvc>skxPf9?W2P3duJTk',
|
|
database: 'p42',
|
|
waitForConnections: true,
|
|
connectionLimit: 10,
|
|
queueLimit: 0
|
|
}
|
|
|
|
const db = await mysql.createConnection(mysqlCreds)
|
|
|
|
const sessionStore = new MySQLStore({
|
|
createDatabaseTable: false,
|
|
clearExpired: true,
|
|
schema: {
|
|
tableName: 'p42_sessions',
|
|
columnNames: {
|
|
session_id: 'session_id',
|
|
expires: 'expires',
|
|
data: 'data'
|
|
}
|
|
}
|
|
}, db)
|
|
|
|
|
|
const sessionParser = session({
|
|
name: 'p42.api.sid',
|
|
secret: 'qNhy555Y9vyxj?!3yaYA=aKfgk+Wy5eymNtP*?4i',
|
|
store: sessionStore,
|
|
resave: false,
|
|
saveUninitialized: false,
|
|
cookie: {
|
|
maxAge: 1000 * 60 * 60 * 4,
|
|
secure: true, //See trust proxy above
|
|
sameSite: 'lax',
|
|
},
|
|
}
|
|
)
|
|
|
|
app.use(sessionParser)
|
|
|
|
const cfgh = new configHelper({
|
|
localfile: './wssGatewayConfig.json',
|
|
})
|
|
|
|
async function startAllRedis(wssGatewayConfig) {
|
|
if (debug) console.log('Starting all Redis instances...')
|
|
|
|
//1. instantiate all & login all
|
|
const redisConns = wssGatewayConfig.redis.map(cfg =>
|
|
new RedisConnexion({ debug, config: cfg, redisId:cfg.redisId })
|
|
)
|
|
const loginResults = await Promise.allSettled(
|
|
redisConns.map(async cnx => {
|
|
cnx.redisLogin()
|
|
return cnx.redisId
|
|
})
|
|
)
|
|
|
|
2. //make sure all connected before going any further
|
|
const failedLogin = loginResults.filter(r => r.status !== 'fulfilled')
|
|
if (failedLogin.length > 0) {
|
|
console.error('Redis login failures:')
|
|
failedLogin.forEach((r, i) => {
|
|
const id = redisConns[i].redisId
|
|
console.error(`chansStart failed for redis:[${id}] → ${r.reason}`)
|
|
})
|
|
throw new Error(
|
|
`Redis login failed for ${failedLogin.length}/${redisConns.length} instances`
|
|
)
|
|
}
|
|
|
|
if (debug) console.log('All Redis logins OK')
|
|
|
|
// --- Phase 2: start channels for all (since all succeeded)
|
|
const chanResults = await Promise.allSettled(
|
|
redisConns.map(async cnx => {
|
|
cnx.redisChansStart()
|
|
return cnx.redisId
|
|
})
|
|
)
|
|
|
|
const failedChans = chanResults.filter(r => r.status !== 'fulfilled')
|
|
if (failedChans.length > 0) {
|
|
console.error('Redis chansStart failures:')
|
|
failedChans.forEach((r, i) => {
|
|
const id = redisConns[i].redisId
|
|
console.error(`chansStart failed for redis:[${id}] → ${r.reason}`)
|
|
})
|
|
throw new Error(
|
|
`Redis chansStart failed for ${failedChans.length}/${redisConns.length} instances`
|
|
)
|
|
}
|
|
|
|
if (debug) console.log('All Redis chansStart OK')
|
|
|
|
return redisConns
|
|
}
|
|
|
|
cfgh.fetchConfig().then( async wssGatewayConfig => {
|
|
|
|
if((!wssGatewayConfig) || (Object.keys(wssGatewayConfig).length<4)) {
|
|
console.error('Cannot get a valid configuration ! Aaarrghhh...')
|
|
process.exit()
|
|
}
|
|
|
|
let httpLib
|
|
if(wssGatewayConfig.server.unsecure) httpLib = await import('http')
|
|
else httpLib = await import('https')
|
|
|
|
|
|
/////////////////////// Create & Start servers \\\\\\\\\\\\\\\\\\\\\\
|
|
console.log(`Debug mode : ${debug ? 'ON' : 'OFF'}`)
|
|
let options
|
|
if(!wssGatewayConfig.server.unsecure) {
|
|
options = {
|
|
key: fs.readFileSync(wssGatewayConfig.server.certKeyFile),
|
|
cert: fs.readFileSync(wssGatewayConfig.server.certFile),
|
|
};
|
|
} else options = {}
|
|
|
|
const HTTPserver = httpLib.createServer(app)
|
|
|
|
const wssServerOptions = {
|
|
// server: HTTPserver,
|
|
// path: wssGatewayConfig.server.listenPath,
|
|
noServer: true
|
|
}
|
|
if(!wssGatewayConfig.server.unsecure) {
|
|
wssServerOptions['key'] = fs.readFileSync(wssGatewayConfig.server.certKeyFile)
|
|
wssServerOptions['cert'] = fs.readFileSync(wssGatewayConfig.server.certFile)
|
|
}
|
|
const WSSServer = new WebSocketServer(wssServerOptions);
|
|
|
|
HTTPserver.on('upgrade', (req, socket, head) => {
|
|
sessionParser(req, {}, () => {
|
|
if (!req.session) {
|
|
console.warn('No session, bye bye!')
|
|
socket.destroy()
|
|
return
|
|
}
|
|
WSSServer.handleUpgrade(req, socket, head, (ws) => {
|
|
ws.session = req.session // direct access to Express session
|
|
WSSServer.emit('connection', ws, req)
|
|
})
|
|
})
|
|
})
|
|
|
|
// WSSServer.on('connection', (ws, req) => {
|
|
// console.log('WS connected with session:', ws.session, ws.session.authenticated, ws.session.userInfos)
|
|
// // modify session if you want
|
|
// ws.session.views = (ws.session.views || 0) + 1
|
|
// ws.session.save()
|
|
// ws.send(`You visited ${ws.session.views} times`)
|
|
// })
|
|
|
|
HTTPserver.listen(wssGatewayConfig.server.listenPort, () => {
|
|
console.log(`WS${wssGatewayConfig.server.unsecure ? '': 'S'} server created for ${wssGatewayConfig.server.listenHost}:${wssGatewayConfig.server.listenPort}`)
|
|
})
|
|
|
|
startRedis(wssGatewayConfig).then((allRediscnx) => {
|
|
const wssSrv = new wssServer(cfgh, WSSServer, allRediscnx, debug);
|
|
});
|
|
|
|
|
|
})
|
|
|