rspace-online/server/sync-instance.ts

46 lines
1.5 KiB
TypeScript

/**
* SyncServer singleton — shared across server/index.ts and modules.
*
* Participant mode: server maintains its own Automerge docs.
* On any doc change, debounced-save to disk via doc-persistence.
*
* When a doc belongs to an encrypted space (meta.encrypted === true),
* the save is encrypted at rest using the space's encryptionKeyId.
*
* Relay mode: for encrypted spaces, the server stores opaque blobs
* it cannot decrypt, enabling cross-device restore.
*/
import { SyncServer } from "./local-first/sync-server";
import { saveDoc, saveEncryptedBlob, loadEncryptedBlob } from "./local-first/doc-persistence";
import { getDocumentData } from "./community-store";
/**
* Look up the encryption key ID for a doc's space.
* DocIds are formatted as "spaceSlug:module:collection[:itemId]".
* Returns the encryptionKeyId if the space has encryption enabled, else undefined.
*/
function getEncryptionKeyId(docId: string): string | undefined {
const spaceSlug = docId.split(":")[0];
if (!spaceSlug || spaceSlug === "global") return undefined;
const data = getDocumentData(spaceSlug);
if (data?.meta?.encrypted && data.meta.encryptionKeyId) {
return data.meta.encryptionKeyId;
}
return undefined;
}
export const syncServer = new SyncServer({
participantMode: true,
onDocChange: (docId, doc) => {
const encryptionKeyId = getEncryptionKeyId(docId);
saveDoc(docId, doc, encryptionKeyId);
},
onRelayBackup: (docId, blob) => {
saveEncryptedBlob(docId, blob);
},
onRelayLoad: (docId) => {
return loadEncryptedBlob(docId);
},
});