mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-11 03:24:16 +00:00
250 lines
6.2 KiB
JavaScript
250 lines
6.2 KiB
JavaScript
import express from "express"
|
|
import http from "http"
|
|
import cors from "cors"
|
|
import morgan from "morgan"
|
|
import socketio from "socket.io"
|
|
import EventEmitter from "@foxify/events"
|
|
import jwt from "jsonwebtoken"
|
|
import axios from "axios"
|
|
|
|
import routes from "./routes"
|
|
|
|
const mainAPI = axios.create({
|
|
baseURL: process.env.MAIN_API_URL ?? "http://localhost:3010",
|
|
headers: {
|
|
"server_token": `${process.env.MAIN_SERVER_ID}:${process.env.MAIN_SERVER_TOKEN}`,
|
|
}
|
|
})
|
|
|
|
class SpotifyRoom {
|
|
constructor(options = {}) {
|
|
this.options = {
|
|
...options,
|
|
}
|
|
|
|
this.owner_user_id = options.owner_user_id
|
|
|
|
this.roomId = options.roomId
|
|
}
|
|
|
|
owner = null
|
|
|
|
listeners = []
|
|
|
|
appendListener = (listener) => {
|
|
|
|
}
|
|
|
|
initialize = async () => {
|
|
|
|
}
|
|
}
|
|
|
|
|
|
class RealtimeRoomEventServer {
|
|
constructor(server, options = {}) {
|
|
this.io = socketio(server, {
|
|
cors: {
|
|
origin: "*",
|
|
methods: ["GET", "POST"],
|
|
credentials: true,
|
|
}
|
|
})
|
|
|
|
this.limitations = {
|
|
...options.limitations,
|
|
}
|
|
}
|
|
|
|
connectionPool = []
|
|
|
|
roomEventsHandlers = {
|
|
"owner:update": (socket, payload) => {
|
|
|
|
}
|
|
}
|
|
|
|
initializeSocketIO = () => {
|
|
this.io.use(async (socket, next) => {
|
|
try {
|
|
const token = socket.handshake.auth.token
|
|
|
|
if (!token) {
|
|
return next(new Error(`auth:token_missing`))
|
|
}
|
|
|
|
const session = await mainAPI.post("/session/validate", {
|
|
session: token
|
|
})
|
|
.then((res) => {
|
|
return res.data
|
|
})
|
|
.catch((err) => {
|
|
console.log(err.response.data)
|
|
return false
|
|
})
|
|
|
|
if (!session || !session?.valid) {
|
|
return next(new Error(`auth:token_invalid`))
|
|
}
|
|
|
|
const userData = await mainAPI.get(`/user/${session.user_id}/data`)
|
|
.then((res) => {
|
|
return res.data
|
|
})
|
|
.catch((err) => {
|
|
console.log(err)
|
|
return null
|
|
})
|
|
|
|
if (!userData) {
|
|
return next(new Error(`auth:user_failed`))
|
|
}
|
|
|
|
try {
|
|
// try to decode the token and get the user's username
|
|
const decodedToken = jwt.decode(token)
|
|
|
|
socket.userData = userData
|
|
socket.token = token
|
|
socket.decodedToken = decodedToken
|
|
}
|
|
catch (err) {
|
|
return next(new Error(`auth:decode_failed`))
|
|
}
|
|
|
|
console.log(`[${socket.id}] connected`)
|
|
|
|
next()
|
|
} catch (error) {
|
|
next(new Error(`auth:authentification_failed`))
|
|
}
|
|
})
|
|
|
|
this.io.on("connection", (socket) => {
|
|
socket.on("join", (...args) => this.handleClientJoin(socket, ...args))
|
|
|
|
socket.on("disconnect", () => {
|
|
this.handleClientDisconnect(socket)
|
|
})
|
|
})
|
|
}
|
|
|
|
async handleClientJoin(socket, payload, cb) {
|
|
const { room, type } = payload
|
|
|
|
if (!room) {
|
|
return cb(new Error(`room:invalid`))
|
|
}
|
|
|
|
socket.connectedRoom = room
|
|
|
|
const pool = await this.attachClientToPool(socket, room).catch((err) => {
|
|
cb(err)
|
|
return null
|
|
})
|
|
|
|
if (!pool) return
|
|
|
|
console.log(`[${socket.id}] joined room [${room}]`)
|
|
|
|
socket.join(room)
|
|
|
|
Object.keys(this.roomEventsHandlers).forEach((event) => {
|
|
socket.on(event, (...args) => this.roomEventsHandlers[event](socket, ...args))
|
|
})
|
|
|
|
// start spotify ws connection
|
|
|
|
const roomConnections = this.connectionPool.filter((client) => client.room === room).length
|
|
|
|
return cb(null, {
|
|
roomConnections,
|
|
limitations: this.limitations,
|
|
})
|
|
}
|
|
|
|
handleClientDisconnect(socket) {
|
|
const index = this.connectionPool.findIndex((client) => client.id === socket.id)
|
|
|
|
if (index === -1) return
|
|
|
|
return this.connectionPool.splice(index, 1)
|
|
}
|
|
|
|
async attachClientToPool(socket, room) {
|
|
// TODO: check if user can join room or is privated
|
|
|
|
if (!room) {
|
|
throw new Error(`room:invalid`)
|
|
}
|
|
|
|
return this.connectionPool.push({
|
|
id: socket.id,
|
|
room,
|
|
socket
|
|
})
|
|
}
|
|
}
|
|
|
|
export default class Server {
|
|
constructor(options = {}) {
|
|
this.app = express()
|
|
this.httpServer = http.createServer(this.app)
|
|
|
|
this.roomServer = new RealtimeRoomEventServer(this.httpServer)
|
|
|
|
this.options = {
|
|
listenPort: process.env.PORT || 3030,
|
|
...options
|
|
}
|
|
}
|
|
|
|
eventBus = global.eventBus = new EventEmitter()
|
|
|
|
initialize = async () => {
|
|
this.app.use(cors())
|
|
this.app.use(express.json({ extended: false }))
|
|
this.app.use(express.urlencoded({ extended: true }))
|
|
|
|
// Use logger if not in production
|
|
if (!process.env.NODE_ENV === "production") {
|
|
this.app.use(morgan("dev"))
|
|
}
|
|
|
|
await this.roomServer.initializeSocketIO()
|
|
|
|
await this.registerBaseRoute()
|
|
await this.registerRoutes()
|
|
|
|
await this.httpServer.listen(this.options.listenPort)
|
|
|
|
return {
|
|
listenPort: this.options.listenPort,
|
|
}
|
|
}
|
|
|
|
async registerBaseRoute() {
|
|
await this.app.get("/", async (req, res) => {
|
|
return res.json({
|
|
uptimeMinutes: Math.floor(process.uptime() / 60),
|
|
})
|
|
})
|
|
}
|
|
|
|
registerRoutes() {
|
|
routes.forEach((route) => {
|
|
const order = []
|
|
|
|
if (route.middlewares) {
|
|
route.middlewares.forEach((middleware) => {
|
|
order.push(middleware)
|
|
})
|
|
}
|
|
|
|
order.push(route.routes)
|
|
|
|
this.app.use(route.use, ...order)
|
|
})
|
|
}
|
|
} |