Phase 896: export mesh gossip-wait + permission helper extract (v0.8.863)
Non-breaking refactor — gossip-rpc-export-mesh-gossip-wait.js dedupes gossip waiter/timeout orchestration; assertGuildExportMeshFetchPermission() centralizes MANAGE_GUILD gate; audit/digest mesh fetch mixins adopt both helpers. Default gossip v1 unchanged; fleet v2 cutover still deferred. Verification: test:ci-phase896, pear run boot, pearcord.log scan.
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
const { deliveryScope, sharedScope, id } = require('../../../platform-class-imports')
|
||||
const { deliveryScope } = require('../../../platform-class-imports')
|
||||
const {
|
||||
markRpcResponse,
|
||||
fetchGuildGossipRpcThenMeshWait,
|
||||
runGuildExportMeshFetchWithRetry
|
||||
runGuildExportMeshFetchWithRetry,
|
||||
assertGuildExportMeshFetchPermission,
|
||||
awaitGuildExportMeshGossipWait
|
||||
} = require('../../../gossip-rpc-pull')
|
||||
|
||||
/** Audit export archive mesh fetch + gossip waiters (Phase 895). */
|
||||
/** Audit export archive mesh fetch + gossip waiters (Phase 895/896). */
|
||||
const platformAuditExportMeshFetchMixin = {
|
||||
_resolveAuditArchiveFetchWaiter (archive) {
|
||||
if (!archive?.id || !archive.entries?.length) return
|
||||
@@ -20,11 +22,10 @@ const platformAuditExportMeshFetchMixin = {
|
||||
},
|
||||
|
||||
async fetchAuditExportArchiveFromMesh (archiveId, opts = {}) {
|
||||
if (!this.guild?.guild) throw new Error('no guild')
|
||||
const roles = await this._memberRoles()
|
||||
if (!sharedScope.roleHasPermission(roles, sharedScope.PERMISSION.MANAGE_GUILD)) {
|
||||
throw new Error('no permission to fetch audit export archive')
|
||||
}
|
||||
await assertGuildExportMeshFetchPermission(
|
||||
this,
|
||||
'no permission to fetch audit export archive'
|
||||
)
|
||||
return runGuildExportMeshFetchWithRetry(
|
||||
(id, o) => this._fetchAuditExportArchiveFromMeshOnce(id, o),
|
||||
archiveId,
|
||||
@@ -55,43 +56,33 @@ const platformAuditExportMeshFetchMixin = {
|
||||
fetchAttempt: (Number(opts.attempt) || 0) + 1
|
||||
})
|
||||
},
|
||||
meshWait: async () => {
|
||||
const timeoutMs = Math.min(12000, Math.max(800, Number(opts.timeoutMs) || 3500))
|
||||
const requestId = id()
|
||||
const waitPromise = new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
this._auditArchiveFetchWaiters.delete(archiveId)
|
||||
reject(new Error('audit archive fetch timed out'))
|
||||
}, timeoutMs)
|
||||
const handlers = this._auditArchiveFetchWaiters.get(archiveId) || []
|
||||
handlers.push((arch) => {
|
||||
clearTimeout(timer)
|
||||
resolve(arch)
|
||||
})
|
||||
this._auditArchiveFetchWaiters.set(archiveId, handlers)
|
||||
})
|
||||
if (this.guild.gossipAuditExportArchiveRequest) {
|
||||
this.guild.gossipAuditExportArchiveRequest({
|
||||
guildId: this.guild.guild.id,
|
||||
archiveId,
|
||||
requestId,
|
||||
requestedBy: this.identity.user?.id || null,
|
||||
targetMemberId: opts.targetMemberId || null,
|
||||
at: Date.now(),
|
||||
attempt: Number(opts.attempt) || 0
|
||||
})
|
||||
meshWait: () => awaitGuildExportMeshGossipWait({
|
||||
resourceId: archiveId,
|
||||
opts,
|
||||
waiters: this._auditArchiveFetchWaiters,
|
||||
waiterMode: 'append',
|
||||
timeoutErrorMessage: 'audit archive fetch timed out',
|
||||
notFoundMessage: 'audit export archive not found on mesh',
|
||||
sendGossipRequest: (requestId) => {
|
||||
if (this.guild.gossipAuditExportArchiveRequest) {
|
||||
this.guild.gossipAuditExportArchiveRequest({
|
||||
guildId: this.guild.guild.id,
|
||||
archiveId,
|
||||
requestId,
|
||||
requestedBy: this.identity.user?.id || null,
|
||||
targetMemberId: opts.targetMemberId || null,
|
||||
at: Date.now(),
|
||||
attempt: Number(opts.attempt) || 0
|
||||
})
|
||||
}
|
||||
},
|
||||
refetchLocal: () => this.deliveryReceipts.getAuditExportArchive(archiveId),
|
||||
validateLocal: (r) => Boolean(r?.entries?.length),
|
||||
formatResult: async (fetched) => {
|
||||
const out = await this.exportAuditExportArchive(fetched.id || archiveId, opts)
|
||||
return { ...out, fetchAttempt: (Number(opts.attempt) || 0) + 1 }
|
||||
}
|
||||
const fetched = await waitPromise.catch(async () => {
|
||||
row = await this.deliveryReceipts.getAuditExportArchive(archiveId)
|
||||
if (row?.entries?.length) return row
|
||||
throw new Error('audit export archive not found on mesh')
|
||||
})
|
||||
if (!fetched?.entries?.length) {
|
||||
throw new Error('audit export archive not found on mesh')
|
||||
}
|
||||
const out = await this.exportAuditExportArchive(fetched.id || archiveId, opts)
|
||||
return { ...out, fetchAttempt: (Number(opts.attempt) || 0) + 1 }
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
return rpcArch
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
const { deliveryScope, sharedScope, id } = require('../../../platform-class-imports')
|
||||
const { deliveryScope } = require('../../../platform-class-imports')
|
||||
const {
|
||||
markRpcResponse,
|
||||
fetchGuildGossipRpcThenMeshWait,
|
||||
runGuildExportMeshFetchWithRetry
|
||||
runGuildExportMeshFetchWithRetry,
|
||||
assertGuildExportMeshFetchPermission,
|
||||
awaitGuildExportMeshGossipWait
|
||||
} = require('../../../gossip-rpc-pull')
|
||||
|
||||
/** Automation digest snapshot mesh fetch + gossip waiters (Phase 895). */
|
||||
/** Automation digest snapshot mesh fetch + gossip waiters (Phase 895/896). */
|
||||
const platformAutomationDigestMeshFetchMixin = {
|
||||
_resolveAutomationDigestSnapshotFetchWaiter (snap) {
|
||||
if (!snap?.id || !snap.body?.length) return
|
||||
@@ -18,11 +20,10 @@ const platformAutomationDigestMeshFetchMixin = {
|
||||
},
|
||||
|
||||
async fetchAutomationDigestSnapshotFromMesh (snapshotId, opts = {}) {
|
||||
if (!this.guild?.guild) throw new Error('no guild')
|
||||
const roles = await this._memberRoles()
|
||||
if (!sharedScope.roleHasPermission(roles, sharedScope.PERMISSION.MANAGE_GUILD)) {
|
||||
throw new Error('no permission to fetch automation digest snapshot')
|
||||
}
|
||||
await assertGuildExportMeshFetchPermission(
|
||||
this,
|
||||
'no permission to fetch automation digest snapshot'
|
||||
)
|
||||
return runGuildExportMeshFetchWithRetry(
|
||||
(id, o) => this._fetchAutomationDigestSnapshotFromMeshOnce(id, o),
|
||||
snapshotId,
|
||||
@@ -62,48 +63,36 @@ const platformAutomationDigestMeshFetchMixin = {
|
||||
fetchAttempt: (Number(opts.attempt) || 0) + 1
|
||||
})
|
||||
},
|
||||
meshWait: async () => {
|
||||
const timeoutMs = Math.min(12000, Math.max(800, Number(opts.timeoutMs) || 3500))
|
||||
const requestId = id()
|
||||
const waitPromise = new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
this._automationDigestSnapshotFetchWaiters.delete(snapshotId)
|
||||
reject(new Error('automation digest snapshot fetch timed out'))
|
||||
}, timeoutMs)
|
||||
this._automationDigestSnapshotFetchWaiters.set(snapshotId, [
|
||||
(snap) => {
|
||||
clearTimeout(timer)
|
||||
resolve(snap)
|
||||
}
|
||||
])
|
||||
})
|
||||
if (this.guild.gossipAutomationDigestExportSnapshotRequest) {
|
||||
this.guild.gossipAutomationDigestExportSnapshotRequest({
|
||||
guildId: this.guild.guild.id,
|
||||
snapshotId,
|
||||
requestId,
|
||||
requestedBy: this.identity.user?.id || null,
|
||||
targetMemberId: opts.targetMemberId || null,
|
||||
at: Date.now()
|
||||
})
|
||||
}
|
||||
const fetched = await waitPromise.catch(async () => {
|
||||
row = await this.deliveryReceipts.getAutomationDigestExportSnapshot(snapshotId)
|
||||
if (row?.body?.length) return row
|
||||
throw new Error('automation digest snapshot not found on mesh')
|
||||
})
|
||||
if (!fetched?.body?.length) {
|
||||
throw new Error('automation digest snapshot not found on mesh')
|
||||
}
|
||||
return {
|
||||
meshWait: () => awaitGuildExportMeshGossipWait({
|
||||
resourceId: snapshotId,
|
||||
opts,
|
||||
waiters: this._automationDigestSnapshotFetchWaiters,
|
||||
waiterMode: 'replace',
|
||||
timeoutErrorMessage: 'automation digest snapshot fetch timed out',
|
||||
notFoundMessage: 'automation digest snapshot not found on mesh',
|
||||
sendGossipRequest: (requestId) => {
|
||||
if (this.guild.gossipAutomationDigestExportSnapshotRequest) {
|
||||
this.guild.gossipAutomationDigestExportSnapshotRequest({
|
||||
guildId: this.guild.guild.id,
|
||||
snapshotId,
|
||||
requestId,
|
||||
requestedBy: this.identity.user?.id || null,
|
||||
targetMemberId: opts.targetMemberId || null,
|
||||
at: Date.now()
|
||||
})
|
||||
}
|
||||
},
|
||||
refetchLocal: () => this.deliveryReceipts.getAutomationDigestExportSnapshot(snapshotId),
|
||||
validateLocal: (r) => Boolean(r?.body?.length),
|
||||
formatResult: (fetched) => ({
|
||||
snapshotId: fetched.id,
|
||||
format: fetched.format,
|
||||
body: fetched.body,
|
||||
summaryLines: fetched.summaryLines,
|
||||
exportedAt: fetched.exportedAt,
|
||||
fetchAttempt: (Number(opts.attempt) || 0) + 1
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
return rpcSnap
|
||||
|
||||
Reference in New Issue
Block a user