+85
-30
@@ -9,7 +9,7 @@ import {
|
||||
} from "../common/deps.ts";
|
||||
import { fileMetaToPageMeta, Space } from "./space.ts";
|
||||
import { FilterOption } from "./types.ts";
|
||||
import { parseYamlSettings } from "../common/util.ts";
|
||||
import { ensureSettingsAndIndex } from "../common/util.ts";
|
||||
import { EventHook } from "../plugos/hooks/event.ts";
|
||||
import { AppCommand } from "./hooks/command.ts";
|
||||
import { PathPageNavigator } from "./navigator.ts";
|
||||
@@ -37,13 +37,16 @@ import { createEditorState } from "./editor_state.ts";
|
||||
import { OpenPages } from "./open_pages.ts";
|
||||
import { MainUI } from "./editor_ui.tsx";
|
||||
import { cleanPageRef } from "$sb/lib/resolve.ts";
|
||||
import { expandPropertyNames } from "$sb/lib/json.ts";
|
||||
import { SpacePrimitives } from "../common/spaces/space_primitives.ts";
|
||||
import { FileMeta, PageMeta } from "$sb/types.ts";
|
||||
import { DataStore } from "../plugos/lib/datastore.ts";
|
||||
import { IndexedDBKvPrimitives } from "../plugos/lib/indexeddb_kv_primitives.ts";
|
||||
import { DataStoreMQ } from "../plugos/lib/mq.datastore.ts";
|
||||
import { DataStoreSpacePrimitives } from "../common/spaces/datastore_space_primitives.ts";
|
||||
import {
|
||||
encryptedFileExt,
|
||||
EncryptedSpacePrimitives,
|
||||
} from "../common/spaces/encrypted_space_primitives.ts";
|
||||
const frontMatterRegex = /^---\n(([^\n]|\n)*?)---\n/;
|
||||
|
||||
const autoSaveInterval = 1000;
|
||||
@@ -54,6 +57,7 @@ declare global {
|
||||
silverBulletConfig: {
|
||||
spaceFolderPath: string;
|
||||
syncOnly: boolean;
|
||||
clientEncryption: boolean;
|
||||
};
|
||||
client: Client;
|
||||
}
|
||||
@@ -69,7 +73,7 @@ export class Client {
|
||||
|
||||
plugSpaceRemotePrimitives!: PlugSpacePrimitives;
|
||||
// localSpacePrimitives!: FilteredSpacePrimitives;
|
||||
remoteSpacePrimitives!: HttpSpacePrimitives;
|
||||
httpSpacePrimitives!: HttpSpacePrimitives;
|
||||
space!: Space;
|
||||
|
||||
saveTimeout?: number;
|
||||
@@ -177,15 +181,14 @@ export class Client {
|
||||
|
||||
this.focus();
|
||||
|
||||
// This constructor will always be followed by an (async) invocatition of init()
|
||||
await this.system.init();
|
||||
|
||||
// Load settings
|
||||
this.settings = await this.loadSettings();
|
||||
this.settings = await ensureSettingsAndIndex(localSpacePrimitives);
|
||||
|
||||
// Pinging a remote space to ensure we're authenticated properly, if not will result in a redirect to auth page
|
||||
try {
|
||||
await this.remoteSpacePrimitives.ping();
|
||||
await this.httpSpacePrimitives.ping();
|
||||
} catch (e: any) {
|
||||
if (e.message === "Not authenticated") {
|
||||
console.warn("Not authenticated, redirecting to auth page");
|
||||
@@ -346,13 +349,83 @@ export class Client {
|
||||
}
|
||||
|
||||
async initSpace(): Promise<SpacePrimitives> {
|
||||
this.remoteSpacePrimitives = new HttpSpacePrimitives(
|
||||
this.httpSpacePrimitives = new HttpSpacePrimitives(
|
||||
location.origin,
|
||||
window.silverBulletConfig.spaceFolderPath,
|
||||
);
|
||||
|
||||
let remoteSpacePrimitives: SpacePrimitives = this.httpSpacePrimitives;
|
||||
|
||||
if (window.silverBulletConfig.clientEncryption) {
|
||||
console.log("Enabling encryption");
|
||||
|
||||
const encryptedSpacePrimitives = new EncryptedSpacePrimitives(
|
||||
this.httpSpacePrimitives,
|
||||
);
|
||||
remoteSpacePrimitives = encryptedSpacePrimitives;
|
||||
let loggedIn = false;
|
||||
// First figure out if we're online & if the key file exists, if not we need to initialize the space
|
||||
try {
|
||||
if (!await encryptedSpacePrimitives.init()) {
|
||||
console.log(
|
||||
"Space not initialized, will ask for password to initialize",
|
||||
);
|
||||
alert(
|
||||
"You appear to be accessing a new space with encryption enabled, you will now be asked to create a password",
|
||||
);
|
||||
const password = prompt("Choose a password");
|
||||
if (!password) {
|
||||
alert("Cannot do anything without a password, reloading");
|
||||
location.reload();
|
||||
throw new Error("Not initialized");
|
||||
}
|
||||
const password2 = prompt("Confirm password");
|
||||
if (password !== password2) {
|
||||
alert("Passwords don't match, reloading");
|
||||
location.reload();
|
||||
throw new Error("Not initialized");
|
||||
}
|
||||
await encryptedSpacePrimitives.setup(password);
|
||||
// this.stateDataStore.set(["encryptionKey"], password);
|
||||
await this.stateDataStore.set(
|
||||
["spaceSalt"],
|
||||
encryptedSpacePrimitives.spaceSalt,
|
||||
);
|
||||
loggedIn = true;
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e.message === "Offline") {
|
||||
console.log(
|
||||
"Offline, will assume encryption space is initialized, fetching salt from data store",
|
||||
);
|
||||
await encryptedSpacePrimitives.init(
|
||||
await this.stateDataStore.get(["spaceSalt"]),
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!loggedIn) {
|
||||
// Let's ask for the password
|
||||
try {
|
||||
await encryptedSpacePrimitives.login(
|
||||
prompt("Password")!,
|
||||
);
|
||||
await this.stateDataStore.set(
|
||||
["spaceSalt"],
|
||||
encryptedSpacePrimitives.spaceSalt,
|
||||
);
|
||||
} catch (e: any) {
|
||||
console.log("Got this error", e);
|
||||
if (e.message === "Incorrect password") {
|
||||
alert("Incorrect password");
|
||||
location.reload();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.plugSpaceRemotePrimitives = new PlugSpacePrimitives(
|
||||
this.remoteSpacePrimitives,
|
||||
remoteSpacePrimitives,
|
||||
this.system.namespaceHook,
|
||||
this.syncMode ? undefined : "client",
|
||||
);
|
||||
@@ -380,7 +453,10 @@ export class Client {
|
||||
(meta) => fileFilterFn(meta.name),
|
||||
// Run when a list of files has been retrieved
|
||||
async () => {
|
||||
await this.loadSettings();
|
||||
if (!this.settings) {
|
||||
this.settings = await ensureSettingsAndIndex(localSpacePrimitives!);
|
||||
}
|
||||
|
||||
if (typeof this.settings?.spaceIgnore === "string") {
|
||||
fileFilterFn = gitIgnoreCompiler(this.settings.spaceIgnore).accepts;
|
||||
} else {
|
||||
@@ -439,27 +515,6 @@ export class Client {
|
||||
return localSpacePrimitives;
|
||||
}
|
||||
|
||||
async loadSettings(): Promise<BuiltinSettings> {
|
||||
let settingsText: string | undefined;
|
||||
|
||||
try {
|
||||
settingsText = (await this.space.readPage("SETTINGS")).text;
|
||||
} catch (e: any) {
|
||||
console.info("No SETTINGS page, falling back to default", e);
|
||||
settingsText = '```yaml\nindexPage: "[[index]]"\n```\n';
|
||||
}
|
||||
let settings = parseYamlSettings(settingsText!) as BuiltinSettings;
|
||||
|
||||
settings = expandPropertyNames(settings);
|
||||
|
||||
// console.log("Settings", settings);
|
||||
|
||||
if (!settings.indexPage) {
|
||||
settings.indexPage = "[[index]]";
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
get currentPage(): string | undefined {
|
||||
return this.ui.viewState.currentPage;
|
||||
}
|
||||
|
||||
+1
-1
@@ -166,6 +166,7 @@ export function createEditorState(
|
||||
{ selector: "FrontMatter", class: "sb-frontmatter" },
|
||||
]),
|
||||
keymap.of([
|
||||
...commandKeyBindings,
|
||||
...smartQuoteKeymap,
|
||||
...closeBracketsKeymap,
|
||||
...standardKeymap,
|
||||
@@ -173,7 +174,6 @@ export function createEditorState(
|
||||
...historyKeymap,
|
||||
...completionKeymap,
|
||||
indentWithTab,
|
||||
...commandKeyBindings,
|
||||
{
|
||||
key: "Ctrl-k",
|
||||
mac: "Cmd-k",
|
||||
|
||||
@@ -39,12 +39,14 @@
|
||||
// These {{VARIABLES}} are replaced by http_server.ts
|
||||
spaceFolderPath: "{{SPACE_PATH}}",
|
||||
syncOnly: "{{SYNC_ONLY}}" === "true",
|
||||
clientEncryption: "{{CLIENT_ENCRYPTION}}" === "true",
|
||||
};
|
||||
// But in case these variables aren't replaced by the server, fall back sync only mode
|
||||
if (window.silverBulletConfig.spaceFolderPath.includes("{{")) {
|
||||
window.silverBulletConfig = {
|
||||
spaceFolderPath: "",
|
||||
syncOnly: true,
|
||||
clientEncryption: false,
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { simpleHash } from "../common/crypto.ts";
|
||||
import { DataStore } from "../plugos/lib/datastore.ts";
|
||||
import { IndexedDBKvPrimitives } from "../plugos/lib/indexeddb_kv_primitives.ts";
|
||||
|
||||
const CACHE_NAME = "{{CACHE_NAME}}";
|
||||
const CACHE_NAME = "{{CACHE_NAME}}_{{CONFIG_HASH}}";
|
||||
|
||||
const precacheFiles = Object.fromEntries([
|
||||
"/",
|
||||
@@ -61,7 +61,6 @@ self.addEventListener("activate", (event: any) => {
|
||||
});
|
||||
|
||||
let ds: DataStore | undefined;
|
||||
const filesMetaPrefix = ["file", "meta"];
|
||||
const filesContentPrefix = ["file", "content"];
|
||||
|
||||
self.addEventListener("fetch", (event: any) => {
|
||||
|
||||
@@ -28,15 +28,15 @@ export function sandboxFetchSyscalls(
|
||||
body: options.base64Body && base64Decode(options.base64Body),
|
||||
}
|
||||
: {};
|
||||
if (!client.remoteSpacePrimitives) {
|
||||
if (!client.httpSpacePrimitives) {
|
||||
// No SB server to proxy the fetch available so let's execute the request directly
|
||||
return performLocalFetch(url, fetchOptions);
|
||||
}
|
||||
fetchOptions.headers = fetchOptions.headers
|
||||
? { ...fetchOptions.headers, "X-Proxy-Request": "true" }
|
||||
: { "X-Proxy-Request": "true" };
|
||||
const resp = await client.remoteSpacePrimitives.authenticatedFetch(
|
||||
`${client.remoteSpacePrimitives.url}/!${url}`,
|
||||
const resp = await client.httpSpacePrimitives.authenticatedFetch(
|
||||
`${client.httpSpacePrimitives.url}/!${url}`,
|
||||
fetchOptions,
|
||||
);
|
||||
const body = await resp.arrayBuffer();
|
||||
|
||||
@@ -10,11 +10,11 @@ export function shellSyscalls(
|
||||
cmd: string,
|
||||
args: string[],
|
||||
): Promise<{ stdout: string; stderr: string; code: number }> => {
|
||||
if (!client.remoteSpacePrimitives) {
|
||||
if (!client.httpSpacePrimitives) {
|
||||
throw new Error("Not supported in fully local mode");
|
||||
}
|
||||
const resp = client.remoteSpacePrimitives.authenticatedFetch(
|
||||
`${client.remoteSpacePrimitives.url}/.rpc`,
|
||||
const resp = client.httpSpacePrimitives.authenticatedFetch(
|
||||
`${client.httpSpacePrimitives.url}/.rpc`,
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
|
||||
@@ -43,7 +43,7 @@ export function systemSyscalls(
|
||||
// Proxy to another environment
|
||||
return proxySyscall(
|
||||
ctx,
|
||||
client.remoteSpacePrimitives,
|
||||
client.httpSpacePrimitives,
|
||||
"system.invokeFunction",
|
||||
[fullName, ...args],
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@ export function proxySyscalls(client: Client, names: string[]): SysCallMapping {
|
||||
const syscalls: SysCallMapping = {};
|
||||
for (const name of names) {
|
||||
syscalls[name] = (ctx, ...args: any[]) => {
|
||||
return proxySyscall(ctx, client.remoteSpacePrimitives, name, args);
|
||||
return proxySyscall(ctx, client.httpSpacePrimitives, name, args);
|
||||
};
|
||||
}
|
||||
return syscalls;
|
||||
|
||||
Reference in New Issue
Block a user