feat(platform): Phase 102 archive fetch, dashboard, trial asserts
- fetchAuditExportArchiveFromMesh via RPC 72 request/response waiters - Respond to audit-export-archive-request when local archive has entries - getAutomationScheduleDashboard on view state - runIntegrationWorkerTrial fans out MESSAGE_CREATE and requires sawMessageCreate - Merge archive ingest resolves pending mesh fetch waiters Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -46,6 +46,7 @@ const {
|
||||
formatAuditExport,
|
||||
formatHookFailureDigestExport,
|
||||
mergeHookFailureDigests,
|
||||
buildAutomationScheduleDashboard,
|
||||
HookFailureAlertStore
|
||||
} = require('pearcord-delivery-receipts')
|
||||
const {
|
||||
@@ -255,6 +256,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
this.hookFailureAlerts = null
|
||||
this.workerReleaseHints = null
|
||||
this._auditScheduleTimer = null
|
||||
this._auditArchiveFetchWaiters = new Map()
|
||||
this.appVersion = opts.appVersion || process.env.PEARCORD_APP_VERSION || '0.0.0'
|
||||
this.discovery = null
|
||||
this.contacts = null
|
||||
@@ -1614,6 +1616,9 @@ class PearcordPlatform extends EventEmitter {
|
||||
guildInstance.on('audit-export-archive', (payload) => {
|
||||
this._onAuditExportArchiveGossip(payload).catch(() => {})
|
||||
})
|
||||
guildInstance.on('audit-export-archive-request', (payload) => {
|
||||
this._onAuditExportArchiveRequestGossip(payload).catch(() => {})
|
||||
})
|
||||
guildInstance.on('audit', async (p) => {
|
||||
if (p?.guildId && p?.id) {
|
||||
const existing = await this.db.get(COLLECTIONS.AUDIT_LOG, { id: p.id })
|
||||
@@ -2018,7 +2023,10 @@ class PearcordPlatform extends EventEmitter {
|
||||
if (this.guild?.guild?.id !== payload.guildId) return null
|
||||
await this._initDeliveryReceipts(payload.guildId)
|
||||
const row = await this.deliveryReceipts.ingestAuditExportArchive(payload)
|
||||
if (row) this.emit('audit-export-archive', row)
|
||||
if (row) {
|
||||
this._resolveAuditArchiveFetchWaiter(row)
|
||||
this.emit('audit-export-archive', row)
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
@@ -2028,6 +2036,70 @@ class PearcordPlatform extends EventEmitter {
|
||||
return this.deliveryReceipts.listAuditExportArchives(limit)
|
||||
}
|
||||
|
||||
async _onAuditExportArchiveRequestGossip (payload) {
|
||||
if (!payload?.guildId || !payload?.archiveId) return null
|
||||
if (this.guild?.guild?.id !== payload.guildId) return null
|
||||
await this._initDeliveryReceipts(payload.guildId)
|
||||
const row = await this.deliveryReceipts.getAuditExportArchive(payload.archiveId)
|
||||
if (!row?.entries?.length) return null
|
||||
if (this.guild.gossipAuditExportArchive) {
|
||||
this.guild.gossipAuditExportArchive(row)
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
_resolveAuditArchiveFetchWaiter (archive) {
|
||||
if (!archive?.id) return
|
||||
for (const [key, handlers] of this._auditArchiveFetchWaiters) {
|
||||
if (key === archive.id || key.startsWith(`${archive.id}:`)) {
|
||||
for (const fn of handlers) fn(archive)
|
||||
this._auditArchiveFetchWaiters.delete(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fetchAuditExportArchiveFromMesh (archiveId, opts = {}) {
|
||||
if (!this.guild?.guild) throw new Error('no guild')
|
||||
const roles = await this._memberRoles()
|
||||
if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) {
|
||||
throw new Error('no permission to fetch audit export archive')
|
||||
}
|
||||
await this._initDeliveryReceipts(this.guild.guild.id)
|
||||
let row = await this.deliveryReceipts.getAuditExportArchive(archiveId)
|
||||
if (row?.entries?.length) {
|
||||
return this.exportAuditExportArchive(archiveId, opts)
|
||||
}
|
||||
const timeoutMs = Math.min(15000, Math.max(2000, Number(opts.timeoutMs) || 8000))
|
||||
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,
|
||||
at: Date.now()
|
||||
})
|
||||
}
|
||||
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')
|
||||
})
|
||||
return this.exportAuditExportArchive(fetched.id || archiveId, opts)
|
||||
}
|
||||
|
||||
async exportAuditExportArchive (archiveId, opts = {}) {
|
||||
if (!this.guild?.guild) throw new Error('no guild')
|
||||
const roles = await this._memberRoles()
|
||||
@@ -2072,23 +2144,52 @@ class PearcordPlatform extends EventEmitter {
|
||||
const worker = this.createIntegrationWorker()
|
||||
const events = []
|
||||
const receipts = []
|
||||
worker.on('event', (ev) => events.push(ev))
|
||||
const eventTypes = []
|
||||
worker.on('event', (ev) => {
|
||||
events.push(ev)
|
||||
if (ev?.type) eventTypes.push(ev.type)
|
||||
})
|
||||
worker.on('receipt', (r) => receipts.push(r))
|
||||
await worker.start({ templateId })
|
||||
const ms = Math.min(5000, Math.max(200, Number(holdMs) || 800))
|
||||
await this._fanoutAppEvent(APP_EVENTS.MESSAGE_CREATE, {
|
||||
message: {
|
||||
id: `trial-${Date.now()}`,
|
||||
channelId: this.activeChannelId || 'trial-ch',
|
||||
content: '(integration worker trial)',
|
||||
authorId: this.identity.user?.id || 'trial'
|
||||
}
|
||||
}).catch(() => {})
|
||||
const ms = Math.min(5000, Math.max(400, Number(holdMs) || 800))
|
||||
await new Promise((resolve) => setTimeout(resolve, ms))
|
||||
await worker.stop()
|
||||
const sawMessageCreate = eventTypes.includes(APP_EVENTS.MESSAGE_CREATE)
|
||||
return {
|
||||
ok: true,
|
||||
ok: sawMessageCreate,
|
||||
templateId,
|
||||
templateName: tpl.name,
|
||||
holdMs: ms,
|
||||
eventsSeen: events.length,
|
||||
receiptsSeen: receipts.length,
|
||||
eventTypes: [...new Set(eventTypes)],
|
||||
sawMessageCreate,
|
||||
guildId: this.guild.guild.id
|
||||
}
|
||||
}
|
||||
|
||||
async getAutomationScheduleDashboard () {
|
||||
if (!this.guild?.guild) {
|
||||
return buildAutomationScheduleDashboard({})
|
||||
}
|
||||
const auditExportSchedule = await this.getAuditExportSchedule()
|
||||
const digestExportSchedule = await this.getDigestExportSchedule()
|
||||
const hookFailureDigest = await this.getHookFailureDigest()
|
||||
return buildAutomationScheduleDashboard({
|
||||
auditExportSchedule,
|
||||
digestExportSchedule,
|
||||
hookFailureDigest
|
||||
})
|
||||
}
|
||||
|
||||
async exportHookFailureDigest (opts = {}) {
|
||||
if (!this.guild?.guild) throw new Error('no guild')
|
||||
const roles = await this._memberRoles()
|
||||
@@ -6370,6 +6471,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
let auditExportSchedule = null
|
||||
let digestExportSchedule = null
|
||||
let auditExportArchives = []
|
||||
let automationScheduleDashboard = null
|
||||
let workerReleaseHint = null
|
||||
let workerInstallPlan = null
|
||||
if (guild) {
|
||||
@@ -6377,6 +6479,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
auditExportSchedule = await this.getAuditExportSchedule()
|
||||
digestExportSchedule = await this.getDigestExportSchedule()
|
||||
auditExportArchives = await this.listAuditExportArchives(6)
|
||||
automationScheduleDashboard = await this.getAutomationScheduleDashboard()
|
||||
workerReleaseHint = await this.getWorkerReleaseHint()
|
||||
this._lastWorkerReleaseHintView = workerReleaseHint
|
||||
if (workerReleaseHint?.updateAvailable) {
|
||||
@@ -6549,6 +6652,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
auditExportSchedule,
|
||||
digestExportSchedule,
|
||||
auditExportArchives,
|
||||
automationScheduleDashboard,
|
||||
workerReleaseHint,
|
||||
workerInstallPlan,
|
||||
auditLogFilterOptions,
|
||||
|
||||
Reference in New Issue
Block a user