1
0
silverbullet/plugs/sync/sync.ts

252 lines
6.3 KiB
TypeScript
Raw Normal View History

2023-01-13 14:41:29 +00:00
import { store } from "$sb/plugos-syscall/mod.ts";
2023-01-20 15:51:06 +00:00
import { editor, space, sync, system } from "$sb/silverbullet-syscall/mod.ts";
2023-01-13 14:41:29 +00:00
import type { SyncEndpoint } from "$sb/silverbullet-syscall/sync.ts";
2023-02-28 10:13:18 +00:00
import { readSetting } from "$sb/lib/settings_page.ts";
2023-01-13 14:41:29 +00:00
export async function configureCommand() {
const url = await editor.prompt(
"Enter the URL of the remote space to sync with",
"https://",
);
if (!url) {
return;
}
const user = await editor.prompt("Username (if any):");
let password = undefined;
if (user) {
password = await editor.prompt("Password:");
}
const syncConfig: SyncEndpoint = {
url,
user,
password,
};
try {
await system.invokeFunction("server", "check", syncConfig);
} catch (e: any) {
await editor.flashNotification(
`Sync configuration failed: ${e.message}`,
"error",
);
return;
}
await store.batchSet([
{ key: "sync.config", value: syncConfig },
// Empty initial snapshot
{ key: "sync.snapshot", value: {} },
]);
await editor.flashNotification("Sync configuration saved.");
return syncConfig;
}
export async function syncCommand() {
let config: SyncEndpoint | undefined = await store.get("sync.config");
if (!config) {
config = await configureCommand();
if (!config) {
return;
}
}
await editor.flashNotification("Starting sync...");
try {
2023-01-14 17:51:00 +00:00
await system.invokeFunction("server", "check", config);
2023-01-13 14:41:29 +00:00
const operations = await system.invokeFunction("server", "performSync");
await editor.flashNotification(
`Sync complete. Performed ${operations} operations.`,
);
} catch (e: any) {
await editor.flashNotification(
`Sync failed: ${e.message}`,
"error",
);
}
}
2023-01-26 14:54:28 +00:00
export async function disableCommand() {
if (
!(await editor.confirm(
"Are you sure you want to disable sync?",
))
) {
return;
}
// remove all sync related keys from the store
await store.deletePrefix("sync.");
await editor.flashNotification("Sync disabled.");
}
export async function localWipeAndSyncCommand() {
let config: SyncEndpoint | undefined = await store.get("sync.config");
if (!config) {
config = await configureCommand();
if (!config) {
return;
}
}
if (
!(await editor.confirm(
"Are you sure you want to wipe your local space and sync with the remote?",
))
) {
return;
}
if (
!(await editor.confirm(
"To be clear: this means all local content will be deleted with no way to recover it. Are you sure?",
))
) {
return;
}
console.log("Wiping local pages");
await editor.flashNotification("Now wiping all pages");
for (const page of await space.listPages()) {
console.log("Deleting page", page.name);
await space.deletePage(page.name);
}
console.log("Wiping local attachments");
await editor.flashNotification("Now wiping all attachments");
for (const attachment of await space.listAttachments()) {
console.log("Deleting attachment", attachment.name);
await space.deleteAttachment(attachment.name);
}
console.log("Wiping local sync state");
await store.set("sync.snapshot", {});
// Starting actual sync
await syncCommand();
2023-01-26 14:54:28 +00:00
// And finally loading all plugs
await system.invokeFunction("client", "core.updatePlugsCommand");
}
export async function syncOpenedPage() {
// Is sync on?
if (!(await store.has("sync.config"))) {
// Nope -> exit
return;
}
await system.invokeFunction(
"server",
"syncPage",
await editor.getCurrentPage(),
);
}
2023-01-13 14:41:29 +00:00
// Run on server
export function check(config: SyncEndpoint) {
return sync.check(config);
}
2023-01-26 14:54:28 +00:00
// If a sync takes longer than this, we'll consider it timed out
2023-01-25 08:46:56 +00:00
const syncTimeout = 1000 * 60 * 10; // 10 minutes
2023-01-13 14:41:29 +00:00
// Run on server
export async function performSync() {
const config: SyncEndpoint = await store.get("sync.config");
if (!config) {
// Sync not configured
return;
}
2023-02-28 10:13:18 +00:00
await augmentSettings(config);
// Check if sync not already in progress
const ongoingSync: number | undefined = await store.get("sync.startTime");
if (ongoingSync) {
if (Date.now() - ongoingSync > syncTimeout) {
console.log("Sync timed out, continuing");
} else {
console.log("Sync already in progress");
return;
}
}
// Keep track of sync start time
await store.set("sync.startTime", Date.now());
try {
// Perform actual sync
const snapshot = await store.get("sync.snapshot");
const { snapshot: newSnapshot, operations, error } = await sync.syncAll(
config,
snapshot,
);
// Store snapshot
await store.set("sync.snapshot", newSnapshot);
// Clear sync start time
await store.del("sync.startTime");
if (error) {
console.error("Sync error", error);
throw new Error(error);
}
return operations;
} catch (e: any) {
// Clear sync start time
await store.del("sync.startTime");
console.error("Sync error", e);
}
}
2023-02-28 10:13:18 +00:00
async function augmentSettings(endpoint: SyncEndpoint) {
const syncSettings = await readSetting("sync", {});
if (syncSettings.excludePrefixes) {
endpoint.excludePrefixes = syncSettings.excludePrefixes;
}
}
export async function syncPage(page: string) {
const config: SyncEndpoint = await store.get("sync.config");
if (!config) {
// Sync not configured
return;
}
2023-02-28 10:13:18 +00:00
await augmentSettings(config);
// Check if sync not already in progress
const ongoingSync: number | undefined = await store.get("sync.startTime");
if (ongoingSync) {
if (Date.now() - ongoingSync > syncTimeout) {
console.log("Sync timed out, continuing");
} else {
console.log("Sync already in progress");
return;
}
}
// Keep track of sync start time
await store.set("sync.startTime", Date.now());
2023-01-13 14:41:29 +00:00
const snapshot = await store.get("sync.snapshot");
console.log("Syncing page", page);
try {
const { snapshot: newSnapshot, error } = await sync.syncFile(
config,
snapshot,
`${page}.md`,
);
// Store snapshot
await store.set("sync.snapshot", newSnapshot);
// Clear sync start time
await store.del("sync.startTime");
if (error) {
console.error("Sync error", error);
throw new Error(error);
}
} catch (e: any) {
// Clear sync start time
await store.del("sync.startTime");
console.error("Sync error", e);
2023-01-13 14:41:29 +00:00
}
}