mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-02-21 02:33:34 +01:00
fix stream-file urls to use new remoteuri (#1984)
This commit is contained in:
parent
1929c70e04
commit
e15d38a795
@ -87,7 +87,10 @@ export function shFrameNavHandler(event: Electron.Event<Electron.WebContentsWill
|
||||
}
|
||||
if (
|
||||
event.frame.name == "pdfview" &&
|
||||
(url.startsWith("blob:file:///") || url.startsWith(getWebServerEndpoint() + "/wave/stream-file?"))
|
||||
(url.startsWith("blob:file:///") ||
|
||||
url.startsWith(getWebServerEndpoint() + "/wave/stream-file?") ||
|
||||
url.startsWith(getWebServerEndpoint() + "/wave/stream-file/") ||
|
||||
url.startsWith(getWebServerEndpoint() + "/wave/stream-local-file?"))
|
||||
) {
|
||||
// allowed
|
||||
return;
|
||||
|
@ -240,7 +240,9 @@ electron.ipcMain.on("webview-image-contextmenu", (event: electron.IpcMainEvent,
|
||||
});
|
||||
|
||||
electron.ipcMain.on("download", (event, payload) => {
|
||||
const streamingUrl = getWebServerEndpoint() + "/wave/stream-file?path=" + encodeURIComponent(payload.filePath);
|
||||
const baseName = encodeURIComponent(path.basename(payload.filePath));
|
||||
const streamingUrl =
|
||||
getWebServerEndpoint() + "/wave/stream-file/" + baseName + "?path=" + encodeURIComponent(payload.filePath);
|
||||
event.sender.downloadURL(streamingUrl);
|
||||
});
|
||||
|
||||
|
@ -241,6 +241,7 @@ const BlockFrame_Header = ({
|
||||
icon: "link-slash",
|
||||
title: "wsh is not installed for this connection",
|
||||
};
|
||||
const showNoWshButton = manageConnection && wshProblem && !util.isBlank(connName) && !connName.startsWith("aws:");
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -263,9 +264,7 @@ const BlockFrame_Header = ({
|
||||
changeConnModalAtom={changeConnModalAtom}
|
||||
/>
|
||||
)}
|
||||
{manageConnection && wshProblem && (
|
||||
<IconButton decl={wshInstallButton} className="block-frame-header-iconbutton" />
|
||||
)}
|
||||
{showNoWshButton && <IconButton decl={wshInstallButton} className="block-frame-header-iconbutton" />}
|
||||
<div className="block-frame-textelems-wrapper">{headerTextElems}</div>
|
||||
<div className="block-frame-end-icons">{endIconsElem}</div>
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@
|
||||
import { RpcApi } from "@/app/store/wshclientapi";
|
||||
import { TabRpcClient } from "@/app/store/wshrpcutil";
|
||||
import { getWebServerEndpoint } from "@/util/endpoints";
|
||||
import { isBlank, makeConnRoute } from "@/util/util";
|
||||
import { formatRemoteUri } from "@/util/waveutil";
|
||||
import parseSrcSet from "parse-srcset";
|
||||
|
||||
export type MarkdownContentBlockType = {
|
||||
@ -158,19 +158,13 @@ export const resolveRemoteFile = async (filepath: string, resolveOpts: MarkdownR
|
||||
if (!filepath || filepath.startsWith("http://") || filepath.startsWith("https://")) {
|
||||
return filepath;
|
||||
}
|
||||
|
||||
try {
|
||||
const route = makeConnRoute(resolveOpts.connName);
|
||||
const fileInfo = await RpcApi.RemoteFileJoinCommand(TabRpcClient, [resolveOpts.baseDir, filepath], {
|
||||
route: route,
|
||||
});
|
||||
|
||||
const baseDirUri = formatRemoteUri(resolveOpts.baseDir, resolveOpts.connName);
|
||||
const fileInfo = await RpcApi.FileJoinCommand(TabRpcClient, [baseDirUri, filepath]);
|
||||
const remoteUri = formatRemoteUri(fileInfo.path, resolveOpts.connName);
|
||||
console.log("markdown resolve", resolveOpts, filepath, "=>", baseDirUri, remoteUri);
|
||||
const usp = new URLSearchParams();
|
||||
usp.set("path", fileInfo.path);
|
||||
if (!isBlank(resolveOpts.connName)) {
|
||||
usp.set("connection", resolveOpts.connName);
|
||||
}
|
||||
|
||||
usp.set("path", remoteUri);
|
||||
return getWebServerEndpoint() + "/wave/stream-file?" + usp.toString();
|
||||
} catch (err) {
|
||||
console.warn("Failed to resolve remote file:", filepath, err);
|
||||
|
@ -348,6 +348,7 @@ const ChangeConnectionBlockModal = React.memo(
|
||||
const connStatusMap = new Map<string, ConnStatus>();
|
||||
const fullConfig = jotai.useAtomValue(atoms.fullConfigAtom);
|
||||
let filterOutNowsh = util.useAtomValueSafe(viewModel.filterOutNowsh) ?? true;
|
||||
const showS3 = util.useAtomValueSafe(viewModel.showS3) ?? false;
|
||||
|
||||
let maxActiveConnNum = 1;
|
||||
for (const conn of allConnStatus) {
|
||||
@ -436,14 +437,17 @@ const ChangeConnectionBlockModal = React.memo(
|
||||
fullConfig,
|
||||
filterOutNowsh
|
||||
);
|
||||
const s3Suggestions = getS3Suggestions(
|
||||
s3List,
|
||||
connection,
|
||||
connSelected,
|
||||
connStatusMap,
|
||||
fullConfig,
|
||||
filterOutNowsh
|
||||
);
|
||||
let s3Suggestions: SuggestionConnectionScope = null;
|
||||
if (showS3) {
|
||||
s3Suggestions = getS3Suggestions(
|
||||
s3List,
|
||||
connection,
|
||||
connSelected,
|
||||
connStatusMap,
|
||||
fullConfig,
|
||||
filterOutNowsh
|
||||
);
|
||||
}
|
||||
const connectionsEditItem = getConnectionsEditItem(changeConnModalAtom, connSelected);
|
||||
const disconnectItem = getDisconnectItem(connection, connStatusMap);
|
||||
const newConnectionSuggestionItem = getNewConnectionSuggestionItem(
|
||||
|
@ -9,9 +9,10 @@ import { ContextMenuModel } from "@/app/store/contextmenu";
|
||||
import { PLATFORM, atoms, createBlock, getApi, globalStore } from "@/app/store/global";
|
||||
import { RpcApi } from "@/app/store/wshclientapi";
|
||||
import { TabRpcClient } from "@/app/store/wshrpcutil";
|
||||
import { formatRemoteUri, type PreviewModel } from "@/app/view/preview/preview";
|
||||
import { type PreviewModel } from "@/app/view/preview/preview";
|
||||
import { checkKeyPressed, isCharacterKeyEvent } from "@/util/keyutil";
|
||||
import { fireAndForget, isBlank, makeNativeLabel } from "@/util/util";
|
||||
import { formatRemoteUri } from "@/util/waveutil";
|
||||
import { offset, useDismiss, useFloating, useInteractions } from "@floating-ui/react";
|
||||
import {
|
||||
Column,
|
||||
@ -575,7 +576,8 @@ function TableBody({
|
||||
{
|
||||
label: "Download File",
|
||||
click: () => {
|
||||
getApi().downloadFile(normPath);
|
||||
const remoteUri = formatRemoteUri(finfo.path, conn);
|
||||
getApi().downloadFile(remoteUri);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -38,6 +38,7 @@ import {
|
||||
makeNativeLabel,
|
||||
stringToBase64,
|
||||
} from "@/util/util";
|
||||
import { formatRemoteUri } from "@/util/waveutil";
|
||||
import { Monaco } from "@monaco-editor/react";
|
||||
import clsx from "clsx";
|
||||
import { Atom, atom, Getter, PrimitiveAtom, useAtom, useAtomValue, useSetAtom, WritableAtom } from "jotai";
|
||||
@ -180,6 +181,8 @@ export class PreviewModel implements ViewModel {
|
||||
directoryKeyDownHandler: (waveEvent: WaveKeyboardEvent) => boolean;
|
||||
codeEditKeyDownHandler: (waveEvent: WaveKeyboardEvent) => boolean;
|
||||
|
||||
showS3 = atom(true);
|
||||
|
||||
constructor(blockId: string, nodeModel: BlockNodeModel) {
|
||||
this.viewType = "preview";
|
||||
this.blockId = blockId;
|
||||
@ -936,13 +939,14 @@ function StreamingPreview({ model }: SpecializedViewProps) {
|
||||
const conn = useAtomValue(model.connection);
|
||||
const fileInfo = useAtomValue(model.statFile);
|
||||
const filePath = fileInfo.path;
|
||||
const remotePath = formatRemoteUri(filePath, conn);
|
||||
const usp = new URLSearchParams();
|
||||
usp.set("path", filePath);
|
||||
usp.set("path", remotePath);
|
||||
if (conn != null) {
|
||||
usp.set("connection", conn);
|
||||
}
|
||||
const streamingUrl = getWebServerEndpoint() + "/wave/stream-file?" + usp.toString();
|
||||
if (fileInfo.mimetype == "application/pdf") {
|
||||
const streamingUrl = `${getWebServerEndpoint()}/wave/stream-file?${usp.toString()}`;
|
||||
if (fileInfo.mimetype === "application/pdf") {
|
||||
return (
|
||||
<div className="view-preview view-preview-pdf">
|
||||
<iframe src={streamingUrl} width="100%" height="100%" name="pdfview" />
|
||||
@ -1304,16 +1308,4 @@ const ErrorOverlay = memo(({ errorMsg, resetOverlay }: { errorMsg: ErrorMsg; res
|
||||
);
|
||||
});
|
||||
|
||||
function formatRemoteUri(path: string, connection: string): string {
|
||||
connection = connection ?? "local";
|
||||
// TODO: We need a better way to handle s3 paths
|
||||
let retVal: string;
|
||||
if (connection.startsWith("aws:")) {
|
||||
retVal = `${connection}:s3://${path ?? ""}`;
|
||||
} else {
|
||||
retVal = `wsh://${connection}/${path}`;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
export { formatRemoteUri, PreviewView };
|
||||
export { PreviewView };
|
||||
|
@ -119,7 +119,8 @@ function TermSticker({ sticker, config }: { sticker: StickerType; config: Sticke
|
||||
if (sticker.imgsrc == null) {
|
||||
return null;
|
||||
}
|
||||
const streamingUrl = getWebServerEndpoint() + "/wave/stream-file?path=" + encodeURIComponent(sticker.imgsrc);
|
||||
const streamingUrl =
|
||||
getWebServerEndpoint() + "/wave/stream-local-file?path=" + encodeURIComponent(sticker.imgsrc);
|
||||
return (
|
||||
<div className="term-sticker term-sticker-image" style={style} onClick={clickHandler}>
|
||||
<img src={streamingUrl} />
|
||||
|
3
frontend/types/custom.d.ts
vendored
3
frontend/types/custom.d.ts
vendored
@ -292,6 +292,9 @@ declare global {
|
||||
// If true, filters out 'nowsh' connections (when managing connections)
|
||||
filterOutNowsh?: jotai.Atom<boolean>;
|
||||
|
||||
// if true, show s3 connections in picker
|
||||
showS3?: jotai.Atom<boolean>;
|
||||
|
||||
// If true, removes padding inside the block content area.
|
||||
noPadding?: jotai.Atom<boolean>;
|
||||
|
||||
|
@ -7,7 +7,9 @@ import { generate as generateCSS, parse as parseCSS, walk as walkCSS } from "css
|
||||
|
||||
function encodeFileURL(file: string) {
|
||||
const webEndpoint = getWebServerEndpoint();
|
||||
return webEndpoint + `/wave/stream-file?path=${encodeURIComponent(file)}&no404=1`;
|
||||
const fileUri = formatRemoteUri(file, "local");
|
||||
const rtn = webEndpoint + `/wave/stream-file?path=${encodeURIComponent(fileUri)}&no404=1`;
|
||||
return rtn;
|
||||
}
|
||||
|
||||
export function processBackgroundUrls(cssText: string): string {
|
||||
@ -86,3 +88,15 @@ export function computeBgStyleFromMeta(meta: MetaType, defaultOpacity: number =
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatRemoteUri(path: string, connection: string): string {
|
||||
connection = connection ?? "local";
|
||||
// TODO: We need a better way to handle s3 paths
|
||||
let retVal: string;
|
||||
if (connection.startsWith("aws:")) {
|
||||
retVal = `${connection}:s3://${path ?? ""}`;
|
||||
} else {
|
||||
retVal = `wsh://${connection}/${path}`;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
@ -774,20 +774,13 @@ func (c S3Client) Delete(ctx context.Context, conn *connparse.Connection, recurs
|
||||
|
||||
func (c S3Client) Join(ctx context.Context, conn *connparse.Connection, parts ...string) (*wshrpc.FileInfo, error) {
|
||||
var joinParts []string
|
||||
if conn.Host == "" || conn.Host == fspath.Separator {
|
||||
if conn.Path == "" || conn.Path == fspath.Separator {
|
||||
joinParts = parts
|
||||
} else {
|
||||
joinParts = append([]string{conn.Path}, parts...)
|
||||
}
|
||||
} else if conn.Path == "" || conn.Path == "/" {
|
||||
joinParts = append([]string{conn.Host}, parts...)
|
||||
if conn.Path == "" || conn.Path == fspath.Separator {
|
||||
joinParts = parts
|
||||
} else {
|
||||
joinParts = append([]string{conn.Host, conn.Path}, parts...)
|
||||
joinParts = append([]string{conn.Path}, parts...)
|
||||
}
|
||||
|
||||
conn.Path = fspath.Join(joinParts...)
|
||||
|
||||
return c.Stat(ctx, conn)
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"github.com/wavetermdev/waveterm/pkg/docsite"
|
||||
"github.com/wavetermdev/waveterm/pkg/filestore"
|
||||
"github.com/wavetermdev/waveterm/pkg/panichandler"
|
||||
"github.com/wavetermdev/waveterm/pkg/remote/fileshare"
|
||||
"github.com/wavetermdev/waveterm/pkg/schema"
|
||||
"github.com/wavetermdev/waveterm/pkg/service"
|
||||
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
|
||||
@ -251,6 +252,10 @@ func handleRemoteStreamFile(w http.ResponseWriter, req *http.Request, conn strin
|
||||
route := wshutil.MakeConnectionRouteId(conn)
|
||||
rpcOpts := &wshrpc.RpcOpts{Route: route, Timeout: 60 * 1000}
|
||||
rtnCh := wshclient.RemoteStreamFileCommand(client, streamFileData, rpcOpts)
|
||||
return handleRemoteStreamFileFromCh(w, req, path, rtnCh, rpcOpts.StreamCancelFn, no404)
|
||||
}
|
||||
|
||||
func handleRemoteStreamFileFromCh(w http.ResponseWriter, req *http.Request, path string, rtnCh <-chan wshrpc.RespOrErrorUnion[wshrpc.FileData], streamCancelFn func(), no404 bool) error {
|
||||
firstPk := true
|
||||
var fileInfo *wshrpc.FileInfo
|
||||
loopDone := false
|
||||
@ -265,7 +270,9 @@ func handleRemoteStreamFile(w http.ResponseWriter, req *http.Request, conn strin
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
rpcOpts.StreamCancelFn()
|
||||
if streamCancelFn != nil {
|
||||
streamCancelFn()
|
||||
}
|
||||
return ctx.Err()
|
||||
case respUnion, ok := <-rtnCh:
|
||||
if !ok {
|
||||
@ -311,6 +318,16 @@ func handleRemoteStreamFile(w http.ResponseWriter, req *http.Request, conn strin
|
||||
}
|
||||
}
|
||||
|
||||
func handleStreamLocalFile(w http.ResponseWriter, r *http.Request) {
|
||||
path := r.URL.Query().Get("path")
|
||||
if path == "" {
|
||||
http.Error(w, "path is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
no404 := r.URL.Query().Get("no404")
|
||||
handleLocalStreamFile(w, r, path, no404 != "")
|
||||
}
|
||||
|
||||
func handleStreamFile(w http.ResponseWriter, r *http.Request) {
|
||||
conn := r.URL.Query().Get("connection")
|
||||
if conn == "" {
|
||||
@ -322,14 +339,16 @@ func handleStreamFile(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
no404 := r.URL.Query().Get("no404")
|
||||
if conn == wshrpc.LocalConnName {
|
||||
handleLocalStreamFile(w, r, path, no404 != "")
|
||||
} else {
|
||||
err := handleRemoteStreamFile(w, r, conn, path, no404 != "")
|
||||
if err != nil {
|
||||
log.Printf("error streaming remote file %q %q: %v\n", conn, path, err)
|
||||
http.Error(w, fmt.Sprintf("error streaming file: %v", err), http.StatusInternalServerError)
|
||||
}
|
||||
data := wshrpc.FileData{
|
||||
Info: &wshrpc.FileInfo{
|
||||
Path: path,
|
||||
},
|
||||
}
|
||||
rtnCh := fileshare.ReadStream(r.Context(), data)
|
||||
err := handleRemoteStreamFileFromCh(w, r, path, rtnCh, nil, no404 != "")
|
||||
if err != nil {
|
||||
log.Printf("error streaming file %q %q: %v\n", conn, path, err)
|
||||
http.Error(w, fmt.Sprintf("error streaming file: %v", err), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
@ -423,7 +442,9 @@ const schemaPrefix = "/schema/"
|
||||
// blocking
|
||||
func RunWebServer(listener net.Listener) {
|
||||
gr := mux.NewRouter()
|
||||
gr.HandleFunc("/wave/stream-local-file", WebFnWrap(WebFnOpts{AllowCaching: true}, handleStreamLocalFile))
|
||||
gr.HandleFunc("/wave/stream-file", WebFnWrap(WebFnOpts{AllowCaching: true}, handleStreamFile))
|
||||
gr.PathPrefix("/wave/stream-file/").HandlerFunc(WebFnWrap(WebFnOpts{AllowCaching: true}, handleStreamFile))
|
||||
gr.HandleFunc("/wave/file", WebFnWrap(WebFnOpts{AllowCaching: false}, handleWaveFile))
|
||||
gr.HandleFunc("/wave/service", WebFnWrap(WebFnOpts{JsonErrors: true}, handleService))
|
||||
gr.HandleFunc("/vdom/{uuid}/{path:.*}", WebFnWrap(WebFnOpts{AllowCaching: true}, handleVDom))
|
||||
|
Loading…
Reference in New Issue
Block a user