Phase 874: protomux-rpc audit export archive pull (v0.8.841)
Extract platform-audit-archive-rpc-mixin for AUDIT_EXPORT_ARCHIVE_REQUEST RPC-first path in fetchAuditExportArchiveFromMesh; register guild RPC handler. Harden archive fetch waiters to require full entries (non-breaking fix). Smokes, CI bundle test:ci-phase874, docs, version 0.8.841.
This commit is contained in:
@@ -92,6 +92,8 @@ Application facade: one `PearcordPlatform` class that wires identity, database,
|
|||||||
|
|
||||||
**Phase 822 (v0.8.789):** Triple-peer topology via `guild-mesh-topology` + `getMeshStabilityStats`. Bundle: `npm run test:ci-phase822`.
|
**Phase 822 (v0.8.789):** Triple-peer topology via `guild-mesh-topology` + `getMeshStabilityStats`. Bundle: `npm run test:ci-phase822`.
|
||||||
|
|
||||||
|
**Phase 874 (v0.8.841):** Opt-in protomux-rpc `AUDIT_EXPORT_ARCHIVE_REQUEST` pull via `platform-audit-archive-rpc-mixin` (`_tryFetchAuditExportArchiveFromMeshRpc`, `PEARCORD_RPC_REQUEST_WIRE=1`). Bundle: `npm run test:ci-phase874`.
|
||||||
|
|
||||||
**Phase 872 (v0.8.839):** Opt-in protomux-rpc `MESSAGE_SEARCH_REQUEST` multi-peer pull via `_fetchGuildSearchFromMeshRpc` (`PEARCORD_RPC_REQUEST_WIRE=1`). Bundle: `npm run test:ci-phase872`.
|
**Phase 872 (v0.8.839):** Opt-in protomux-rpc `MESSAGE_SEARCH_REQUEST` multi-peer pull via `_fetchGuildSearchFromMeshRpc` (`PEARCORD_RPC_REQUEST_WIRE=1`). Bundle: `npm run test:ci-phase872`.
|
||||||
|
|
||||||
**Phase 871 (v0.8.838):** Opt-in protomux-rpc `MEMBER_PAGE_REQUEST` pull via `_tryMemberPageRequestRpc` (`PEARCORD_RPC_REQUEST_WIRE=1`). Bundle: `npm run test:ci-phase871`.
|
**Phase 871 (v0.8.838):** Opt-in protomux-rpc `MEMBER_PAGE_REQUEST` pull via `_tryMemberPageRequestRpc` (`PEARCORD_RPC_REQUEST_WIRE=1`). Bundle: `npm run test:ci-phase871`.
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ const { platformGuildWireMixin } = require('./mixins/runtime/guild-sync/platform
|
|||||||
const { platformViewMixin } = require('./mixins/runtime/view/platform-view-mixin.js')
|
const { platformViewMixin } = require('./mixins/runtime/view/platform-view-mixin.js')
|
||||||
const { platformJsonExportSurfaceMixin } = require('./mixins/runtime/features/platform-json-export-surface-mixin.js')
|
const { platformJsonExportSurfaceMixin } = require('./mixins/runtime/features/platform-json-export-surface-mixin.js')
|
||||||
const { platformAuditAutomationExportMixin } = require('./mixins/runtime/features/platform-audit-automation-export-mixin.js')
|
const { platformAuditAutomationExportMixin } = require('./mixins/runtime/features/platform-audit-automation-export-mixin.js')
|
||||||
|
const { platformAuditArchiveRpcMixin } = require('./mixins/runtime/features/platform-audit-archive-rpc-mixin.js')
|
||||||
const { platformDmVoiceMediaMixin } = require('./mixins/runtime/voice-dm/platform-dm-voice-media-mixin.js')
|
const { platformDmVoiceMediaMixin } = require('./mixins/runtime/voice-dm/platform-dm-voice-media-mixin.js')
|
||||||
const { platformDmSessionMixin } = require('./mixins/runtime/voice-dm/platform-dm-session-mixin.js')
|
const { platformDmSessionMixin } = require('./mixins/runtime/voice-dm/platform-dm-session-mixin.js')
|
||||||
const { platformProfileContactsMixin } = require('./mixins/runtime/features/platform-profile-contacts-mixin.js')
|
const { platformProfileContactsMixin } = require('./mixins/runtime/features/platform-profile-contacts-mixin.js')
|
||||||
@@ -263,6 +264,7 @@ const ORDERED_PLATFORM_MIXINS = [
|
|||||||
platformViewMixin,
|
platformViewMixin,
|
||||||
platformJsonExportSurfaceMixin,
|
platformJsonExportSurfaceMixin,
|
||||||
platformAuditAutomationExportMixin,
|
platformAuditAutomationExportMixin,
|
||||||
|
platformAuditArchiveRpcMixin,
|
||||||
platformDmVoiceMediaMixin,
|
platformDmVoiceMediaMixin,
|
||||||
platformDmSessionMixin,
|
platformDmSessionMixin,
|
||||||
platformProfileContactsMixin,
|
platformProfileContactsMixin,
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const b4a = require('b4a')
|
||||||
|
const { id } = require('../../../platform-class-imports')
|
||||||
|
const sharedScope = require('../../../platform-pearcord-shared-imports')
|
||||||
|
|
||||||
|
function meshPeerIdHex (pk) {
|
||||||
|
if (pk == null) return null
|
||||||
|
if (typeof pk === 'string') {
|
||||||
|
const s = pk.trim()
|
||||||
|
if (/^[0-9a-f]{64}$/i.test(s)) return s.toLowerCase()
|
||||||
|
}
|
||||||
|
if (b4a.isBuffer(pk) || pk instanceof Uint8Array) {
|
||||||
|
return b4a.toString(pk, 'hex')
|
||||||
|
}
|
||||||
|
return String(pk).trim() || null
|
||||||
|
}
|
||||||
|
|
||||||
|
const platformAuditArchiveRpcMixin = {
|
||||||
|
async _buildAuditExportArchiveResponsePayload (payload) {
|
||||||
|
if (!payload?.guildId || !payload?.archiveId) return null
|
||||||
|
if (this.guild?.guild?.id !== payload.guildId) return null
|
||||||
|
const selfId = this.identity.user?.id || null
|
||||||
|
if (payload.targetMemberId && payload.targetMemberId !== selfId) return null
|
||||||
|
await this._initDeliveryReceipts(payload.guildId)
|
||||||
|
const row = await this.deliveryReceipts.getAuditExportArchive(payload.archiveId)
|
||||||
|
if (!row?.entries?.length) return null
|
||||||
|
return {
|
||||||
|
...row,
|
||||||
|
respondedBy: selfId,
|
||||||
|
requestId: payload.requestId || null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async _handleAuditExportArchiveRequestRpc (payload) {
|
||||||
|
const res = await this._buildAuditExportArchiveResponsePayload(payload)
|
||||||
|
if (res) res.viaRpc = true
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
|
||||||
|
async _tryFetchAuditExportArchiveFromMeshRpc (archiveId, opts = {}) {
|
||||||
|
if (!sharedScope.isRpcGossipRequestEnabled?.()) return null
|
||||||
|
if (!this.guild?.guild || !this.guild?.requestGossipFromPeer) return null
|
||||||
|
const guildId = this.guild.guild.id
|
||||||
|
let peerIds = [...(this.guild.peers?.keys() || [])]
|
||||||
|
if (!opts.targetMemberId) {
|
||||||
|
let hostPk = meshPeerIdHex(await this._lookupGuildHostPublicKey(this.guild.guild))
|
||||||
|
if (hostPk && !this.guild.peers?.has(hostPk) && peerIds.length === 1) {
|
||||||
|
hostPk = peerIds[0]
|
||||||
|
}
|
||||||
|
if (hostPk && peerIds.includes(hostPk)) {
|
||||||
|
peerIds = [hostPk]
|
||||||
|
} else if (hostPk && !peerIds.length) {
|
||||||
|
peerIds = [hostPk]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!peerIds.length) return null
|
||||||
|
await this._flushGuildGossipOutbox().catch(() => {})
|
||||||
|
try {
|
||||||
|
const { ensureGuildWireChannels } = require('pearcord-guild/mesh')
|
||||||
|
ensureGuildWireChannels(this.guild)
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
const requestId = id()
|
||||||
|
const payload = {
|
||||||
|
guildId,
|
||||||
|
archiveId,
|
||||||
|
requestId,
|
||||||
|
requestedBy: this.identity.user?.id || null,
|
||||||
|
targetMemberId: opts.targetMemberId || null,
|
||||||
|
at: Date.now(),
|
||||||
|
attempt: Number(opts.attempt) || 0
|
||||||
|
}
|
||||||
|
const rpcMs = Number(process.env.PEARCORD_AUDIT_EXPORT_ARCHIVE_RPC_MS) || 12000
|
||||||
|
const wireMs =
|
||||||
|
Number(process.env.PEARCORD_AUDIT_EXPORT_ARCHIVE_RPC_WIRE_READY_MS) || 8000
|
||||||
|
const responses = await Promise.all(
|
||||||
|
peerIds.map((peerId) =>
|
||||||
|
this.guild
|
||||||
|
.requestGossipFromPeer(
|
||||||
|
peerId,
|
||||||
|
sharedScope.RPC.AUDIT_EXPORT_ARCHIVE_REQUEST,
|
||||||
|
payload,
|
||||||
|
{ timeout: rpcMs, wireReadyMs: wireMs }
|
||||||
|
)
|
||||||
|
.catch(() => null)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
const hit = responses.find((r) => r && Array.isArray(r.entries) && r.entries.length)
|
||||||
|
return hit || null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { platformAuditArchiveRpcMixin }
|
||||||
@@ -304,7 +304,9 @@ async _onAuditExportArchiveGossip (payload) {
|
|||||||
await this._initDeliveryReceipts(payload.guildId)
|
await this._initDeliveryReceipts(payload.guildId)
|
||||||
const row = await this.deliveryReceipts.ingestAuditExportArchive(payload)
|
const row = await this.deliveryReceipts.ingestAuditExportArchive(payload)
|
||||||
if (row) {
|
if (row) {
|
||||||
this._resolveAuditArchiveFetchWaiter(row)
|
if (row.entries?.length) {
|
||||||
|
this._resolveAuditArchiveFetchWaiter(row)
|
||||||
|
}
|
||||||
this.emit('audit-export-archive', row)
|
this.emit('audit-export-archive', row)
|
||||||
}
|
}
|
||||||
return row
|
return row
|
||||||
@@ -317,21 +319,12 @@ async listAuditExportArchives (limit = 8) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async _onAuditExportArchiveRequestGossip (payload) {
|
async _onAuditExportArchiveRequestGossip (payload) {
|
||||||
if (!payload?.guildId || !payload?.archiveId) return null
|
const res = await this._buildAuditExportArchiveResponsePayload(payload)
|
||||||
if (this.guild?.guild?.id !== payload.guildId) return null
|
if (!res) return null
|
||||||
const selfId = this.identity.user?.id || null
|
|
||||||
if (payload.targetMemberId && payload.targetMemberId !== selfId) 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) {
|
if (this.guild.gossipAuditExportArchive) {
|
||||||
this.guild.gossipAuditExportArchive({
|
this.guild.gossipAuditExportArchive(res)
|
||||||
...row,
|
|
||||||
respondedBy: selfId,
|
|
||||||
requestId: payload.requestId || null
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return row
|
return res
|
||||||
},
|
},
|
||||||
|
|
||||||
async _touchArchivePeerSeen (userId, source = 'presence') {
|
async _touchArchivePeerSeen (userId, source = 'presence') {
|
||||||
@@ -536,7 +529,7 @@ async _fetchAutomationDigestSnapshotFromMeshOnce (snapshotId, opts = {}) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_resolveAuditArchiveFetchWaiter (archive) {
|
_resolveAuditArchiveFetchWaiter (archive) {
|
||||||
if (!archive?.id) return
|
if (!archive?.id || !archive.entries?.length) return
|
||||||
for (const [key, handlers] of this._auditArchiveFetchWaiters) {
|
for (const [key, handlers] of this._auditArchiveFetchWaiters) {
|
||||||
if (key === archive.id || key.startsWith(`${archive.id}:`)) {
|
if (key === archive.id || key.startsWith(`${archive.id}:`)) {
|
||||||
for (const fn of handlers) fn(archive)
|
for (const fn of handlers) fn(archive)
|
||||||
@@ -588,6 +581,18 @@ async _fetchAuditExportArchiveFromMeshOnce (archiveId, opts = {}) {
|
|||||||
const out = await this.exportAuditExportArchive(archiveId, opts)
|
const out = await this.exportAuditExportArchive(archiveId, opts)
|
||||||
return { ...out, fetchAttempt: (Number(opts.attempt) || 0) + 1 }
|
return { ...out, fetchAttempt: (Number(opts.attempt) || 0) + 1 }
|
||||||
}
|
}
|
||||||
|
if (sharedScope.isRpcGossipRequestEnabled?.()) {
|
||||||
|
const rpcArch = await this._tryFetchAuditExportArchiveFromMeshRpc(archiveId, opts)
|
||||||
|
if (rpcArch?.entries?.length) {
|
||||||
|
await this.deliveryReceipts.ingestAuditExportArchive(rpcArch).catch(() => {})
|
||||||
|
const out = await this.exportAuditExportArchive(rpcArch.id || archiveId, opts)
|
||||||
|
return {
|
||||||
|
...out,
|
||||||
|
fetchAttempt: (Number(opts.attempt) || 0) + 1,
|
||||||
|
viaRpc: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
const timeoutMs = Math.min(12000, Math.max(800, Number(opts.timeoutMs) || 3500))
|
const timeoutMs = Math.min(12000, Math.max(800, Number(opts.timeoutMs) || 3500))
|
||||||
const requestId = id()
|
const requestId = id()
|
||||||
const waitPromise = new Promise((resolve, reject) => {
|
const waitPromise = new Promise((resolve, reject) => {
|
||||||
@@ -618,6 +623,9 @@ async _fetchAuditExportArchiveFromMeshOnce (archiveId, opts = {}) {
|
|||||||
if (row?.entries?.length) return row
|
if (row?.entries?.length) return row
|
||||||
throw new Error('audit export archive not found on mesh')
|
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)
|
const out = await this.exportAuditExportArchive(fetched.id || archiveId, opts)
|
||||||
return { ...out, fetchAttempt: (Number(opts.attempt) || 0) + 1 }
|
return { ...out, fetchAttempt: (Number(opts.attempt) || 0) + 1 }
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ _bindGuildGossipRpcHandlers (guildInstance) {
|
|||||||
? {
|
? {
|
||||||
GUILD_SYNC_REQUEST: (payload) => this._handleGuildSyncRequestRpc(payload),
|
GUILD_SYNC_REQUEST: (payload) => this._handleGuildSyncRequestRpc(payload),
|
||||||
MEMBER_PAGE_REQUEST: (payload) => this._handleMemberPageRequestRpc(payload),
|
MEMBER_PAGE_REQUEST: (payload) => this._handleMemberPageRequestRpc(payload),
|
||||||
MESSAGE_SEARCH_REQUEST: (payload) => this._handleMessageSearchRequestRpc(payload)
|
MESSAGE_SEARCH_REQUEST: (payload) => this._handleMessageSearchRequestRpc(payload),
|
||||||
|
AUDIT_EXPORT_ARCHIVE_REQUEST: (payload) =>
|
||||||
|
this._handleAuditExportArchiveRequestRpc(payload)
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
const hadHandlers = !!guildInstance._gossipRpcHandlers
|
const hadHandlers = !!guildInstance._gossipRpcHandlers
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* Ordered PearcordPlatform prototype mixin registration (Phase 730).
|
* Ordered PearcordPlatform prototype mixin registration (Phase 730).
|
||||||
* Preserves assign order from index.js / apply-platform-mixins (Phase 727).
|
* Preserves assign order from index.js / apply-platform-mixins (Phase 727).
|
||||||
*/
|
*/
|
||||||
const PLATFORM_MIXIN_ASSIGNMENT_COUNT = 136
|
const PLATFORM_MIXIN_ASSIGNMENT_COUNT = 137
|
||||||
|
|
||||||
const PLATFORM_MIXIN_ASSIGNMENTS = [
|
const PLATFORM_MIXIN_ASSIGNMENTS = [
|
||||||
{ module: './mixins/domain/polls-scheduling', export: 'pollSchedulingMixin' },
|
{ module: './mixins/domain/polls-scheduling', export: 'pollSchedulingMixin' },
|
||||||
@@ -126,6 +126,7 @@ const PLATFORM_MIXIN_ASSIGNMENTS = [
|
|||||||
{ module: './mixins/runtime/view/platform-view-mixin', export: 'platformViewMixin' },
|
{ module: './mixins/runtime/view/platform-view-mixin', export: 'platformViewMixin' },
|
||||||
{ module: './mixins/runtime/features/platform-json-export-surface-mixin', export: 'platformJsonExportSurfaceMixin' },
|
{ module: './mixins/runtime/features/platform-json-export-surface-mixin', export: 'platformJsonExportSurfaceMixin' },
|
||||||
{ module: './mixins/runtime/features/platform-audit-automation-export-mixin', export: 'platformAuditAutomationExportMixin' },
|
{ module: './mixins/runtime/features/platform-audit-automation-export-mixin', export: 'platformAuditAutomationExportMixin' },
|
||||||
|
{ module: './mixins/runtime/features/platform-audit-archive-rpc-mixin', export: 'platformAuditArchiveRpcMixin' },
|
||||||
{ module: './mixins/runtime/voice-dm/platform-dm-voice-media-mixin', export: 'platformDmVoiceMediaMixin' },
|
{ module: './mixins/runtime/voice-dm/platform-dm-voice-media-mixin', export: 'platformDmVoiceMediaMixin' },
|
||||||
{ module: './mixins/runtime/voice-dm/platform-dm-session-mixin', export: 'platformDmSessionMixin' },
|
{ module: './mixins/runtime/voice-dm/platform-dm-session-mixin', export: 'platformDmSessionMixin' },
|
||||||
{ module: './mixins/runtime/features/platform-profile-contacts-mixin', export: 'platformProfileContactsMixin' },
|
{ module: './mixins/runtime/features/platform-profile-contacts-mixin', export: 'platformProfileContactsMixin' },
|
||||||
|
|||||||
Reference in New Issue
Block a user