mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-11 03:24:16 +00:00
improve ws & cleanup
This commit is contained in:
parent
41ae2d81e8
commit
92879b0312
@ -5,29 +5,34 @@ import bcrypt from "bcrypt"
|
|||||||
import passport from "passport"
|
import passport from "passport"
|
||||||
|
|
||||||
import jwt from "jsonwebtoken"
|
import jwt from "jsonwebtoken"
|
||||||
|
import EventEmitter from "@foxify/events"
|
||||||
|
|
||||||
import { User, Session, Config } from "./models"
|
import { User, Session, Config } from "./models"
|
||||||
|
|
||||||
import DbManager from "./classes/DbManager"
|
import DbManager from "./classes/DbManager"
|
||||||
import { createStorageClientInstance } from "./classes/StorageClient"
|
import { createStorageClientInstance } from "./classes/StorageClient"
|
||||||
|
|
||||||
|
import internalEvents from "./events"
|
||||||
|
|
||||||
const ExtractJwt = require("passport-jwt").ExtractJwt
|
const ExtractJwt = require("passport-jwt").ExtractJwt
|
||||||
const LocalStrategy = require("passport-local").Strategy
|
const LocalStrategy = require("passport-local").Strategy
|
||||||
|
|
||||||
const controllers = require("./controllers")
|
const controllers = require("./controllers")
|
||||||
const middlewares = require("./middlewares")
|
const middlewares = require("./middlewares")
|
||||||
|
|
||||||
|
global.httpListenPort = process.env.PORT || 3000
|
||||||
|
global.signLocation = process.env.signLocation
|
||||||
|
|
||||||
export default class Server {
|
export default class Server {
|
||||||
env = process.env
|
DB = new DbManager()
|
||||||
|
eventBus = new EventEmitter()
|
||||||
|
|
||||||
storage = global.storage = createStorageClientInstance()
|
storage = global.storage = createStorageClientInstance()
|
||||||
DB = new DbManager()
|
|
||||||
|
|
||||||
httpListenPort = this.env.listenPort ?? 3000
|
|
||||||
|
|
||||||
controllers = [
|
controllers = [
|
||||||
controllers.ConfigController,
|
controllers.ConfigController,
|
||||||
controllers.RolesController,
|
controllers.RolesController,
|
||||||
|
controllers.FollowerController,
|
||||||
controllers.SessionController,
|
controllers.SessionController,
|
||||||
controllers.UserController,
|
controllers.UserController,
|
||||||
controllers.FilesController,
|
controllers.FilesController,
|
||||||
@ -43,24 +48,28 @@ export default class Server {
|
|||||||
middlewares = middlewares
|
middlewares = middlewares
|
||||||
|
|
||||||
server = new LinebridgeServer({
|
server = new LinebridgeServer({
|
||||||
port: this.httpListenPort,
|
port: httpListenPort,
|
||||||
headers: {
|
headers: {
|
||||||
"Access-Control-Expose-Headers": "regenerated_token",
|
"Access-Control-Expose-Headers": "regenerated_token",
|
||||||
},
|
},
|
||||||
onWSClientConnection: this.onWSClientConnection,
|
onWSClientConnection: (...args) => {
|
||||||
onWSClientDisconnection: this.onWSClientDisconnection,
|
this.onWSClientConnection(...args)
|
||||||
}, this.controllers, this.middlewares)
|
},
|
||||||
|
onWSClientDisconnect: (...args) => {
|
||||||
|
this.onWSClientDisconnect(...args)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
this.controllers,
|
||||||
|
this.middlewares
|
||||||
|
)
|
||||||
|
|
||||||
options = {
|
jwtStrategy = global.jwtStrategy = {
|
||||||
jwtStrategy: {
|
|
||||||
sessionLocationSign: this.server.id,
|
|
||||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
secretOrKey: this.server.oskid,
|
secretOrKey: this.server.oskid,
|
||||||
algorithms: ["sha1", "RS256", "HS256"],
|
algorithms: ["sha1", "RS256", "HS256"],
|
||||||
expiresIn: this.env.signLifetime ?? "1h",
|
expiresIn: process.env.signLifetime ?? "1h",
|
||||||
enforceRegenerationTokenExpiration: false,
|
enforceRegenerationTokenExpiration: false,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.server.engineInstance.use(express.json())
|
this.server.engineInstance.use(express.json())
|
||||||
@ -82,12 +91,8 @@ export default class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
global.wsInterface = this.server.wsInterface
|
global.wsInterface = this.server.wsInterface
|
||||||
global.httpListenPort = this.listenPort
|
|
||||||
|
|
||||||
global.uploadCachePath = this.env.uploadCachePath ?? path.resolve(process.cwd(), "cache")
|
global.uploadCachePath = process.env.uploadCachePath ?? path.resolve(process.cwd(), "cache")
|
||||||
|
|
||||||
global.jwtStrategy = this.options.jwtStrategy
|
|
||||||
global.signLocation = this.env.signLocation
|
|
||||||
|
|
||||||
global.DEFAULT_POSTING_POLICY = {
|
global.DEFAULT_POSTING_POLICY = {
|
||||||
maxMessageLength: 512,
|
maxMessageLength: 512,
|
||||||
@ -111,7 +116,14 @@ export default class Server {
|
|||||||
maximumFileSize: 80 * 1024 * 1024,
|
maximumFileSize: 80 * 1024 * 1024,
|
||||||
maximunFilesPerRequest: 20,
|
maximunFilesPerRequest: 20,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// register internal events
|
||||||
|
for (const [eventName, eventHandler] of Object.entries(internalEvents)) {
|
||||||
|
this.eventBus.on(eventName, eventHandler)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
events = internalEvents
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
await this.DB.connect()
|
await this.DB.connect()
|
||||||
@ -176,7 +188,7 @@ export default class Server {
|
|||||||
|
|
||||||
initPassport() {
|
initPassport() {
|
||||||
this.server.middlewares["useJwtStrategy"] = (req, res, next) => {
|
this.server.middlewares["useJwtStrategy"] = (req, res, next) => {
|
||||||
req.jwtStrategy = this.options.jwtStrategy
|
req.jwtStrategy = this.jwtStrategy
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,40 +205,41 @@ export default class Server {
|
|||||||
User.findOne(query).select("+password")
|
User.findOne(query).select("+password")
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data === null) {
|
if (data === null) {
|
||||||
return done(null, false, this.options.jwtStrategy)
|
return done(null, false, this.jwtStrategy)
|
||||||
} else if (!bcrypt.compareSync(password, data.password)) {
|
} else if (!bcrypt.compareSync(password, data.password)) {
|
||||||
return done(null, false, this.options.jwtStrategy)
|
return done(null, false, this.jwtStrategy)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a token
|
// create a token
|
||||||
return done(null, data, this.options.jwtStrategy, { username, password })
|
return done(null, data, this.jwtStrategy, { username, password })
|
||||||
})
|
})
|
||||||
.catch(err => done(err, null, this.options.jwtStrategy))
|
.catch(err => done(err, null, this.jwtStrategy))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
this.server.engineInstance.use(passport.initialize())
|
this.server.engineInstance.use(passport.initialize())
|
||||||
}
|
}
|
||||||
|
|
||||||
initWebsockets() {
|
initWebsockets() {
|
||||||
this.server.middlewares["useWS"] = (req, res, next) => {
|
const onAuthenticated = async (socket, userData) => {
|
||||||
req.ws = global.wsInterface
|
await this.attachClientSocket(socket, userData)
|
||||||
next()
|
|
||||||
|
return socket.emit("authenticated")
|
||||||
}
|
}
|
||||||
|
|
||||||
const onAuthenticated = (socket, user_id) => {
|
const onAuthenticatedFailed = async (socket, error) => {
|
||||||
this.attachClientSocket(socket, user_id)
|
await this.detachClientSocket(socket)
|
||||||
socket.emit("authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
const onAuthenticatedFailed = (socket, error) => {
|
return socket.emit("authenticateFailed", {
|
||||||
this.detachClientSocket(socket)
|
|
||||||
socket.emit("authenticateFailed", {
|
|
||||||
error,
|
error,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.server.wsInterface.eventsChannels.push(["/main", "authenticate", async (socket, token) => {
|
this.server.wsInterface.eventsChannels.push(["/main", "authenticate", async (socket, authPayload) => {
|
||||||
const session = await Session.findOne({ token }).catch(err => {
|
if (!authPayload) {
|
||||||
|
return onAuthenticatedFailed(socket, "missing_auth_payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
const session = await Session.findOne({ token: authPayload.token }).catch((err) => {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -234,20 +247,20 @@ export default class Server {
|
|||||||
return onAuthenticatedFailed(socket, "Session not found")
|
return onAuthenticatedFailed(socket, "Session not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
this.verifyJwt(token, async (err, decoded) => {
|
await jwt.verify(authPayload.token, this.jwtStrategy.secretOrKey, async (err, decoded) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return onAuthenticatedFailed(socket, err)
|
return onAuthenticatedFailed(socket, err)
|
||||||
} else {
|
}
|
||||||
const user = await User.findById(decoded.user_id).catch(err => {
|
|
||||||
|
const userData = await User.findById(decoded.user_id).catch((err) => {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!user) {
|
if (!userData) {
|
||||||
return onAuthenticatedFailed(socket, "User not found")
|
return onAuthenticatedFailed(socket, "User not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
return onAuthenticated(socket, user)
|
return onAuthenticated(socket, userData)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
@ -256,48 +269,40 @@ export default class Server {
|
|||||||
console.log(`🌐 Client connected: ${socket.id}`)
|
console.log(`🌐 Client connected: ${socket.id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
onWSClientDisconnection = async (socket) => {
|
onWSClientDisconnect = async (socket) => {
|
||||||
console.log(`🌐 Client disconnected: ${socket.id}`)
|
console.log(`🌐 Client disconnected: ${socket.id}`)
|
||||||
this.detachClientSocket(socket)
|
this.detachClientSocket(socket)
|
||||||
}
|
}
|
||||||
|
|
||||||
attachClientSocket = async (client, userData) => {
|
attachClientSocket = async (socket, userData) => {
|
||||||
const socket = this.server.wsInterface.clients.find(c => c.id === client.id)
|
const client = this.server.wsInterface.clients.find(c => c.id === socket.id)
|
||||||
|
|
||||||
if (socket) {
|
if (client) {
|
||||||
socket.socket.disconnect()
|
client.socket.disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientObj = {
|
const clientObj = {
|
||||||
id: client.id,
|
id: socket.id,
|
||||||
socket: client,
|
socket: socket,
|
||||||
userId: userData._id.toString(),
|
user_id: userData._id.toString(),
|
||||||
user: userData,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.server.wsInterface.clients.push(clientObj)
|
this.server.wsInterface.clients.push(clientObj)
|
||||||
|
|
||||||
this.server.wsInterface.io.emit("userConnected", userData)
|
console.log(`📣 Client [${socket.id}] authenticated as ${userData.username}`)
|
||||||
|
|
||||||
|
this.eventBus.emit("user.connected", clientObj.user_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
detachClientSocket = async (client) => {
|
detachClientSocket = async (socket) => {
|
||||||
const socket = this.server.wsInterface.clients.find(c => c.id === client.id)
|
const client = this.server.wsInterface.clients.find(c => c.id === socket.id)
|
||||||
|
|
||||||
if (socket) {
|
if (client) {
|
||||||
socket.socket.disconnect()
|
this.server.wsInterface.clients = this.server.wsInterface.clients.filter(c => c.id !== socket.id)
|
||||||
this.server.wsInterface.clients = this.server.wsInterface.clients.filter(c => c.id !== client.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.server.wsInterface.io.emit("userDisconnect", client.id)
|
console.log(`📣🔴 Client [${socket.id}] authenticated as ${client.user_id} disconnected`)
|
||||||
}
|
|
||||||
|
|
||||||
verifyJwt = (token, callback) => {
|
this.eventBus.emit("user.disconnected", client.user_id)
|
||||||
jwt.verify(token, this.options.jwtStrategy.secretOrKey, async (err, decoded) => {
|
}
|
||||||
if (err) {
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null, decoded)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user