reimplement api, with namespaces

This commit is contained in:
srgooglo 2022-08-04 12:46:20 +02:00
parent 8d42494f10
commit a09b79aaea
19 changed files with 147 additions and 180 deletions

View File

@ -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

View File

@ -313,11 +313,18 @@ 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",
@ -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

View File

@ -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) {

View File

@ -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()

View File

@ -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 })

View File

@ -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)
}
}, [])

View File

@ -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([])

View File

@ -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])
})
}

View File

@ -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) {

View File

@ -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) => {

View File

@ -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)

View File

@ -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)
}
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)
request = (namespace = "main", method, endpoint, ...args) => {
if (!this.namespaces[namespace]) {
throw new Error(`Namespace ${namespace} not found`)
}
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)
})
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
})
if (!this.namespaces[namespace].endpoints[method]) {
throw new Error(`Method ${method} not found`)
}
createBridge() {
if (!this.namespaces[namespace].endpoints[method][endpoint]) {
throw new Error(`Endpoint ${endpoint} not found`)
}
return this.namespaces[namespace].endpoints[method][endpoint](...args)
}
withEndpoints = (namespace = "main") => {
if (!this.namespaces[namespace]) {
throw new Error(`Namespace ${namespace} not found`)
}
return this.namespaces[namespace].endpoints
}
connectBridge = (key, params) => {
this.namespaces[key] = this.createBridge(params)
}
createBridge(params = {}) {
const getSessionContext = async () => {
const obj = {}
const token = await Session.token
@ -80,54 +70,56 @@ 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,
...params,
origin: params.httpAddress ?? config.remotes.mainApi,
wsOrigin: params.wsAddress ?? config.remotes.websocketApi,
}
const bridge = new Bridge(bridgeOptions)
// handle main ws events
const mainWSSocket = bridge.wsInterface.sockets["main"]
mainWSSocket.on("authenticated", () => {
console.debug("[WS] Authenticated")
})
}
async attachWSConnection() {
if (!this.WSInterface.sockets.main.connected) {
await this.WSInterface.sockets.main.connect()
}
let startTime = null
let latency = null
let latencyWarning = false
let pingInterval = setInterval(() => {
if (!this.WSInterface.mainSocketConnected) {
return clearTimeout(pingInterval)
}
startTime = Date.now()
this.WSInterface.sockets.main.emit("ping")
}, 2000)
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)
}
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
}
async attachAPIConnection() {
await this.apiBridge.initialize()
}
listenEvent = (to, fn) => {
generateMainWSEventListener(obj) {
return (to, fn) => {
if (typeof to === "undefined") {
console.error("handleWSListener: to must be defined")
return false
@ -147,12 +139,14 @@ export default class ApiCore extends Core {
event = to.event
}
return window.app.ws.sockets[ns].on(event, async (...context) => {
return obj.sockets[ns].on(event, async (...context) => {
return await fn(...context)
})
}
}
unlistenEvent = (to, fn) => {
generateMainWSEventUnlistener(obj) {
return (to, fn) => {
if (typeof to === "undefined") {
console.error("handleWSListener: to must be defined")
return false
@ -172,19 +166,7 @@ export default class ApiCore extends Core {
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)
})
request.on("response", (...responses) => {
return resolve(...responses)
})
})
return obj.sockets[ns].removeListener(event, fn)
}
}
}

View File

@ -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) => {

View File

@ -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() {

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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()