SilverBullet pivot to become an offline-first PWA (#403)
This commit is contained in:
@@ -1,44 +0,0 @@
|
||||
name: sync
|
||||
functions:
|
||||
configureCommand:
|
||||
path: sync.ts:configureCommand
|
||||
command:
|
||||
name: "Sync: Configure"
|
||||
|
||||
disableCommand:
|
||||
path: sync.ts:disableCommand
|
||||
command:
|
||||
name: "Sync: Disable"
|
||||
|
||||
syncCommand:
|
||||
path: sync.ts:syncCommand
|
||||
command:
|
||||
name: "Sync: Sync"
|
||||
key: "Shift-Alt-s"
|
||||
|
||||
wipeAndSyncCommand:
|
||||
path: sync.ts:localWipeAndSyncCommand
|
||||
command:
|
||||
name: "Sync: Wipe Local Space and Sync"
|
||||
|
||||
syncOpenedPage:
|
||||
path: sync.ts:syncOpenedPage
|
||||
events:
|
||||
- editor:pageLoaded
|
||||
|
||||
check:
|
||||
env: server
|
||||
path: sync.ts:check
|
||||
|
||||
performSync:
|
||||
env: server
|
||||
path: sync.ts:performSync
|
||||
# Sync every minute
|
||||
cron: "* * * * *"
|
||||
|
||||
# Automatically sync the current page upon change
|
||||
syncPage:
|
||||
path: sync.ts:syncPage
|
||||
env: server
|
||||
events:
|
||||
- page:saved
|
||||
@@ -1,251 +0,0 @@
|
||||
import { store } from "$sb/plugos-syscall/mod.ts";
|
||||
import { editor, space, sync, system } from "$sb/silverbullet-syscall/mod.ts";
|
||||
import type { SyncEndpoint } from "$sb/silverbullet-syscall/sync.ts";
|
||||
import { readSetting } from "$sb/lib/settings_page.ts";
|
||||
|
||||
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 {
|
||||
await system.invokeFunction("server", "check", config);
|
||||
|
||||
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",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// 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(),
|
||||
);
|
||||
}
|
||||
|
||||
// Run on server
|
||||
export function check(config: SyncEndpoint) {
|
||||
return sync.check(config);
|
||||
}
|
||||
|
||||
// If a sync takes longer than this, we'll consider it timed out
|
||||
const syncTimeout = 1000 * 60 * 10; // 10 minutes
|
||||
|
||||
// Run on server
|
||||
export async function performSync() {
|
||||
const config: SyncEndpoint = await store.get("sync.config");
|
||||
if (!config) {
|
||||
// Sync not configured
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user