Files
obsidian-livesync/src/common/replicatorProviders.ts
T

156 lines
7.3 KiB
TypeScript

import { REMOTE_COUCHDB, REMOTE_MINIO, type RemoteDBSettings } from "@vrtmrz/livesync-commonlib/compat/common/types";
import {
CAPABILITY_NOT_APPLICABLE,
CENTRAL_REMOTE_REPLICATION_READINESS,
NO_INTERACTION,
REMOTE_RESOURCE_KINDS,
defineReplicatorProviderDefinitions,
supportedOpenReplicationContinuous,
replicationBlocked,
replicationFailed,
supportedStopActiveTransfer,
supportedCapability,
type ReplicatorProviderDefinitionMap,
type ReplicationOutcome,
type ReplicatorInstance,
type UserInitiatedOneShotRunner,
type UnattendedOneShotRunner,
} from "@vrtmrz/livesync-commonlib/replication";
import {
LiveSyncCouchDBReplicator,
type LiveSyncCouchDBReplicatorEnv,
} from "@vrtmrz/livesync-commonlib/compat/replication/couchdb/LiveSyncReplicator";
import { LiveSyncJournalReplicator } from "@vrtmrz/livesync-commonlib/compat/replication/journal/LiveSyncJournalReplicator";
import type { LiveSyncJournalReplicatorEnv } from "@vrtmrz/livesync-commonlib/compat/replication/journal/LiveSyncJournalReplicatorEnv";
import {
getCouchDBReplicatorConfigurationIdentity,
getObjectStorageReplicatorConfigurationIdentity,
} from "./replicatorConfigurationIdentity";
import {
createCouchDBConnectionProbeFactory,
createCouchDBPreferredTweakProbeFactory,
createCouchDBSecuritySeedResourceFactory,
createCouchDBSynchronisationInformationResourceFactory,
createObjectStorageConnectionProbeFactory,
createObjectStoragePreferredTweakProbeFactory,
createObjectStorageSecuritySeedResourceFactory,
} from "./replicatorResources";
import {
COUCHDB_CENTRAL_REMOTE_ADMINISTRATION_CAPABILITY,
OBJECT_STORAGE_CENTRAL_REMOTE_ADMINISTRATION_CAPABILITY,
} from "./centralRemoteAdministration";
export type CentralReplicatorProviderHost = LiveSyncCouchDBReplicatorEnv & LiveSyncJournalReplicatorEnv;
/** Minimal operation required by both central one-shot adapters. */
interface OneShotOutcomeReplicator extends ReplicatorInstance {
openOneShotReplicationWithOutcome(setting: RemoteDBSettings, showResult: boolean): Promise<ReplicationOutcome>;
}
function asOneShotOutcomeReplicator(instance: ReplicatorInstance): OneShotOutcomeReplicator | undefined {
const candidate = instance as Partial<OneShotOutcomeReplicator>;
return typeof candidate.openOneShotReplicationWithOutcome === "function"
? (instance as OneShotOutcomeReplicator)
: undefined;
}
async function runOneShotWithOutcome(
instance: ReplicatorInstance,
setting: RemoteDBSettings,
showResult: boolean
): Promise<ReplicationOutcome> {
const replicator = asOneShotOutcomeReplicator(instance);
if (!replicator) {
return replicationFailed(new Error("The configured provider does not implement one-shot replication."));
}
return await replicator.openOneShotReplicationWithOutcome(setting, showResult);
}
const couchDBUserInitiatedOneShot: UserInitiatedOneShotRunner = async (instance, setting, request) => {
return await runOneShotWithOutcome(
instance,
setting,
request.interaction.kind === "permitted" && request.interaction.permissions.failureRecovery
);
};
const couchDBUnattendedOneShot: UnattendedOneShotRunner = async (instance, setting, request) => {
if (request.interaction.kind !== NO_INTERACTION.kind) return replicationBlocked("interaction-required");
return await runOneShotWithOutcome(instance, setting, false);
};
const objectStorageUserInitiatedOneShot: UserInitiatedOneShotRunner = async (instance, setting, request) => {
return await runOneShotWithOutcome(
instance,
setting,
request.interaction.kind === "permitted" && request.interaction.permissions.failureRecovery
);
};
const objectStorageUnattendedOneShot: UnattendedOneShotRunner = async (instance, setting, request) => {
if (request.interaction.kind !== NO_INTERACTION.kind) return replicationBlocked("interaction-required");
return await runOneShotWithOutcome(instance, setting, false);
};
/** Build the complete central-remote provider policy for one LiveSync host. */
export function createCentralReplicatorProviderDefinitions(
host: CentralReplicatorProviderHost
): ReplicatorProviderDefinitionMap {
return defineReplicatorProviderDefinitions([REMOTE_COUCHDB, REMOTE_MINIO] as const, {
[REMOTE_COUCHDB]: {
kind: REMOTE_COUCHDB,
diagnosticName: "CouchDB",
readiness: CENTRAL_REMOTE_REPLICATION_READINESS,
isConfigured: (settings) =>
settings.remoteType === REMOTE_COUCHDB &&
!!settings.couchDB_URI?.trim() &&
!!settings.couchDB_DBNAME?.trim(),
configurationIdentity: getCouchDBReplicatorConfigurationIdentity,
create: () => Promise.resolve(new LiveSyncCouchDBReplicator(host)),
remoteResources: {
[REMOTE_RESOURCE_KINDS.CONNECTION]: supportedCapability(createCouchDBConnectionProbeFactory(host)),
[REMOTE_RESOURCE_KINDS.PREFERRED_TWEAK]: supportedCapability(
createCouchDBPreferredTweakProbeFactory(host)
),
[REMOTE_RESOURCE_KINDS.SECURITY_SEED]: supportedCapability(
createCouchDBSecuritySeedResourceFactory(host)
),
[REMOTE_RESOURCE_KINDS.SYNCHRONISATION_INFORMATION]: supportedCapability(
createCouchDBSynchronisationInformationResourceFactory(host)
),
},
centralRemoteAdministration: COUCHDB_CENTRAL_REMOTE_ADMINISTRATION_CAPABILITY,
userInitiatedOneShot: supportedCapability(couchDBUserInitiatedOneShot),
unattendedOneShot: supportedCapability(couchDBUnattendedOneShot),
continuous: supportedOpenReplicationContinuous(),
stopActiveTransfer: supportedStopActiveTransfer(),
},
[REMOTE_MINIO]: {
kind: REMOTE_MINIO,
diagnosticName: "Object Storage",
readiness: CENTRAL_REMOTE_REPLICATION_READINESS,
isConfigured: (settings) =>
settings.remoteType === REMOTE_MINIO && !!settings.endpoint?.trim() && !!settings.bucket?.trim(),
configurationIdentity: getObjectStorageReplicatorConfigurationIdentity,
create: () => Promise.resolve(new LiveSyncJournalReplicator(host)),
remoteResources: {
[REMOTE_RESOURCE_KINDS.CONNECTION]: supportedCapability(
createObjectStorageConnectionProbeFactory(host)
),
[REMOTE_RESOURCE_KINDS.PREFERRED_TWEAK]: supportedCapability(
createObjectStoragePreferredTweakProbeFactory(host)
),
[REMOTE_RESOURCE_KINDS.SECURITY_SEED]: supportedCapability(
createObjectStorageSecuritySeedResourceFactory(host)
),
[REMOTE_RESOURCE_KINDS.SYNCHRONISATION_INFORMATION]: CAPABILITY_NOT_APPLICABLE,
},
centralRemoteAdministration: OBJECT_STORAGE_CENTRAL_REMOTE_ADMINISTRATION_CAPABILITY,
userInitiatedOneShot: supportedCapability(objectStorageUserInitiatedOneShot),
unattendedOneShot: supportedCapability(objectStorageUnattendedOneShot),
continuous: CAPABILITY_NOT_APPLICABLE,
stopActiveTransfer: supportedStopActiveTransfer(),
},
});
}