2024-05-14 18:37:41 +02:00
|
|
|
package fileservice
|
|
|
|
|
|
|
|
import (
|
2024-08-19 20:02:40 +02:00
|
|
|
"bytes"
|
2024-06-03 22:03:21 +02:00
|
|
|
"context"
|
2024-05-14 21:29:41 +02:00
|
|
|
"encoding/base64"
|
2024-05-20 20:39:23 +02:00
|
|
|
"encoding/json"
|
2024-05-14 21:29:41 +02:00
|
|
|
"fmt"
|
2024-08-19 20:02:40 +02:00
|
|
|
"io"
|
2024-06-03 22:03:21 +02:00
|
|
|
"time"
|
2024-05-14 18:37:41 +02:00
|
|
|
|
2024-09-05 23:25:45 +02:00
|
|
|
"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"
|
2024-05-14 18:37:41 +02:00
|
|
|
)
|
|
|
|
|
2024-05-17 07:48:23 +02:00
|
|
|
const MaxFileSize = 10 * 1024 * 1024 // 10M
|
2024-06-03 22:03:21 +02:00
|
|
|
const DefaultTimeout = 2 * time.Second
|
2024-05-17 07:48:23 +02:00
|
|
|
|
2024-05-14 18:37:41 +02:00
|
|
|
type FileService struct{}
|
|
|
|
|
2024-05-16 09:29:58 +02:00
|
|
|
type FullFile struct {
|
2024-08-12 19:58:39 +02:00
|
|
|
Info *wshrpc.FileInfo `json:"info"`
|
|
|
|
Data64 string `json:"data64"` // base64 encoded
|
2024-05-16 09:29:58 +02:00
|
|
|
}
|
|
|
|
|
2024-08-19 20:02:40 +02:00
|
|
|
func (fs *FileService) SaveFile_Meta() tsgenmeta.MethodMeta {
|
|
|
|
return tsgenmeta.MethodMeta{
|
|
|
|
Desc: "save file",
|
|
|
|
ArgNames: []string{"connection", "path", "data64"},
|
2024-07-18 08:41:33 +02:00
|
|
|
}
|
2024-08-19 20:02:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (fs *FileService) SaveFile(connection string, path string, data64 string) error {
|
|
|
|
if connection == "" {
|
|
|
|
connection = wshrpc.LocalConnName
|
2024-07-18 08:41:33 +02:00
|
|
|
}
|
2024-08-19 20:02:40 +02:00
|
|
|
connRoute := wshutil.MakeConnectionRouteId(connection)
|
|
|
|
client := wshserver.GetMainRpcClient()
|
|
|
|
writeData := wshrpc.CommandRemoteWriteFileData{Path: path, Data64: data64}
|
|
|
|
return wshclient.RemoteWriteFileCommand(client, writeData, &wshrpc.RpcOpts{Route: connRoute})
|
2024-07-18 08:41:33 +02:00
|
|
|
}
|
|
|
|
|
2024-08-17 03:45:45 +02:00
|
|
|
func (fs *FileService) StatFile_Meta() tsgenmeta.MethodMeta {
|
|
|
|
return tsgenmeta.MethodMeta{
|
|
|
|
Desc: "get file info",
|
|
|
|
ArgNames: []string{"connection", "path"},
|
2024-05-16 09:29:58 +02:00
|
|
|
}
|
2024-08-17 03:45:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (fs *FileService) StatFile(connection string, path string) (*wshrpc.FileInfo, error) {
|
|
|
|
if connection == "" {
|
|
|
|
connection = wshrpc.LocalConnName
|
2024-05-16 09:29:58 +02:00
|
|
|
}
|
2024-08-17 03:45:45 +02:00
|
|
|
connRoute := wshutil.MakeConnectionRouteId(connection)
|
|
|
|
client := wshserver.GetMainRpcClient()
|
|
|
|
return wshclient.RemoteFileInfoCommand(client, path, &wshrpc.RpcOpts{Route: connRoute})
|
2024-05-16 09:29:58 +02:00
|
|
|
}
|
|
|
|
|
2024-12-03 07:23:44 +01:00
|
|
|
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})
|
|
|
|
}
|
|
|
|
|
2024-08-19 20:02:40 +02:00
|
|
|
func (fs *FileService) ReadFile_Meta() tsgenmeta.MethodMeta {
|
|
|
|
return tsgenmeta.MethodMeta{
|
|
|
|
Desc: "read file",
|
|
|
|
ArgNames: []string{"connection", "path"},
|
2024-05-16 09:29:58 +02:00
|
|
|
}
|
2024-08-19 20:02:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (fs *FileService) ReadFile(connection string, path string) (*FullFile, error) {
|
|
|
|
if connection == "" {
|
|
|
|
connection = wshrpc.LocalConnName
|
2024-05-17 07:48:23 +02:00
|
|
|
}
|
2024-08-19 20:02:40 +02:00
|
|
|
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
|
2024-06-22 09:41:49 +02:00
|
|
|
}
|
2024-08-19 20:02:40 +02:00
|
|
|
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
|
2024-06-27 21:30:08 +02:00
|
|
|
}
|
2024-08-19 20:02:40 +02:00
|
|
|
if isDir {
|
|
|
|
if len(resp.FileInfo) == 0 {
|
2024-07-18 00:24:43 +02:00
|
|
|
continue
|
|
|
|
}
|
2024-08-19 20:02:40 +02:00
|
|
|
fileInfoArr = append(fileInfoArr, resp.FileInfo...)
|
|
|
|
} else {
|
|
|
|
if resp.Data64 == "" {
|
|
|
|
continue
|
2024-06-22 09:41:49 +02:00
|
|
|
}
|
2024-08-19 20:02:40 +02:00
|
|
|
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)
|
2024-05-20 20:39:23 +02:00
|
|
|
}
|
|
|
|
}
|
2024-08-19 20:02:40 +02:00
|
|
|
}
|
|
|
|
if isDir {
|
|
|
|
fiBytes, err := json.Marshal(fileInfoArr)
|
2024-05-20 20:39:23 +02:00
|
|
|
if err != nil {
|
2024-08-19 20:02:40 +02:00
|
|
|
return nil, fmt.Errorf("unable to serialize files %s", path)
|
2024-05-20 20:39:23 +02:00
|
|
|
}
|
2024-08-19 20:02:40 +02:00
|
|
|
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())
|
2024-05-14 21:29:41 +02:00
|
|
|
}
|
2024-08-19 20:02:40 +02:00
|
|
|
return fullFile, nil
|
2024-05-14 18:37:41 +02:00
|
|
|
}
|
2024-06-03 22:03:21 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2024-06-20 08:59:41 +02:00
|
|
|
|
2024-08-19 20:02:40 +02:00
|
|
|
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})
|
2024-06-25 22:53:55 +02:00
|
|
|
}
|
|
|
|
|
2024-08-28 03:49:49 +02:00
|
|
|
func (fs *FileService) GetFullConfig() wconfig.FullConfigType {
|
2024-06-20 08:59:41 +02:00
|
|
|
watcher := wconfig.GetWatcher()
|
2024-08-28 03:49:49 +02:00
|
|
|
return watcher.GetFullConfig()
|
2024-06-20 08:59:41 +02:00
|
|
|
}
|