General Actions to handlers Refacto

This commit is contained in:
STEINNI
2026-06-20 18:50:26 +00:00
parent 7435d96135
commit 44a84c64ec
56 changed files with 832 additions and 973 deletions
+40
View File
@@ -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)
})
}
+78
View File
@@ -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)
}
+33
View File
@@ -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)
}
+69
View File
@@ -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,
})
}