General Actions to handlers Refacto
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import { dispatchActions } from './dispatchActions.js'
|
||||
import { dispatchEvents } from './dispatchEvents.js'
|
||||
|
||||
export function assembleHandlers(modules) {
|
||||
const actions = {}
|
||||
const tree = {}
|
||||
const afterLogin = []
|
||||
|
||||
for(const mod of modules) {
|
||||
if(mod.actions) Object.assign(actions, mod.actions)
|
||||
if(typeof(mod.construct) === 'function') afterLogin.push(mod.construct)
|
||||
if(!mod.eventHandlers) continue
|
||||
for(const [channelPattern, byType] of Object.entries(mod.eventHandlers)) {
|
||||
if(!tree[channelPattern]) tree[channelPattern] = {}
|
||||
for(const [eventType, handler] of Object.entries(byType)) {
|
||||
if(!tree[channelPattern][eventType]) tree[channelPattern][eventType] = []
|
||||
const list = Array.isArray(handler) ? handler : [handler]
|
||||
tree[channelPattern][eventType].push(...list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return({
|
||||
actionHandlers: actions,
|
||||
eventHandlers: tree,
|
||||
afterLogin,
|
||||
})
|
||||
}
|
||||
|
||||
export function createDispatchMessage({ eventHandlers, actionRules }) {
|
||||
return(async function dispatchMessage(redisCnx, msg, chan) {
|
||||
if(msg.action && msg.eventType) {
|
||||
console.warn(`[${redisCnx.redisId}] Message has both action and eventType on ${chan}`)
|
||||
return(false)
|
||||
}
|
||||
if(msg.action) return(dispatchActions(redisCnx, msg, chan, actionRules(redisCnx)))
|
||||
if(msg.eventType) return(dispatchEvents(redisCnx, msg, chan, eventHandlers))
|
||||
return(false)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { replyToAction } from './publishActionReply.js'
|
||||
|
||||
function matchesActionsChannel(redisCnx, chan, channels) {
|
||||
if(!Array.isArray(channels) || !channels.length) return(false)
|
||||
for(const configured of channels) {
|
||||
if(!configured) continue
|
||||
if(redisCnx.fullChan(configured) === chan) return(true)
|
||||
}
|
||||
return(false)
|
||||
}
|
||||
|
||||
export async function dispatchActions(redisCnx, msg, chan, rules) {
|
||||
if(!matchesActionsChannel(redisCnx, chan, rules.channels)) return(false)
|
||||
|
||||
const action = msg.action
|
||||
const sender = msg.sender ?? null
|
||||
const reqid = ('reqid' in msg) ? msg.reqid.substr(0, 50) : null
|
||||
const roles = Array.isArray(msg.roles) ? msg.roles : ['*']
|
||||
|
||||
if(!action || typeof(action) !== 'string') {
|
||||
if(!sender) return(true)
|
||||
replyToAction(redisCnx, {
|
||||
action,
|
||||
reqid,
|
||||
sender,
|
||||
success: false,
|
||||
err: 'Missing or invalid action',
|
||||
})
|
||||
return(true)
|
||||
}
|
||||
|
||||
if(!sender) {
|
||||
console.warn(`[${redisCnx.redisId}] Action ${action} without sender on ${chan}`)
|
||||
return(true)
|
||||
}
|
||||
|
||||
if(redisCnx.accessRights && !redisCnx.accessRights.canDo(roles, action, sender)) {
|
||||
replyToAction(redisCnx, {
|
||||
action,
|
||||
reqid,
|
||||
sender,
|
||||
success: false,
|
||||
err: 'Unauthorized action !',
|
||||
})
|
||||
return(true)
|
||||
}
|
||||
|
||||
const handler = redisCnx['action_'+action]
|
||||
if(typeof(handler) !== 'function') {
|
||||
replyToAction(redisCnx, {
|
||||
action,
|
||||
reqid,
|
||||
sender,
|
||||
success: false,
|
||||
err: `Unknown action: ${action}`,
|
||||
})
|
||||
return(true)
|
||||
}
|
||||
|
||||
if(redisCnx.debug) {
|
||||
console.log(`[${redisCnx.redisId}] Dispatching action ${action} from ${sender}`)
|
||||
}
|
||||
|
||||
try {
|
||||
await handler.call(redisCnx, action, ('payload' in msg) ? msg.payload : null, reqid, sender, roles)
|
||||
} catch(err) {
|
||||
console.error(`[${redisCnx.redisId}] Action ${action} failed:`, err)
|
||||
replyToAction(redisCnx, {
|
||||
action,
|
||||
reqid,
|
||||
sender,
|
||||
success: false,
|
||||
err: err.message ?? `${action} failed`,
|
||||
})
|
||||
}
|
||||
|
||||
return(true)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
export function dispatchEvents(redisCnx, msg, chan, eventHandlers) {
|
||||
const eventType = msg.eventType
|
||||
if(!eventType || typeof(eventType) !== 'string') return(false)
|
||||
|
||||
let handled = false
|
||||
|
||||
for(const [channelPattern, byType] of Object.entries(eventHandlers ?? {})) {
|
||||
if(!redisCnx.matchesChan(chan, channelPattern)) continue
|
||||
|
||||
const handlers = byType[eventType]
|
||||
if(!handlers?.length) continue
|
||||
|
||||
for(const handle of handlers) {
|
||||
try {
|
||||
handle.call(redisCnx, msg, chan)
|
||||
} catch(err) {
|
||||
console.error(
|
||||
`[${redisCnx.redisId}] Event ${eventType} on ${chan} failed:`,
|
||||
err
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
handled = true
|
||||
}
|
||||
|
||||
if(!handled && redisCnx.debug) {
|
||||
console.log(`[${redisCnx.redisId}] Unhandled event ${eventType} on ${chan}`)
|
||||
}
|
||||
|
||||
return(handled)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
|
||||
export function busReplyRoute(daemonBlock, meshName) {
|
||||
if(!daemonBlock?.senderId) return(null)
|
||||
|
||||
const onArena = meshName === 'arena'
|
||||
const systemReply = daemonBlock.maestroActionsReply
|
||||
?? daemonBlock.gpsActionsReply
|
||||
?? daemonBlock.observerActionsReply
|
||||
const actionsReply = onArena
|
||||
? (daemonBlock.bus?.arena?.actionsReply ?? systemReply)
|
||||
: systemReply
|
||||
|
||||
if(!actionsReply) return(null)
|
||||
|
||||
return({
|
||||
senderId: daemonBlock.senderId,
|
||||
actionsReply,
|
||||
})
|
||||
}
|
||||
|
||||
export function publishActionReply(redisCnx, options) {
|
||||
const {
|
||||
action,
|
||||
reqid,
|
||||
sender,
|
||||
reply,
|
||||
replyChannel,
|
||||
senderId,
|
||||
} = options
|
||||
reply.action = action
|
||||
reply.sender = senderId
|
||||
if(reqid) reply.reqid = reqid
|
||||
const chan = replyChannel.replace(/\[UID\]/g, sender)
|
||||
redisCnx.redisPublish(chan, reply)
|
||||
}
|
||||
|
||||
export function replyToAction(redisCnx, options) {
|
||||
const {
|
||||
action,
|
||||
reqid,
|
||||
sender,
|
||||
success,
|
||||
payload,
|
||||
err,
|
||||
replyChannel,
|
||||
senderId,
|
||||
} = options
|
||||
|
||||
const routeReplyChannel = replyChannel ?? redisCnx.actionsReply
|
||||
const routeSenderId = senderId ?? redisCnx.senderId
|
||||
|
||||
if(!routeReplyChannel || !routeSenderId) {
|
||||
console.error(`[${redisCnx.redisId}] Cannot resolve action reply route`)
|
||||
return
|
||||
}
|
||||
|
||||
const reply = { success }
|
||||
if(err != null) reply.err = err
|
||||
if(payload !== undefined) reply.payload = payload
|
||||
|
||||
publishActionReply(redisCnx, {
|
||||
action,
|
||||
reqid,
|
||||
sender,
|
||||
replyChannel: routeReplyChannel,
|
||||
senderId: routeSenderId,
|
||||
reply,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user