diff --git a/packages/app/constants/settings/account/index.jsx b/packages/app/constants/settings/account/index.jsx index cffee7d7..f22336f4 100644 --- a/packages/app/constants/settings/account/index.jsx +++ b/packages/app/constants/settings/account/index.jsx @@ -35,7 +35,7 @@ export default [ "onUpdate": async (value) => { const selfId = await User.selfUserId() - const result = window.app.request.post.updateUser({ + const result = window.app.api.withEndpoints("main").post.updateUser({ _id: selfId, update: { fullName: value @@ -52,7 +52,7 @@ export default [ "icon": "Delete", "title": "Unset", "onClick": async () => { - window.app.request.post.unsetPublicName() + window.app.api.withEndpoints("main").post.unsetPublicName() } } ], @@ -78,7 +78,7 @@ export default [ "onUpdate": async (value) => { const selfId = await User.selfUserId() - const result = window.app.request.post.updateUser({ + const result = window.app.api.withEndpoints("main").post.updateUser({ _id: selfId, update: { email: value @@ -105,7 +105,7 @@ export default [ "onUpdate": async (value) => { const selfId = await User.selfUserId() - const result = window.app.request.post.updateUser({ + const result = window.app.api.withEndpoints("main").post.updateUser({ _id: selfId, update: { avatar: value @@ -132,7 +132,7 @@ export default [ "onUpdate": async (value) => { const selfId = await User.selfUserId() - const result = window.app.request.post.updateUser({ + const result = window.app.api.withEndpoints("main").post.updateUser({ _id: selfId, update: { cover: value @@ -173,7 +173,7 @@ export default [ "onUpdate": async (value) => { const selfId = await User.selfUserId() - const result = window.app.request.post.updateUser({ + const result = window.app.api.withEndpoints("main").post.updateUser({ _id: selfId, update: { description: value diff --git a/packages/app/src/App.jsx b/packages/app/src/App.jsx index 0ef4bf71..99d5a2e8 100644 --- a/packages/app/src/App.jsx +++ b/packages/app/src/App.jsx @@ -313,12 +313,19 @@ class App extends React.Component { const initializationTasks = [ async () => { try { - await this.props.cores.ApiCore.attachAPIConnection() + // mount main api bridge + await this.props.cores.ApiCore.connectBridge("main", { + locked: true + }) + + // and initialize it + await this.props.cores.ApiCore.namespaces["main"].initialize() app.eventBus.emit("app.initialization.api_success") } catch (error) { app.eventBus.emit("app.initialization.api_error", error) - + console.error(`[App] Error while initializing api`, error) + throw { cause: "Cannot connect to API", details: `Sorry but we cannot connect to the API. Please try again later. [${config.remotes.mainApi}]`, @@ -353,20 +360,6 @@ class App extends React.Component { } } }, - async () => { - try { - await this.__WSInit() - - app.eventBus.emit("app.initialization.ws_success") - } catch (error) { - app.eventBus.emit("app.initialization.ws_error", error) - - throw { - cause: "Cannot connect to WebSocket", - details: error.message, - } - } - }, ] await Promise.tasked(initializationTasks).catch((reason) => { @@ -394,14 +387,6 @@ class App extends React.Component { await this.setState({ session }) } - __WSInit = async () => { - if (!this.state.session) { - return false - } - - await this.props.cores.ApiCore.attachWSConnection() - } - __UserInit = async () => { if (!this.state.session) { return false diff --git a/packages/app/src/components/AdminTools/UserDataManager/index.jsx b/packages/app/src/components/AdminTools/UserDataManager/index.jsx index 8371df59..dee08d90 100644 --- a/packages/app/src/components/AdminTools/UserDataManager/index.jsx +++ b/packages/app/src/components/AdminTools/UserDataManager/index.jsx @@ -71,7 +71,7 @@ export default class UserDataManager extends React.Component { loading: false, } - api = window.app.request + api = window.app.api.withEndpoints("main") componentDidMount = async () => { if (!this.props.user && this.props.userId) { diff --git a/packages/app/src/components/AdminTools/UserRolesManager/index.jsx b/packages/app/src/components/AdminTools/UserRolesManager/index.jsx index 5e411ccc..54ec7e55 100644 --- a/packages/app/src/components/AdminTools/UserRolesManager/index.jsx +++ b/packages/app/src/components/AdminTools/UserRolesManager/index.jsx @@ -12,7 +12,7 @@ export default class UserRolesManager extends React.Component { roles: null, } - api = window.app.request + api = window.app.api.withEndpoints("main") componentDidMount = async () => { await this.fetchRoles() diff --git a/packages/app/src/components/ImageUploader/index.jsx b/packages/app/src/components/ImageUploader/index.jsx index 663ac5a1..cebcf46a 100644 --- a/packages/app/src/components/ImageUploader/index.jsx +++ b/packages/app/src/components/ImageUploader/index.jsx @@ -12,7 +12,7 @@ export default class ImageUploader extends React.Component { urlList: [], } - api = window.app.request + api = window.app.api.withEndpoints("main") handleChange = ({ fileList }) => { this.setState({ fileList }) diff --git a/packages/app/src/components/PostCard/index.jsx b/packages/app/src/components/PostCard/index.jsx index 85e443cc..f0162f11 100644 --- a/packages/app/src/components/PostCard/index.jsx +++ b/packages/app/src/components/PostCard/index.jsx @@ -309,7 +309,7 @@ export const PostCard = React.memo(({ React.useEffect(() => { // first listen to post changes - window.app.ws.listen(`post.dataUpdate.${data._id}`, onDataUpdate) + window.app.api.namespaces["main"].listenEvent(`post.dataUpdate.${data._id}`, onDataUpdate) // proccess post info // {...} @@ -319,7 +319,7 @@ export const PostCard = React.memo(({ return () => { // remove the listener - window.app.ws.unlisten(`post.dataUpdate.${data._id}`, onDataUpdate) + window.app.api.namespaces["main"].unlistenEvent(`post.dataUpdate.${data._id}`, onDataUpdate) } }, []) diff --git a/packages/app/src/components/PostCreator/index.jsx b/packages/app/src/components/PostCreator/index.jsx index 49f8720d..48f974bf 100644 --- a/packages/app/src/components/PostCreator/index.jsx +++ b/packages/app/src/components/PostCreator/index.jsx @@ -12,7 +12,7 @@ import "./index.less" const maxMessageLength = 512 export default (props) => { - const api = window.app.request + const api = window.app.api.withEndpoints("main") const additionsRef = React.useRef(null) const [pending, setPending] = React.useState([]) diff --git a/packages/app/src/components/PostsFeed/index.jsx b/packages/app/src/components/PostsFeed/index.jsx index e6ca886c..2b527419 100644 --- a/packages/app/src/components/PostsFeed/index.jsx +++ b/packages/app/src/components/PostsFeed/index.jsx @@ -30,7 +30,7 @@ export default class PostsFeed extends React.Component { renderList: [], } - api = window.app.request + api = window.app.api.withEndpoints() listRef = React.createRef() @@ -48,7 +48,7 @@ export default class PostsFeed extends React.Component { // load ws events Object.keys(this.wsEvents).forEach((event) => { - window.app.ws.listen(event, this.wsEvents[event]) + window.app.api.namespaces["main"].listenEvent(event, this.wsEvents[event]) }) // TODO: register keybindings to handle directions key scrolling to posts (use app.shortcuts) @@ -67,7 +67,7 @@ export default class PostsFeed extends React.Component { componentWillUnmount = async () => { // unload ws events Object.keys(this.wsEvents).forEach((event) => { - window.app.ws.unlisten(event, this.wsEvents[event]) + window.app.api.namespaces["main"].unlistenEvent(event, this.wsEvents[event]) }) } diff --git a/packages/app/src/components/StepsForm/index.jsx b/packages/app/src/components/StepsForm/index.jsx index 4bde7439..47a7152c 100644 --- a/packages/app/src/components/StepsForm/index.jsx +++ b/packages/app/src/components/StepsForm/index.jsx @@ -18,7 +18,7 @@ export default class StepsForm extends React.Component { renderStep: null, } - api = window.app.request + api = window.app.api.withEndpoints("main") componentDidMount = async () => { if (this.props.defaultValues) { diff --git a/packages/app/src/components/UserRegister/index.jsx b/packages/app/src/components/UserRegister/index.jsx index 5018f5c0..4d2d3df8 100644 --- a/packages/app/src/components/UserRegister/index.jsx +++ b/packages/app/src/components/UserRegister/index.jsx @@ -66,7 +66,7 @@ const steps = [ ] export default (props) => { - const api = window.app.request + const api = window.app.api.withEndpoints("main") const onSubmit = async (values) => { const result = await api.post.register(values).catch((err) => { diff --git a/packages/app/src/components/UserSelector/index.jsx b/packages/app/src/components/UserSelector/index.jsx index 71c2f77b..2cca9e25 100644 --- a/packages/app/src/components/UserSelector/index.jsx +++ b/packages/app/src/components/UserSelector/index.jsx @@ -14,7 +14,7 @@ export default class UserSelector extends React.Component { searchValue: null, } - api = window.app.request + api = window.app.api.withEndpoints("main") componentDidMount = async () => { this.toogleLoading(true) diff --git a/packages/app/src/cores/api/index.js b/packages/app/src/cores/api/index.js index 3b10175c..4d48d2d2 100644 --- a/packages/app/src/cores/api/index.js +++ b/packages/app/src/cores/api/index.js @@ -7,50 +7,40 @@ export default class ApiCore extends Core { constructor(props) { super(props) - this.apiBridge = this.createBridge() + this.namespaces = Object() - this.WSInterface = { - ...this.apiBridge.wsInterface, - request: this.WSRequest, - listen: this.listenEvent, - unlisten: this.unlistenEvent, - mainSocketConnected: false + this.ctx.registerPublicMethod("api", this) + } + + request = (namespace = "main", method, endpoint, ...args) => { + if (!this.namespaces[namespace]) { + throw new Error(`Namespace ${namespace} not found`) } - this.ctx.registerPublicMethod("api", this.apiBridge) - this.ctx.registerPublicMethod("ws", this.WSInterface) - this.ctx.registerPublicMethod("request", this.apiBridge.endpoints) - this.ctx.registerPublicMethod("WSRequest", this.WSInterface.wsEndpoints) + if (!this.namespaces[namespace].endpoints[method]) { + throw new Error(`Method ${method} not found`) + } + + if (!this.namespaces[namespace].endpoints[method][endpoint]) { + throw new Error(`Endpoint ${endpoint} not found`) + } + + return this.namespaces[namespace].endpoints[method][endpoint](...args) } - async intialize() { - this.WSInterface.sockets.main.on("authenticated", () => { - console.debug("[WS] Authenticated") - }) - this.WSInterface.sockets.main.on("authenticateFailed", (error) => { - console.error("[WS] Authenticate Failed", error) - }) + withEndpoints = (namespace = "main") => { + if (!this.namespaces[namespace]) { + throw new Error(`Namespace ${namespace} not found`) + } - this.WSInterface.sockets.main.on("connect", () => { - this.ctx.eventBus.emit("websocket_connected") - - this.WSInterface.mainSocketConnected = true - }) - - this.WSInterface.sockets.main.on("disconnect", (...context) => { - this.ctx.eventBus.emit("websocket_disconnected", ...context) - - this.WSInterface.mainSocketConnected = false - }) - - this.WSInterface.sockets.main.on("connect_error", (...context) => { - this.ctx.eventBus.emit("websocket_connection_error", ...context) - - this.WSInterface.mainSocketConnected = false - }) + return this.namespaces[namespace].endpoints } - createBridge() { + connectBridge = (key, params) => { + this.namespaces[key] = this.createBridge(params) + } + + createBridge(params = {}) { const getSessionContext = async () => { const obj = {} const token = await Session.token @@ -80,111 +70,103 @@ export default class ApiCore extends Core { } } - return new Bridge({ - origin: config.remotes.mainApi, - wsOrigin: config.remotes.websocketApi, + if (typeof params !== "object") { + throw new Error("Params must be an object") + } + + const bridgeOptions = { wsOptions: { autoConnect: false, }, onRequest: getSessionContext, onResponse: handleResponse, - }) - } - - async attachWSConnection() { - if (!this.WSInterface.sockets.main.connected) { - await this.WSInterface.sockets.main.connect() + ...params, + origin: params.httpAddress ?? config.remotes.mainApi, + wsOrigin: params.wsAddress ?? config.remotes.websocketApi, } - let startTime = null - let latency = null - let latencyWarning = false + const bridge = new Bridge(bridgeOptions) - let pingInterval = setInterval(() => { - if (!this.WSInterface.mainSocketConnected) { - return clearTimeout(pingInterval) + // handle main ws events + const mainWSSocket = bridge.wsInterface.sockets["main"] + + mainWSSocket.on("authenticated", () => { + console.debug("[WS] Authenticated") + }) + + mainWSSocket.on("authenticateFailed", (error) => { + console.error("[WS] Authenticate Failed", error) + }) + + mainWSSocket.on("connect", () => { + this.ctx.eventBus.emit(`api.ws.${mainWSSocket.id}.connect`) + }) + + mainWSSocket.on("disconnect", (...context) => { + this.ctx.eventBus.emit(`api.ws.${mainWSSocket.id}.disconnect`, ...context) + }) + + mainWSSocket.on("connect_error", (...context) => { + this.ctx.eventBus.emit(`api.ws.${mainWSSocket.id}.connect_error`, ...context) + }) + + // generate functions + bridge.listenEvent = this.generateMainWSEventListener(bridge.wsInterface) + bridge.unlistenEvent = this.generateMainWSEventUnlistener(bridge.wsInterface) + + // return bridge + return bridge + } + + generateMainWSEventListener(obj) { + return (to, fn) => { + if (typeof to === "undefined") { + console.error("handleWSListener: to must be defined") + return false + } + if (typeof fn !== "function") { + console.error("handleWSListener: fn must be function") + return false } - startTime = Date.now() - this.WSInterface.sockets.main.emit("ping") - }, 2000) + let ns = "main" + let event = null - this.WSInterface.sockets.main.on("pong", () => { - latency = Date.now() - startTime - - if (latency > 800 && this.WSInterface.mainSocketConnected) { - latencyWarning = true - console.error("[WS] Latency is too high > 800ms", latency) - window.app.eventBus.emit("websocket_latency_too_high", latency) - } else if (latencyWarning && this.WSInterface.mainSocketConnected) { - latencyWarning = false - window.app.eventBus.emit("websocket_latency_normal", latency) + if (typeof to === "string") { + event = to + } else if (typeof to === "object") { + ns = to.ns + event = to.event } - }) - } - async attachAPIConnection() { - await this.apiBridge.initialize() - } - - listenEvent = (to, fn) => { - if (typeof to === "undefined") { - console.error("handleWSListener: to must be defined") - return false - } - if (typeof fn !== "function") { - console.error("handleWSListener: fn must be function") - return false - } - - let ns = "main" - let event = null - - if (typeof to === "string") { - event = to - } else if (typeof to === "object") { - ns = to.ns - event = to.event - } - - return window.app.ws.sockets[ns].on(event, async (...context) => { - return await fn(...context) - }) - } - - unlistenEvent = (to, fn) => { - if (typeof to === "undefined") { - console.error("handleWSListener: to must be defined") - return false - } - if (typeof fn !== "function") { - console.error("handleWSListener: fn must be function") - return false - } - - let ns = "main" - let event = null - - if (typeof to === "string") { - event = to - } else if (typeof to === "object") { - ns = to.ns - event = to.event - } - - return window.app.ws.sockets[ns].removeListener(event, fn) - } - - WSRequest = (socket = "main", channel, ...args) => { - return new Promise(async (resolve, reject) => { - const request = await window.app.ws.sockets[socket].emit(channel, ...args) - - request.on("responseError", (...errors) => { - return reject(...errors) + return obj.sockets[ns].on(event, async (...context) => { + return await fn(...context) }) - request.on("response", (...responses) => { - return resolve(...responses) - }) - }) + } + } + + generateMainWSEventUnlistener(obj) { + return (to, fn) => { + if (typeof to === "undefined") { + console.error("handleWSListener: to must be defined") + return false + } + if (typeof fn !== "function") { + console.error("handleWSListener: fn must be function") + return false + } + + let ns = "main" + let event = null + + if (typeof to === "string") { + event = to + } else if (typeof to === "object") { + ns = to.ns + event = to.event + } + + return obj.sockets[ns].removeListener(event, fn) + } } } \ No newline at end of file diff --git a/packages/app/src/models/session/index.js b/packages/app/src/models/session/index.js index a6199df9..bcf43612 100644 --- a/packages/app/src/models/session/index.js +++ b/packages/app/src/models/session/index.js @@ -5,7 +5,7 @@ import { Storage } from '@capacitor/storage' export default class Session { static get bridge() { - return window.app?.request + return window.app?.api.withEndpoints("main") } static capStorage = async (method, value) => { diff --git a/packages/app/src/models/user/index.js b/packages/app/src/models/user/index.js index dda14f57..a50aee72 100644 --- a/packages/app/src/models/user/index.js +++ b/packages/app/src/models/user/index.js @@ -2,7 +2,7 @@ import Session from "../session" export default class User { static get bridge() { - return window.app?.request + return window.app?.api.withEndpoints("main") } static async data() { diff --git a/packages/app/src/pages/account/index.jsx b/packages/app/src/pages/account/index.jsx index 3f513ed0..dd006990 100644 --- a/packages/app/src/pages/account/index.jsx +++ b/packages/app/src/pages/account/index.jsx @@ -82,7 +82,7 @@ export default class Account extends React.Component { isNotExistent: false, } - api = window.app.request + api = window.app.api.withEndpoints("main") componentDidMount = async () => { const token = await Session.decodedToken() diff --git a/packages/app/src/pages/live/[key].jsx b/packages/app/src/pages/live/[key].jsx index 7693b60f..8328c1b2 100644 --- a/packages/app/src/pages/live/[key].jsx +++ b/packages/app/src/pages/live/[key].jsx @@ -136,7 +136,7 @@ export default class StreamViewer extends React.Component { } gatherStreamInfo = async () => { - const result = await app.request.get.streamInfoFromUsername(undefined, { + const result = await app.api.withEndpoints("main").get.streamInfoFromUsername(undefined, { username: this.state.streamKey, }).catch((error) => { console.error(error) @@ -152,7 +152,7 @@ export default class StreamViewer extends React.Component { } gatherUserInfo = async () => { - const result = await app.request.get.user(undefined, { + const result = await app.api.withEndpoints("main").get.user(undefined, { username: this.state.streamKey, }).catch((error) => { console.error(error) diff --git a/packages/app/src/pages/streaming_control/index.jsx b/packages/app/src/pages/streaming_control/index.jsx index 5cb68313..c54a38e9 100644 --- a/packages/app/src/pages/streaming_control/index.jsx +++ b/packages/app/src/pages/streaming_control/index.jsx @@ -35,7 +35,7 @@ export default (props) => { const [serverTier, setServerTier] = React.useState(null) const checkStreamingKey = async () => { - const result = await app.request.get.streamingKey().catch((error) => { + const result = await app.api.withEndpoints("main").get.streamingKey().catch((error) => { console.error(error) antd.message.error(error.message) @@ -48,7 +48,7 @@ export default (props) => { } const checkTagetServer = async () => { - const result = await app.request.get.targetStreamingServer() + const result = await app.api.withEndpoints("main").get.targetStreamingServer() if (result) { const targetServer = `${result.protocol}://${result.address}:${result.port}/${result.space}` @@ -61,7 +61,7 @@ export default (props) => { title: "Regenerate streaming key", content: "Are you sure you want to regenerate the streaming key? After this, all other generated keys will be deleted.", onOk: async () => { - const result = await app.request.post.regenerateStreamingKey().catch((error) => { + const result = await app.api.withEndpoints("main").post.regenerateStreamingKey().catch((error) => { console.error(error) antd.message.error(error.message) diff --git a/packages/app/src/pages/streams/index.jsx b/packages/app/src/pages/streams/index.jsx index 2c00f82a..9471fef9 100644 --- a/packages/app/src/pages/streams/index.jsx +++ b/packages/app/src/pages/streams/index.jsx @@ -10,7 +10,7 @@ export default class Streams extends React.Component { list: [], } - api = window.app.request + api = window.app.api.withEndpoints("main") componentDidMount = async () => { await this.updateStreamsList() diff --git a/packages/app/src/pages/users/index.jsx b/packages/app/src/pages/users/index.jsx index 0dabab41..bf1939d1 100644 --- a/packages/app/src/pages/users/index.jsx +++ b/packages/app/src/pages/users/index.jsx @@ -11,7 +11,7 @@ export default class Users extends React.Component { selectionEnabled: false, } - api = window.app.request + api = window.app.api.withEndpoints("main") componentDidMount = async () => { await this.loadData()