mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
04c4f0a203
New context menu options are available in the directory preview to create and rename files and directories It's missing three pieces of functionality, none of which are a regression: - Editing or creating an entry does not update the focused index. Focus index right now is pretty dumb, it doesn't factor in the column sorting so if you change that, the selected item will change to whatever is now at that index. We should update this so we use the actual file name to determine which element to focus and let the table determine which index to then highlight given the current sorting algo - Open in native preview should not be an option on remote connections with the exception of WSL, where it should resolve the file in the Windows filesystem, rather than the WSL one - We should catch CRUD errors in the dir preview and display a popup
187 lines
5.6 KiB
Go
187 lines
5.6 KiB
Go
package fileservice
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/filestore"
|
|
"github.com/wavetermdev/waveterm/pkg/tsgen/tsgenmeta"
|
|
"github.com/wavetermdev/waveterm/pkg/wconfig"
|
|
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
|
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
|
|
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshserver"
|
|
"github.com/wavetermdev/waveterm/pkg/wshutil"
|
|
)
|
|
|
|
const MaxFileSize = 10 * 1024 * 1024 // 10M
|
|
const DefaultTimeout = 2 * time.Second
|
|
|
|
type FileService struct{}
|
|
|
|
type FullFile struct {
|
|
Info *wshrpc.FileInfo `json:"info"`
|
|
Data64 string `json:"data64"` // base64 encoded
|
|
}
|
|
|
|
func (fs *FileService) SaveFile_Meta() tsgenmeta.MethodMeta {
|
|
return tsgenmeta.MethodMeta{
|
|
Desc: "save file",
|
|
ArgNames: []string{"connection", "path", "data64"},
|
|
}
|
|
}
|
|
|
|
func (fs *FileService) SaveFile(connection string, path string, data64 string) error {
|
|
if connection == "" {
|
|
connection = wshrpc.LocalConnName
|
|
}
|
|
connRoute := wshutil.MakeConnectionRouteId(connection)
|
|
client := wshserver.GetMainRpcClient()
|
|
writeData := wshrpc.CommandRemoteWriteFileData{Path: path, Data64: data64}
|
|
return wshclient.RemoteWriteFileCommand(client, writeData, &wshrpc.RpcOpts{Route: connRoute})
|
|
}
|
|
|
|
func (fs *FileService) StatFile_Meta() tsgenmeta.MethodMeta {
|
|
return tsgenmeta.MethodMeta{
|
|
Desc: "get file info",
|
|
ArgNames: []string{"connection", "path"},
|
|
}
|
|
}
|
|
|
|
func (fs *FileService) StatFile(connection string, path string) (*wshrpc.FileInfo, error) {
|
|
if connection == "" {
|
|
connection = wshrpc.LocalConnName
|
|
}
|
|
connRoute := wshutil.MakeConnectionRouteId(connection)
|
|
client := wshserver.GetMainRpcClient()
|
|
return wshclient.RemoteFileInfoCommand(client, path, &wshrpc.RpcOpts{Route: connRoute})
|
|
}
|
|
|
|
func (fs *FileService) Mkdir(connection string, path string) error {
|
|
if connection == "" {
|
|
connection = wshrpc.LocalConnName
|
|
}
|
|
connRoute := wshutil.MakeConnectionRouteId(connection)
|
|
client := wshserver.GetMainRpcClient()
|
|
return wshclient.RemoteMkdirCommand(client, path, &wshrpc.RpcOpts{Route: connRoute})
|
|
}
|
|
|
|
func (fs *FileService) TouchFile(connection string, path string) error {
|
|
if connection == "" {
|
|
connection = wshrpc.LocalConnName
|
|
}
|
|
connRoute := wshutil.MakeConnectionRouteId(connection)
|
|
client := wshserver.GetMainRpcClient()
|
|
return wshclient.RemoteFileTouchCommand(client, path, &wshrpc.RpcOpts{Route: connRoute})
|
|
}
|
|
|
|
func (fs *FileService) Rename(connection string, path string, newPath string) error {
|
|
if connection == "" {
|
|
connection = wshrpc.LocalConnName
|
|
}
|
|
connRoute := wshutil.MakeConnectionRouteId(connection)
|
|
client := wshserver.GetMainRpcClient()
|
|
return wshclient.RemoteFileRenameCommand(client, [2]string{path, newPath}, &wshrpc.RpcOpts{Route: connRoute})
|
|
}
|
|
|
|
func (fs *FileService) ReadFile_Meta() tsgenmeta.MethodMeta {
|
|
return tsgenmeta.MethodMeta{
|
|
Desc: "read file",
|
|
ArgNames: []string{"connection", "path"},
|
|
}
|
|
}
|
|
|
|
func (fs *FileService) ReadFile(connection string, path string) (*FullFile, error) {
|
|
if connection == "" {
|
|
connection = wshrpc.LocalConnName
|
|
}
|
|
connRoute := wshutil.MakeConnectionRouteId(connection)
|
|
client := wshserver.GetMainRpcClient()
|
|
streamFileData := wshrpc.CommandRemoteStreamFileData{Path: path}
|
|
rtnCh := wshclient.RemoteStreamFileCommand(client, streamFileData, &wshrpc.RpcOpts{Route: connRoute})
|
|
fullFile := &FullFile{}
|
|
firstPk := true
|
|
isDir := false
|
|
var fileBuf bytes.Buffer
|
|
var fileInfoArr []*wshrpc.FileInfo
|
|
for respUnion := range rtnCh {
|
|
if respUnion.Error != nil {
|
|
return nil, respUnion.Error
|
|
}
|
|
resp := respUnion.Response
|
|
if firstPk {
|
|
firstPk = false
|
|
// first packet has the fileinfo
|
|
if len(resp.FileInfo) != 1 {
|
|
return nil, fmt.Errorf("stream file protocol error, first pk fileinfo len=%d", len(resp.FileInfo))
|
|
}
|
|
fullFile.Info = resp.FileInfo[0]
|
|
if fullFile.Info.IsDir {
|
|
isDir = true
|
|
}
|
|
continue
|
|
}
|
|
if isDir {
|
|
if len(resp.FileInfo) == 0 {
|
|
continue
|
|
}
|
|
fileInfoArr = append(fileInfoArr, resp.FileInfo...)
|
|
} else {
|
|
if resp.Data64 == "" {
|
|
continue
|
|
}
|
|
decoder := base64.NewDecoder(base64.StdEncoding, bytes.NewReader([]byte(resp.Data64)))
|
|
_, err := io.Copy(&fileBuf, decoder)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("stream file, failed to decode base64 data %q: %w", resp.Data64, err)
|
|
}
|
|
}
|
|
}
|
|
if isDir {
|
|
fiBytes, err := json.Marshal(fileInfoArr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to serialize files %s", path)
|
|
}
|
|
fullFile.Data64 = base64.StdEncoding.EncodeToString(fiBytes)
|
|
} else {
|
|
// we can avoid this re-encoding if we ensure the remote side always encodes chunks of 3 bytes so we don't get padding chars
|
|
fullFile.Data64 = base64.StdEncoding.EncodeToString(fileBuf.Bytes())
|
|
}
|
|
return fullFile, nil
|
|
}
|
|
|
|
func (fs *FileService) GetWaveFile(id string, path string) (any, error) {
|
|
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
|
|
defer cancelFn()
|
|
file, err := filestore.WFS.Stat(ctx, id, path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error getting file: %w", err)
|
|
}
|
|
return file, nil
|
|
}
|
|
|
|
func (fs *FileService) DeleteFile_Meta() tsgenmeta.MethodMeta {
|
|
return tsgenmeta.MethodMeta{
|
|
Desc: "delete file",
|
|
ArgNames: []string{"connection", "path"},
|
|
}
|
|
}
|
|
|
|
func (fs *FileService) DeleteFile(connection string, path string) error {
|
|
if connection == "" {
|
|
connection = wshrpc.LocalConnName
|
|
}
|
|
connRoute := wshutil.MakeConnectionRouteId(connection)
|
|
client := wshserver.GetMainRpcClient()
|
|
return wshclient.RemoteFileDeleteCommand(client, path, &wshrpc.RpcOpts{Route: connRoute})
|
|
}
|
|
|
|
func (fs *FileService) GetFullConfig() wconfig.FullConfigType {
|
|
watcher := wconfig.GetWatcher()
|
|
return watcher.GetFullConfig()
|
|
}
|