Refactoring about how proxy fetching happens
This commit is contained in:
parent
38a95d2382
commit
5a88254cde
@ -8,13 +8,13 @@ import { CodeWidgetT } from "../web/hooks/code_widget.ts";
|
|||||||
import { MQHookT } from "../plugos/hooks/mq.ts";
|
import { MQHookT } from "../plugos/hooks/mq.ts";
|
||||||
import { EndpointHookT } from "../plugos/hooks/endpoint.ts";
|
import { EndpointHookT } from "../plugos/hooks/endpoint.ts";
|
||||||
|
|
||||||
/** Silverbullet hooks give plugs access to silverbullet core systems.
|
/** Silverbullet hooks give plugs access to silverbullet core systems.
|
||||||
*
|
*
|
||||||
* Hooks are associated with typescript functions through a manifest file.
|
* Hooks are associated with typescript functions through a manifest file.
|
||||||
* On various triggers (user enters a slash command, an HTTP endpoint is hit, user clicks, etc) the typescript function is called.
|
* On various triggers (user enters a slash command, an HTTP endpoint is hit, user clicks, etc) the typescript function is called.
|
||||||
*
|
*
|
||||||
* related: plugos/type.ts#FunctionDef
|
* related: plugos/type.ts#FunctionDef
|
||||||
*/
|
*/
|
||||||
export type SilverBulletHooks =
|
export type SilverBulletHooks =
|
||||||
& CommandHookT
|
& CommandHookT
|
||||||
& SlashCommandHookT
|
& SlashCommandHookT
|
||||||
@ -28,7 +28,7 @@ export type SilverBulletHooks =
|
|||||||
/** Syntax extension allow plugs to declaratively add new *inline* parse tree nodes to the markdown parser. */
|
/** Syntax extension allow plugs to declaratively add new *inline* parse tree nodes to the markdown parser. */
|
||||||
export type SyntaxExtensions = {
|
export type SyntaxExtensions = {
|
||||||
/** A map of node **name** (also called "type"), to parsing and highlighting instructions. Each entry defines a new node. By convention node names (types) are UpperCamelCase (PascalCase).
|
/** A map of node **name** (also called "type"), to parsing and highlighting instructions. Each entry defines a new node. By convention node names (types) are UpperCamelCase (PascalCase).
|
||||||
*
|
*
|
||||||
* see: plug-api/lib/tree.ts#ParseTree
|
* see: plug-api/lib/tree.ts#ParseTree
|
||||||
*/
|
*/
|
||||||
syntax?: { [key: string]: NodeDef };
|
syntax?: { [key: string]: NodeDef };
|
||||||
@ -37,21 +37,21 @@ export type SyntaxExtensions = {
|
|||||||
/** Parsing and highlighting instructions for SyntaxExtension */
|
/** Parsing and highlighting instructions for SyntaxExtension */
|
||||||
export type NodeDef = {
|
export type NodeDef = {
|
||||||
/** An array of possible first characters to begin matching on.
|
/** An array of possible first characters to begin matching on.
|
||||||
*
|
*
|
||||||
* **Example**: If this node has the regex '[abc][123]', NodeDef.firstCharacters should be ["a", "b", "c"].
|
* **Example**: If this node has the regex '[abc][123]', NodeDef.firstCharacters should be ["a", "b", "c"].
|
||||||
*/
|
*/
|
||||||
firstCharacters: string[];
|
firstCharacters: string[];
|
||||||
|
|
||||||
/** A regular expression that matches the *entire* syntax, including the first character. */
|
/** A regular expression that matches the *entire* syntax, including the first character. */
|
||||||
regex: string;
|
regex: string;
|
||||||
|
|
||||||
/** CSS styles to apply to the matched text.
|
/** CSS styles to apply to the matched text.
|
||||||
*
|
*
|
||||||
* Key-value pair of CSS key to value:
|
* Key-value pair of CSS key to value:
|
||||||
*
|
*
|
||||||
* **Example**: `backgroundColor: "rgba(22,22,22,0.07)"`
|
* **Example**: `backgroundColor: "rgba(22,22,22,0.07)"`
|
||||||
*/
|
*/
|
||||||
styles: { [key: string]: string };
|
styles?: { [key: string]: string };
|
||||||
|
|
||||||
/** CSS class name to apply to the matched text */
|
/** CSS class name to apply to the matched text */
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Tag } from "../deps.ts";
|
import { Tag } from "../deps.ts";
|
||||||
import type { MarkdownConfig } from "../deps.ts";
|
import type { MarkdownConfig } from "../deps.ts";
|
||||||
import { System } from "../../plugos/system.ts";
|
import { System } from "../../plugos/system.ts";
|
||||||
import { Manifest } from "../manifest.ts";
|
import { Manifest, NodeDef } from "../manifest.ts";
|
||||||
|
|
||||||
export type MDExt = {
|
export type MDExt = {
|
||||||
// unicode char code for efficiency .charCodeAt(0)
|
// unicode char code for efficiency .charCodeAt(0)
|
||||||
@ -9,7 +9,7 @@ export type MDExt = {
|
|||||||
regex: RegExp;
|
regex: RegExp;
|
||||||
nodeType: string;
|
nodeType: string;
|
||||||
tag: Tag;
|
tag: Tag;
|
||||||
styles: { [key: string]: string };
|
styles?: { [key: string]: string };
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -53,16 +53,20 @@ export function loadMarkdownExtensions(system: System<any>): MDExt[] {
|
|||||||
const manifest = plug.manifest as Manifest;
|
const manifest = plug.manifest as Manifest;
|
||||||
if (manifest.syntax) {
|
if (manifest.syntax) {
|
||||||
for (const [nodeType, def] of Object.entries(manifest.syntax)) {
|
for (const [nodeType, def] of Object.entries(manifest.syntax)) {
|
||||||
mdExtensions.push({
|
mdExtensions.push(nodeDefToMDExt(nodeType, def));
|
||||||
nodeType,
|
|
||||||
tag: Tag.define(),
|
|
||||||
firstCharCodes: def.firstCharacters.map((ch) => ch.charCodeAt(0)),
|
|
||||||
regex: new RegExp("^" + def.regex),
|
|
||||||
styles: def.styles,
|
|
||||||
className: def.className,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mdExtensions;
|
return mdExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function nodeDefToMDExt(nodeType: string, def: NodeDef): MDExt {
|
||||||
|
return {
|
||||||
|
nodeType,
|
||||||
|
tag: Tag.define(),
|
||||||
|
firstCharCodes: def.firstCharacters.map((ch) => ch.charCodeAt(0)),
|
||||||
|
regex: new RegExp("^" + def.regex),
|
||||||
|
styles: def.styles,
|
||||||
|
className: def.className,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { base64Encode } from "../plugos/asset_bundle/base64.ts";
|
import { base64Decode, base64Encode } from "../plugos/asset_bundle/base64.ts";
|
||||||
|
|
||||||
export type ProxyFetchRequest = {
|
export type ProxyFetchRequest = {
|
||||||
method?: string;
|
method?: string;
|
||||||
headers?: Record<string, string>;
|
headers?: Record<string, string>;
|
||||||
body?: string;
|
base64Body?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProxyFetchResponse = {
|
export type ProxyFetchResponse = {
|
||||||
@ -23,7 +23,7 @@ export async function performLocalFetch(
|
|||||||
req && {
|
req && {
|
||||||
method: req.method,
|
method: req.method,
|
||||||
headers: req.headers,
|
headers: req.headers,
|
||||||
body: req.body,
|
body: req.base64Body && base64Decode(req.base64Body),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
|
@ -6,11 +6,6 @@ import {
|
|||||||
|
|
||||||
import { YAML } from "$sb/plugos-syscall/mod.ts";
|
import { YAML } from "$sb/plugos-syscall/mod.ts";
|
||||||
|
|
||||||
export type Attribute = {
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts attributes from a tree, optionally cleaning them out of the tree.
|
* Extracts attributes from a tree, optionally cleaning them out of the tree.
|
||||||
* @param tree tree to extract attributes from
|
* @param tree tree to extract attributes from
|
||||||
|
49
plug-api/lib/feed.test.ts
Normal file
49
plug-api/lib/feed.test.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import "$sb/lib/syscall_mock.ts";
|
||||||
|
import { parse } from "../../common/markdown_parser/parse_tree.ts";
|
||||||
|
import buildMarkdown from "../../common/markdown_parser/parser.ts";
|
||||||
|
import { assertEquals } from "../../test_deps.ts";
|
||||||
|
import { extractFeedItems } from "$sb/lib/feed.ts";
|
||||||
|
import { nodeDefToMDExt } from "../../common/markdown_parser/markdown_ext.ts";
|
||||||
|
|
||||||
|
const feedSample1 = `---
|
||||||
|
test: ignore me
|
||||||
|
---
|
||||||
|
# My first item
|
||||||
|
$myid
|
||||||
|
Some text
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# My second item
|
||||||
|
[id: myid2][otherAttribute: 42]
|
||||||
|
And some text
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Completely free form
|
||||||
|
`;
|
||||||
|
|
||||||
|
Deno.test("Test feed parsing", async () => {
|
||||||
|
// Ad hoc added the NamedAnchor extension from the core plug-in inline here
|
||||||
|
const lang = buildMarkdown([nodeDefToMDExt("NamedAnchor", {
|
||||||
|
firstCharacters: ["$"],
|
||||||
|
regex: "\\$[a-zA-Z\\.\\-\\/]+[\\w\\.\\-\\/]*",
|
||||||
|
})]);
|
||||||
|
const tree = parse(lang, feedSample1);
|
||||||
|
const items = await extractFeedItems(tree);
|
||||||
|
assertEquals(items.length, 3);
|
||||||
|
assertEquals(items[0], {
|
||||||
|
id: "myid",
|
||||||
|
text: "Some text",
|
||||||
|
title: "My first item",
|
||||||
|
});
|
||||||
|
assertEquals(items[1], {
|
||||||
|
id: "myid2",
|
||||||
|
attributes: {
|
||||||
|
otherAttribute: 42,
|
||||||
|
},
|
||||||
|
title: "My second item",
|
||||||
|
text: "And some text",
|
||||||
|
});
|
||||||
|
assertEquals(items[2].text, "Completely free form");
|
||||||
|
});
|
103
plug-api/lib/feed.ts
Normal file
103
plug-api/lib/feed.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import {
|
||||||
|
findNodeMatching,
|
||||||
|
findNodeOfType,
|
||||||
|
ParseTree,
|
||||||
|
renderToText,
|
||||||
|
} from "$sb/lib/tree.ts";
|
||||||
|
|
||||||
|
import { extractAttributes } from "$sb/lib/attribute.ts";
|
||||||
|
|
||||||
|
export type FeedItem = {
|
||||||
|
id: string;
|
||||||
|
title?: string;
|
||||||
|
attributes?: Record<string, any>;
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// tree = Document node
|
||||||
|
export async function extractFeedItems(tree: ParseTree): Promise<FeedItem[]> {
|
||||||
|
let nodes: ParseTree[] = [];
|
||||||
|
const feedItems: FeedItem[] = [];
|
||||||
|
if (tree.type !== "Document") {
|
||||||
|
throw new Error("Did not get a document");
|
||||||
|
}
|
||||||
|
// Run through the whole document to find the feed items
|
||||||
|
for (const node of tree.children!) {
|
||||||
|
if (node.type === "FrontMatter") {
|
||||||
|
// Not interested
|
||||||
|
console.log("Ignoring", node);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (node.type === "HorizontalRule") {
|
||||||
|
// Ok we reached the end of a feed item
|
||||||
|
feedItems.push(await nodesToFeedItem(nodes));
|
||||||
|
nodes = [];
|
||||||
|
} else {
|
||||||
|
nodes.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (renderToText({ children: nodes }).trim().length > 0) {
|
||||||
|
feedItems.push(await nodesToFeedItem(nodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
return feedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function nodesToFeedItem(nodes: ParseTree[]): Promise<FeedItem> {
|
||||||
|
const wrapperNode: ParseTree = {
|
||||||
|
children: nodes,
|
||||||
|
};
|
||||||
|
const attributes = await extractAttributes(wrapperNode, true);
|
||||||
|
let id = attributes.id;
|
||||||
|
delete attributes.id;
|
||||||
|
if (!id) {
|
||||||
|
const anchor = findNodeOfType(wrapperNode, "NamedAnchor");
|
||||||
|
if (anchor) {
|
||||||
|
id = anchor.children![0].text!.substring(1);
|
||||||
|
if (id.startsWith("id/")) {
|
||||||
|
id = id.substring(3);
|
||||||
|
}
|
||||||
|
// Empty it out
|
||||||
|
anchor.children = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find a title
|
||||||
|
let title: string | undefined;
|
||||||
|
const titleNode = findNodeMatching(
|
||||||
|
wrapperNode,
|
||||||
|
(node) => !!node.type?.startsWith("ATXHeading"),
|
||||||
|
);
|
||||||
|
if (titleNode) {
|
||||||
|
title = titleNode.children![1].text!.trim();
|
||||||
|
titleNode.children = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = renderToText(wrapperNode).trim();
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
// If all else fails, generate content based ID
|
||||||
|
id = `gen/${djb2Hash(JSON.stringify({ attributes, text }))}`;
|
||||||
|
}
|
||||||
|
// console.log("Extracted attributes", attributes);
|
||||||
|
const feedItem: FeedItem = { id, text };
|
||||||
|
if (title) {
|
||||||
|
feedItem.title = title;
|
||||||
|
}
|
||||||
|
if (Object.keys(attributes).length > 0) {
|
||||||
|
feedItem.attributes = attributes;
|
||||||
|
}
|
||||||
|
return feedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
function djb2Hash(input: string): string {
|
||||||
|
let hash = 5381; // Initial hash value
|
||||||
|
|
||||||
|
for (let i = 0; i < input.length; i++) {
|
||||||
|
// Update the hash value by shifting and adding the character code
|
||||||
|
hash = (hash * 33) ^ input.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the hash to a hexadecimal string representation
|
||||||
|
return hash.toString(16);
|
||||||
|
}
|
@ -1,15 +1,33 @@
|
|||||||
|
import { init } from "https://esm.sh/v131/node_events.js";
|
||||||
import type {
|
import type {
|
||||||
ProxyFetchRequest,
|
ProxyFetchRequest,
|
||||||
ProxyFetchResponse,
|
ProxyFetchResponse,
|
||||||
} from "../../common/proxy_fetch.ts";
|
} from "../../common/proxy_fetch.ts";
|
||||||
import { base64Decode } from "../../plugos/asset_bundle/base64.ts";
|
import {
|
||||||
|
base64Decode,
|
||||||
|
base64Encode,
|
||||||
|
} from "../../plugos/asset_bundle/base64.ts";
|
||||||
|
|
||||||
export function sandboxFetch(
|
export async function sandboxFetch(
|
||||||
url: string,
|
reqInfo: RequestInfo,
|
||||||
options?: ProxyFetchRequest,
|
options?: ProxyFetchRequest,
|
||||||
): Promise<ProxyFetchResponse> {
|
): Promise<ProxyFetchResponse> {
|
||||||
|
if (typeof reqInfo !== "string") {
|
||||||
|
// Request as first argument, let's deconstruct it
|
||||||
|
// console.log("fetch", reqInfo);
|
||||||
|
options = {
|
||||||
|
method: reqInfo.method,
|
||||||
|
headers: Object.fromEntries(reqInfo.headers.entries()),
|
||||||
|
base64Body: reqInfo.body
|
||||||
|
? base64Encode(
|
||||||
|
new Uint8Array(await (new Response(reqInfo.body)).arrayBuffer()),
|
||||||
|
)
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
reqInfo = reqInfo.url;
|
||||||
|
}
|
||||||
// @ts-ignore: monkey patching fetch
|
// @ts-ignore: monkey patching fetch
|
||||||
return syscall("sandboxFetch.fetch", url, options);
|
return syscall("sandboxFetch.fetch", reqInfo, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function monkeyPatchFetch() {
|
export function monkeyPatchFetch() {
|
||||||
@ -17,15 +35,19 @@ export function monkeyPatchFetch() {
|
|||||||
globalThis.nativeFetch = globalThis.fetch;
|
globalThis.nativeFetch = globalThis.fetch;
|
||||||
// @ts-ignore: monkey patching fetch
|
// @ts-ignore: monkey patching fetch
|
||||||
globalThis.fetch = async function (
|
globalThis.fetch = async function (
|
||||||
url: string,
|
reqInfo: RequestInfo,
|
||||||
init?: RequestInit,
|
init?: RequestInit,
|
||||||
): Promise<Response> {
|
): Promise<Response> {
|
||||||
const r = await sandboxFetch(
|
const r = await sandboxFetch(
|
||||||
url,
|
reqInfo,
|
||||||
init && {
|
init && {
|
||||||
method: init.method,
|
method: init.method,
|
||||||
headers: init.headers as Record<string, string>,
|
headers: init.headers as Record<string, string>,
|
||||||
body: init.body as string,
|
base64Body: init.body
|
||||||
|
? base64Encode(
|
||||||
|
new Uint8Array(await (new Response(init.body)).arrayBuffer()),
|
||||||
|
)
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return new Response(r.base64Body ? base64Decode(r.base64Body) : null, {
|
return new Response(r.base64Body ? base64Decode(r.base64Body) : null, {
|
||||||
|
@ -8,7 +8,10 @@ export function base64Decode(s: string): Uint8Array {
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function base64Encode(buffer: Uint8Array): string {
|
export function base64Encode(buffer: Uint8Array | string): string {
|
||||||
|
if (typeof buffer === "string") {
|
||||||
|
buffer = new TextEncoder().encode(buffer);
|
||||||
|
}
|
||||||
let binary = "";
|
let binary = "";
|
||||||
const len = buffer.byteLength;
|
const len = buffer.byteLength;
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
|
@ -109,7 +109,7 @@ export class System<HookT> extends EventEmitter<SystemEvents<HookT>> {
|
|||||||
plug.manifest,
|
plug.manifest,
|
||||||
manifestOverrides[plug.manifest!.name],
|
manifestOverrides[plug.manifest!.name],
|
||||||
);
|
);
|
||||||
console.log("New manifest", plug.manifest);
|
// console.log("New manifest", plug.manifest);
|
||||||
}
|
}
|
||||||
// and there it is!
|
// and there it is!
|
||||||
const manifest = plug.manifest!;
|
const manifest = plug.manifest!;
|
||||||
|
@ -33,11 +33,11 @@ export async function indexLinks({ name, tree }: IndexTreeEvent) {
|
|||||||
pageMeta[k] = v;
|
pageMeta[k] = v;
|
||||||
}
|
}
|
||||||
// Don't index meta data starting with $
|
// Don't index meta data starting with $
|
||||||
// for (const key in pageMeta) {
|
for (const key in pageMeta) {
|
||||||
// if (key.startsWith("$")) {
|
if (key.startsWith("$")) {
|
||||||
// delete pageMeta[key];
|
delete pageMeta[key];
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// console.log("Extracted page meta data", pageMeta);
|
// console.log("Extracted page meta data", pageMeta);
|
||||||
await index.set(name, "meta:", pageMeta);
|
await index.set(name, "meta:", pageMeta);
|
||||||
}
|
}
|
||||||
|
@ -357,6 +357,15 @@ function render(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
case "Escape": {
|
||||||
|
return {
|
||||||
|
name: "span",
|
||||||
|
attrs: {
|
||||||
|
class: "escape",
|
||||||
|
},
|
||||||
|
body: t.children![0].text!.slice(1),
|
||||||
|
};
|
||||||
|
}
|
||||||
// Text
|
// Text
|
||||||
case undefined:
|
case undefined:
|
||||||
return t.text!;
|
return t.text!;
|
||||||
|
@ -8,20 +8,15 @@ export async function publishCommand() {
|
|||||||
const text = await editor.getText();
|
const text = await editor.getText();
|
||||||
const pageName = await editor.getCurrentPage();
|
const pageName = await editor.getCurrentPage();
|
||||||
const tree = await markdown.parseMarkdown(text);
|
const tree = await markdown.parseMarkdown(text);
|
||||||
const { $share } = await extractFrontmatter(tree);
|
let { $share } = await extractFrontmatter(tree);
|
||||||
if (!$share) {
|
if (!$share) {
|
||||||
await editor.flashNotification("Saved.");
|
// Nothing to do here
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!Array.isArray($share)) {
|
if (!Array.isArray($share)) {
|
||||||
await editor.flashNotification(
|
$share = [$share];
|
||||||
"$share front matter must be an array.",
|
|
||||||
"error",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
await editor.flashNotification("Sharing...");
|
await editor.flashNotification("Sharing...");
|
||||||
// Delegate actual publishing to the server
|
|
||||||
try {
|
try {
|
||||||
await publish(pageName, $share);
|
await publish(pageName, $share);
|
||||||
await editor.flashNotification("Done!");
|
await editor.flashNotification("Done!");
|
||||||
@ -31,9 +26,6 @@ export async function publishCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function publish(pageName: string, uris: string[]) {
|
async function publish(pageName: string, uris: string[]) {
|
||||||
if (!Array.isArray(uris)) {
|
|
||||||
uris = [uris];
|
|
||||||
}
|
|
||||||
for (const uri of uris) {
|
for (const uri of uris) {
|
||||||
const publisher = uri.split(":")[0];
|
const publisher = uri.split(":")[0];
|
||||||
const results = await events.dispatchEvent(
|
const results = await events.dispatchEvent(
|
||||||
|
@ -282,13 +282,13 @@ export class HttpServer {
|
|||||||
const body = await request.body({ type: "json" }).value;
|
const body = await request.body({ type: "json" }).value;
|
||||||
try {
|
try {
|
||||||
switch (body.operation) {
|
switch (body.operation) {
|
||||||
case "fetch": {
|
// case "fetch": {
|
||||||
const result = await performLocalFetch(body.url, body.options);
|
// const result = await performLocalFetch(body.url, body.options);
|
||||||
console.log("Proxying fetch request to", body.url);
|
// console.log("Proxying fetch request to", body.url);
|
||||||
response.headers.set("Content-Type", "application/json");
|
// response.headers.set("Content-Type", "application/json");
|
||||||
response.body = JSON.stringify(result);
|
// response.body = JSON.stringify(result);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
case "shell": {
|
case "shell": {
|
||||||
// TODO: Have a nicer way to do this
|
// TODO: Have a nicer way to do this
|
||||||
if (this.options.pagesPath.startsWith("s3://")) {
|
if (this.options.pagesPath.startsWith("s3://")) {
|
||||||
@ -335,7 +335,7 @@ export class HttpServer {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const filePathRegex = "\/(.+\\.[a-zA-Z]+)";
|
const filePathRegex = "\/([^!].+\\.[a-zA-Z]+)";
|
||||||
|
|
||||||
fsRouter
|
fsRouter
|
||||||
.get(
|
.get(
|
||||||
@ -379,7 +379,6 @@ export class HttpServer {
|
|||||||
response.status = 500;
|
response.status = 500;
|
||||||
response.body = e.message;
|
response.body = e.message;
|
||||||
}
|
}
|
||||||
// response.redirect(url);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -459,6 +458,40 @@ export class HttpServer {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.options(filePathRegex, corsMiddleware);
|
.options(filePathRegex, corsMiddleware);
|
||||||
|
|
||||||
|
const proxyPathRegex = "\/!(.+)";
|
||||||
|
fsRouter.all(proxyPathRegex, async ({ params, response, request }) => {
|
||||||
|
let url = params[0];
|
||||||
|
console.log("Requested path to proxy", url, request.method);
|
||||||
|
if (url.startsWith("localhost")) {
|
||||||
|
url = `http://${url}`;
|
||||||
|
} else {
|
||||||
|
url = `https://${url}`;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const req = await fetch(url, {
|
||||||
|
method: request.method,
|
||||||
|
headers: request.headers,
|
||||||
|
body: request.hasBody
|
||||||
|
? request.body({ type: "stream" }).value
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
response.status = req.status;
|
||||||
|
// // Override X-Permssion header to always be "ro"
|
||||||
|
// const newHeaders = new Headers();
|
||||||
|
// for (const [key, value] of req.headers.entries()) {
|
||||||
|
// newHeaders.set(key, value);
|
||||||
|
// }
|
||||||
|
// newHeaders.set("X-Permission", "ro");
|
||||||
|
response.headers = req.headers;
|
||||||
|
response.body = req.body;
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error("Error fetching federated link", e);
|
||||||
|
response.status = 500;
|
||||||
|
response.body = e.message;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
});
|
||||||
return fsRouter;
|
return fsRouter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,7 +394,7 @@ export class Client {
|
|||||||
|
|
||||||
settings = expandPropertyNames(settings);
|
settings = expandPropertyNames(settings);
|
||||||
|
|
||||||
console.log("Settings", settings);
|
// console.log("Settings", settings);
|
||||||
|
|
||||||
if (!settings.indexPage) {
|
if (!settings.indexPage) {
|
||||||
settings.indexPage = "[[index]]";
|
settings.indexPage = "[[index]]";
|
||||||
|
@ -5,6 +5,10 @@ import {
|
|||||||
ProxyFetchResponse,
|
ProxyFetchResponse,
|
||||||
} from "../../common/proxy_fetch.ts";
|
} from "../../common/proxy_fetch.ts";
|
||||||
import type { Client } from "../client.ts";
|
import type { Client } from "../client.ts";
|
||||||
|
import {
|
||||||
|
base64Decode,
|
||||||
|
base64Encode,
|
||||||
|
} from "../../plugos/asset_bundle/base64.ts";
|
||||||
|
|
||||||
export function sandboxFetchSyscalls(
|
export function sandboxFetchSyscalls(
|
||||||
client: Client,
|
client: Client,
|
||||||
@ -13,25 +17,32 @@ export function sandboxFetchSyscalls(
|
|||||||
"sandboxFetch.fetch": async (
|
"sandboxFetch.fetch": async (
|
||||||
_ctx,
|
_ctx,
|
||||||
url: string,
|
url: string,
|
||||||
options: ProxyFetchRequest,
|
options?: ProxyFetchRequest,
|
||||||
): Promise<ProxyFetchResponse> => {
|
): Promise<ProxyFetchResponse> => {
|
||||||
// console.log("Got sandbox fetch ", url);
|
// console.log("Got sandbox fetch ", url, op);
|
||||||
|
url = url.replace(/^https?:\/\//, "");
|
||||||
|
const fetchOptions = options
|
||||||
|
? {
|
||||||
|
method: options.method,
|
||||||
|
headers: options.headers,
|
||||||
|
body: options.base64Body && base64Decode(options.base64Body),
|
||||||
|
}
|
||||||
|
: {};
|
||||||
if (!client.remoteSpacePrimitives) {
|
if (!client.remoteSpacePrimitives) {
|
||||||
// No SB server to proxy the fetch available so let's execute the request directly
|
// No SB server to proxy the fetch available so let's execute the request directly
|
||||||
return performLocalFetch(url, options);
|
return performLocalFetch(url, fetchOptions);
|
||||||
}
|
}
|
||||||
const resp = client.remoteSpacePrimitives.authenticatedFetch(
|
const resp = await client.remoteSpacePrimitives.authenticatedFetch(
|
||||||
`${client.remoteSpacePrimitives.url}/.rpc`,
|
`${client.remoteSpacePrimitives.url}/!${url}`,
|
||||||
{
|
fetchOptions,
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({
|
|
||||||
operation: "fetch",
|
|
||||||
url,
|
|
||||||
options,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
return (await resp).json();
|
const body = await resp.arrayBuffer();
|
||||||
|
return {
|
||||||
|
ok: resp.ok,
|
||||||
|
status: resp.status,
|
||||||
|
headers: Object.fromEntries(resp.headers.entries()),
|
||||||
|
base64Body: base64Encode(new Uint8Array(body)),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,10 @@ export function yamlSyscalls(): SysCallMapping {
|
|||||||
return YAML.parse(text);
|
return YAML.parse(text);
|
||||||
},
|
},
|
||||||
"yaml.stringify": (_ctx, obj: any): string => {
|
"yaml.stringify": (_ctx, obj: any): string => {
|
||||||
return YAML.stringify(obj);
|
return YAML.stringify(obj, {
|
||||||
|
noArrayIndent: true,
|
||||||
|
noCompatMode: true,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,10 @@ release.
|
|||||||
## Next
|
## Next
|
||||||
|
|
||||||
* Cookies set when using SilverBullet's built-in [[Authentication]] are now per domain + port, allowing you to run multiple instances of SB on a single host with different ports without the authentication interfering.
|
* Cookies set when using SilverBullet's built-in [[Authentication]] are now per domain + port, allowing you to run multiple instances of SB on a single host with different ports without the authentication interfering.
|
||||||
|
* Page references in [[SETTINGS]] now use double-bracket notation (optionally) which is nicer, because you’ll get completion. See [[SETTINGS]] for examples.
|
||||||
|
* It is now possible to override [[🔌 Plugs]] manifests. The primary use case for this is to be able to _override keyboard shortcuts_. This feature may still change over time, but you can try it out. See [[SETTINGS]] for an example.
|
||||||
|
* Fixes to syntax coloring
|
||||||
|
* Various internal refactoring in preparation for cool things to come
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -17,7 +21,6 @@ release.
|
|||||||
* New `/page-template` slash command to apply (insert) a page [[🔌 Core/Templates|template]] at the current location
|
* New `/page-template` slash command to apply (insert) a page [[🔌 Core/Templates|template]] at the current location
|
||||||
* When the PWA starts, it will now send you back to the last opened page instead of the index page (you may have to reinstall the PWA for this change to take effect).
|
* When the PWA starts, it will now send you back to the last opened page instead of the index page (you may have to reinstall the PWA for this change to take effect).
|
||||||
* [[Markdown/Syntax Highlighting]] for HTML
|
* [[Markdown/Syntax Highlighting]] for HTML
|
||||||
* [[Frontmatter]] attributes starting with `$` are now indexed again (couldn't remember why we excluded them before)
|
|
||||||
* Various heavy-weight commands (such as {[Space: Reindex]} and {[Directives: Update Entire Space]}) now use an internal message queue, allowing to continue the processing even when interrupted or crashing.
|
* Various heavy-weight commands (such as {[Space: Reindex]} and {[Directives: Update Entire Space]}) now use an internal message queue, allowing to continue the processing even when interrupted or crashing.
|
||||||
* Various internal refactorings
|
* Various internal refactorings
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ For the sake of simplicity, we will use the `page` data source and limit the res
|
|||||||
<!-- #query page limit 3 -->
|
<!-- #query page limit 3 -->
|
||||||
|name |lastModified |contentType |size|perm|pageAttribute|
|
|name |lastModified |contentType |size|perm|pageAttribute|
|
||||||
|----------|-------------|-------------|----|--|-----|
|
|----------|-------------|-------------|----|--|-----|
|
||||||
|API |1691499342795|text/markdown|1879|rw| |
|
|API |1692191260028|text/markdown|2200|rw| |
|
||||||
|Attributes|1691176701257|text/markdown|1466|rw|hello|
|
|Attributes|1691176701257|text/markdown|1466|rw|hello|
|
||||||
|Authelia |1688482500313|text/markdown|866 |rw| |
|
|Authelia |1688482500313|text/markdown|866 |rw| |
|
||||||
<!-- /query -->
|
<!-- /query -->
|
||||||
@ -171,13 +171,13 @@ For the sake of simplicity, we will use the `page` data source and limit the res
|
|||||||
**Result:** Okay, this is what we wanted but there is also information such as `perm`, `type` and `lastModified` that we don't need.
|
**Result:** Okay, this is what we wanted but there is also information such as `perm`, `type` and `lastModified` that we don't need.
|
||||||
|
|
||||||
<!-- #query page where type = "plug" order by lastModified desc limit 5 -->
|
<!-- #query page where type = "plug" order by lastModified desc limit 5 -->
|
||||||
|name |lastModified |contentType |size|perm|type|repo |uri |author |share-support|
|
|name |lastModified |contentType |size|perm|type|uri |repo |author |share-support|
|
||||||
|--|--|--|--|--|--|--|--|--|--|
|
|--|--|--|--|--|--|--|--|--|--|
|
||||||
|🔌 Share |1691177844386|text/markdown|693 |rw|plug|https://github.com/silverbulletmd/silverbullet | | | |
|
|🔌 Twitter |1692810059854|text/markdown|1266|rw|plug|github:silverbulletmd/silverbullet-twitter/twitter.plug.js |https://github.com/silverbulletmd/silverbullet-twitter |SilverBullet Authors| |
|
||||||
|🔌 Github |1691137925014|text/markdown|2206|rw|plug|https://github.com/silverbulletmd/silverbullet-github |github:silverbulletmd/silverbullet-github/github.plug.js |Zef Hemel|true|
|
|🔌 Share |1691177844386|text/markdown|693 |rw|plug| |https://github.com/silverbulletmd/silverbullet | | |
|
||||||
|🔌 Mattermost|1691137924741|text/markdown|3535|rw|plug|https://github.com/silverbulletmd/silverbullet-mattermost|github:silverbulletmd/silverbullet-mattermost/mattermost.plug.json|Zef Hemel|true|
|
|🔌 Github |1691137925014|text/markdown|2206|rw|plug|github:silverbulletmd/silverbullet-github/github.plug.js |https://github.com/silverbulletmd/silverbullet-github |Zef Hemel |true|
|
||||||
|🔌 Git |1691137924435|text/markdown|1112|rw|plug|https://github.com/silverbulletmd/silverbullet-git |github:silverbulletmd/silverbullet-git/git.plug.js |Zef Hemel| |
|
|🔌 Mattermost|1691137924741|text/markdown|3535|rw|plug|github:silverbulletmd/silverbullet-mattermost/mattermost.plug.json|https://github.com/silverbulletmd/silverbullet-mattermost|Zef Hemel |true|
|
||||||
|🔌 Ghost |1691137922296|text/markdown|1733|rw|plug|https://github.com/silverbulletmd/silverbullet-ghost |github:silverbulletmd/silverbullet-ghost/ghost.plug.js |Zef Hemel|true|
|
|🔌 Git |1691137924435|text/markdown|1112|rw|plug|github:silverbulletmd/silverbullet-git/git.plug.js |https://github.com/silverbulletmd/silverbullet-git |Zef Hemel | |
|
||||||
<!-- /query -->
|
<!-- /query -->
|
||||||
|
|
||||||
#### 6.3 Query to select only certain fields
|
#### 6.3 Query to select only certain fields
|
||||||
@ -189,13 +189,13 @@ and `repo` columns and then sort by last modified time.
|
|||||||
from a visual perspective.
|
from a visual perspective.
|
||||||
|
|
||||||
<!-- #query page select name, author, repo where type = "plug" order by lastModified desc limit 5 -->
|
<!-- #query page select name, author, repo where type = "plug" order by lastModified desc limit 5 -->
|
||||||
|name |author |repo |
|
|name |author |repo |
|
||||||
|--|--|--|
|
|--|--|--|
|
||||||
|🔌 Share | |https://github.com/silverbulletmd/silverbullet |
|
|🔌 Twitter |SilverBullet Authors|https://github.com/silverbulletmd/silverbullet-twitter |
|
||||||
|🔌 Github |Zef Hemel|https://github.com/silverbulletmd/silverbullet-github |
|
|🔌 Share | |https://github.com/silverbulletmd/silverbullet |
|
||||||
|🔌 Mattermost|Zef Hemel|https://github.com/silverbulletmd/silverbullet-mattermost|
|
|🔌 Github |Zef Hemel |https://github.com/silverbulletmd/silverbullet-github |
|
||||||
|🔌 Git |Zef Hemel|https://github.com/silverbulletmd/silverbullet-git |
|
|🔌 Mattermost|Zef Hemel |https://github.com/silverbulletmd/silverbullet-mattermost|
|
||||||
|🔌 Ghost |Zef Hemel|https://github.com/silverbulletmd/silverbullet-ghost |
|
|🔌 Git |Zef Hemel |https://github.com/silverbulletmd/silverbullet-git |
|
||||||
<!-- /query -->
|
<!-- /query -->
|
||||||
|
|
||||||
#### 6.4 Display the data in a format defined by a template
|
#### 6.4 Display the data in a format defined by a template
|
||||||
@ -205,11 +205,11 @@ from a visual perspective.
|
|||||||
**Result:** Here you go. This is the result we would like to achieve 🎉. Did you see how I used `render` and `template/plug` in a query? 🚀
|
**Result:** Here you go. This is the result we would like to achieve 🎉. Did you see how I used `render` and `template/plug` in a query? 🚀
|
||||||
|
|
||||||
<!-- #query page where type = "plug" order by lastModified desc limit 5 render [[template/plug]] -->
|
<!-- #query page where type = "plug" order by lastModified desc limit 5 render [[template/plug]] -->
|
||||||
|
* [[🔌 Twitter]] by **SilverBullet Authors** ([repo](https://github.com/silverbulletmd/silverbullet-twitter))
|
||||||
* [[🔌 Share]]
|
* [[🔌 Share]]
|
||||||
* [[🔌 Github]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-github))
|
* [[🔌 Github]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-github))
|
||||||
* [[🔌 Mattermost]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-mattermost))
|
* [[🔌 Mattermost]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-mattermost))
|
||||||
* [[🔌 Git]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-git))
|
* [[🔌 Git]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-git))
|
||||||
* [[🔌 Ghost]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-ghost))
|
|
||||||
<!-- /query -->
|
<!-- /query -->
|
||||||
|
|
||||||
PS: You don't need to select only certain fields to use templates. Templates are
|
PS: You don't need to select only certain fields to use templates. Templates are
|
||||||
|
Loading…
Reference in New Issue
Block a user