PSA/hocuspocus/CollabPersistenceExtension.js
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

71 lines
2.4 KiB
JavaScript

import * as Y from 'yjs'
/**
* Durable persistence for collaborative documents.
*
* Hocuspocus only syncs the Y.js document in memory (and across instances via
* Redis). On its own nothing is written to Postgres, so edits are lost on room
* eviction. This extension implements `onStoreDocument` — which Hocuspocus
* calls debounced while editing and again when the last client disconnects —
* and ships the Y.js state to an internal app endpoint that converts it to
* ProseMirror JSON and writes it to document_block_content.
*
* Only `document:<tenant>:<id>` rooms are persisted. Ticket / notification /
* AI rooms are ignored. Errors are logged, never thrown: a failed persist must
* not disrupt the live editing session — Hocuspocus retries on the next
* debounce / unload.
*/
export class CollabPersistenceExtension {
constructor(config = {}) {
this.apiUrl = config.apiUrl || 'http://localhost:3000/api/internal/collab/persist'
this.apiKey = config.apiKey || ''
}
async onStoreDocument({ document, documentName }) {
if (!documentName || !documentName.startsWith('document:')) {
return
}
const parts = documentName.split(':')
if (parts.length !== 3) {
return
}
const [, tenantId, documentId] = parts
if (!tenantId || !documentId) {
return
}
const fragment = document.getXmlFragment('prosemirror')
if (!fragment || fragment.length === 0) {
// Fresh/empty room — don't overwrite stored content before the client
// has seeded it from the database.
return
}
if (!this.apiKey) {
console.error('[CollabPersistenceExtension] COLLAB_PERSIST_API_KEY not set; skipping persistence')
return
}
try {
const update = Buffer.from(Y.encodeStateAsUpdate(document)).toString('base64')
const response = await fetch(this.apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': this.apiKey,
},
body: JSON.stringify({ tenantId, documentId, update }),
})
if (!response.ok) {
const errorBody = await response.text().catch(() => '')
console.error(
`[CollabPersistenceExtension] persist failed ${response.status} for ${documentName}: ${errorBody}`
)
}
} catch (error) {
console.error(`[CollabPersistenceExtension] persist error for ${documentName}:`, error)
}
}
}