mirror of
https://github.com/wavetermdev/waveterm
synced 2026-05-24 09:18:27 +00:00
Big simplification. Remove the FileShare interface that abstracted wsh://, s3://, and wavefile:// files. It produced a lot of complexity for very little usage. We're just going to focus on the wsh:// implementation since that's core to our remote workflows. * remove s3 implementation (and connections, and picker items for preview) * remove capabilities for FE * remove wavefile backend impl as well * simplify wsh file remote backend * remove ability to copy/move/ls recursively * limit file transfers to 32m the longer term fix here is to use the new streaming RPC primitives. they have full end-to-end flow-control built in and will not create pipeline stalls, blocking other requests, and OOM issues. these other impls had to be removed (or fixed) because transferring large files could cause stalls or crashes with the new router infrastructure.
95 lines
3.2 KiB
TypeScript
95 lines
3.2 KiB
TypeScript
// Copyright 2025, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0s
|
|
|
|
import { getWebServerEndpoint } from "@/util/endpoints";
|
|
import { boundNumber, isBlank } from "@/util/util";
|
|
import { generate as generateCSS, parse as parseCSS, walk as walkCSS } from "css-tree";
|
|
|
|
function encodeFileURL(file: string) {
|
|
const webEndpoint = getWebServerEndpoint();
|
|
const fileUri = formatRemoteUri(file, "local");
|
|
const rtn = webEndpoint + `/wave/stream-file?path=${encodeURIComponent(fileUri)}&no404=1`;
|
|
return rtn;
|
|
}
|
|
|
|
export function processBackgroundUrls(cssText: string): string {
|
|
if (isBlank(cssText)) {
|
|
return null;
|
|
}
|
|
cssText = cssText.trim();
|
|
if (cssText.endsWith(";")) {
|
|
cssText = cssText.slice(0, -1);
|
|
}
|
|
const attrRe = /^background(-image)?\s*:\s*/i;
|
|
cssText = cssText.replace(attrRe, "");
|
|
const ast = parseCSS("background: " + cssText, {
|
|
context: "declaration",
|
|
});
|
|
let hasUnsafeUrl = false;
|
|
walkCSS(ast, {
|
|
visit: "Url",
|
|
enter(node) {
|
|
const originalUrl = node.value.trim();
|
|
if (
|
|
originalUrl.startsWith("http:") ||
|
|
originalUrl.startsWith("https:") ||
|
|
originalUrl.startsWith("data:")
|
|
) {
|
|
return;
|
|
}
|
|
// allow file:/// urls (if they are absolute)
|
|
if (originalUrl.startsWith("file://")) {
|
|
const path = originalUrl.slice(7);
|
|
if (!path.startsWith("/")) {
|
|
console.log(`Invalid background, contains a non-absolute file URL: ${originalUrl}`);
|
|
hasUnsafeUrl = true;
|
|
return;
|
|
}
|
|
const newUrl = encodeFileURL(path);
|
|
node.value = newUrl;
|
|
return;
|
|
}
|
|
// allow absolute paths
|
|
if (originalUrl.startsWith("/") || originalUrl.startsWith("~/") || /^[a-zA-Z]:(\/|\\)/.test(originalUrl)) {
|
|
const newUrl = encodeFileURL(originalUrl);
|
|
node.value = newUrl;
|
|
return;
|
|
}
|
|
hasUnsafeUrl = true;
|
|
console.log(`Invalid background, contains an unsafe URL scheme: ${originalUrl}`);
|
|
},
|
|
});
|
|
if (hasUnsafeUrl) {
|
|
return null;
|
|
}
|
|
const rtnStyle = generateCSS(ast);
|
|
if (rtnStyle == null) {
|
|
return null;
|
|
}
|
|
return rtnStyle.replace(/^background:\s*/, "");
|
|
}
|
|
|
|
export function computeBgStyleFromMeta(meta: MetaType, defaultOpacity: number = null): React.CSSProperties {
|
|
const bgAttr = meta?.["bg"];
|
|
if (isBlank(bgAttr)) {
|
|
return null;
|
|
}
|
|
try {
|
|
const processedBg = processBackgroundUrls(bgAttr);
|
|
const rtn: React.CSSProperties = {};
|
|
rtn.background = processedBg;
|
|
rtn.opacity = boundNumber(meta["bg:opacity"], 0, 1) ?? defaultOpacity;
|
|
if (!isBlank(meta?.["bg:blendmode"])) {
|
|
rtn.backgroundBlendMode = meta["bg:blendmode"];
|
|
}
|
|
return rtn;
|
|
} catch (e) {
|
|
console.error("error processing background", e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function formatRemoteUri(path: string, connection: string): string {
|
|
connection = connection ?? "local";
|
|
return `wsh://${connection}/${path}`;
|
|
}
|