List Config dir if directory is specified (#468)

* implemented list dir for config

* added file stat and path check

* addressed comments, added file not found error
This commit is contained in:
Cole Lashley 2024-03-18 17:11:13 -07:00 committed by GitHub
parent b656f4ce0f
commit 0a14a58663
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 123 additions and 1 deletions

View File

@ -797,12 +797,16 @@ declare global {
};
type FileInfoType = {
type: string;
name: string;
size: number;
modts: number;
isdir: boolean;
perm: number;
notfound: boolean;
modestr?: string;
path?: string;
outputpos?: number;
};
type ExtBlob = Blob & {

View File

@ -11,9 +11,11 @@ import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"reflect"
"sync"
"time"
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
"github.com/wavetermdev/waveterm/waveshell/pkg/wlog"
@ -58,6 +60,7 @@ const (
WriteFileReadyPacketStr = "writefileready" // rpc-response
WriteFileDonePacketStr = "writefiledone" // rpc-response
FileDataPacketStr = "filedata"
FileStatPacketStr = "filestat"
LogPacketStr = "log" // logging packet (sent from waveshell back to server)
ShellStatePacketStr = "shellstate"
@ -112,6 +115,7 @@ func init() {
TypeStrToFactory[WriteFileDonePacketStr] = reflect.TypeOf(WriteFileDonePacketType{})
TypeStrToFactory[LogPacketStr] = reflect.TypeOf(LogPacketType{})
TypeStrToFactory[ShellStatePacketStr] = reflect.TypeOf(ShellStatePacketType{})
TypeStrToFactory[FileStatPacketStr] = reflect.TypeOf(FileStatPacketType{})
var _ RpcPacketType = (*RunPacketType)(nil)
var _ RpcPacketType = (*GetCmdPacketType)(nil)
@ -379,6 +383,51 @@ func MakeReInitPacket() *ReInitPacketType {
return &ReInitPacketType{Type: ReInitPacketStr}
}
type FileStatPacketType struct {
Type string `json:"type"`
Name string `json:"name"`
Size int64 `json:"size"`
ModTs time.Time `json:"modts"`
IsDir bool `json:"isdir"`
Perm int `json:"perm"`
ModeStr string `json:"modestr"`
Error string `json:"error"`
Done bool `json:"done"`
RespId string `json:"respid"`
Path string `json:"path"`
}
func (*FileStatPacketType) GetType() string {
return FileStatPacketStr
}
func (p *FileStatPacketType) GetResponseDone() bool {
return p.Done
}
func (p *FileStatPacketType) GetResponseId() string {
return p.RespId
}
func MakeFileStatPacketType() *FileStatPacketType {
return &FileStatPacketType{Type: FileStatPacketStr}
}
func MakeFileStatPacketFromFileInfo(finfo fs.FileInfo, err string, done bool) *FileStatPacketType {
resp := MakeFileStatPacketType()
resp.Error = err
resp.Done = done
resp.IsDir = finfo.IsDir()
resp.Name = finfo.Name()
resp.Size = finfo.Size()
resp.ModTs = finfo.ModTime()
resp.Perm = int(finfo.Mode().Perm())
resp.ModeStr = finfo.Mode().String()
return resp
}
type StreamFilePacketType struct {
Type string `json:"type"`
ReqId string `json:"reqid"`

View File

@ -677,6 +677,40 @@ func HandleRunCommand(w http.ResponseWriter, r *http.Request) {
WriteJsonSuccess(w, update)
}
func CheckIsDir(dirHandler http.Handler, fileHandler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
configPath := r.URL.Path
configAbsPath, err := filepath.Abs(configPath)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error getting absolute path", err)))
return
}
configBaseDir := path.Join(scbase.GetWaveHomeDir(), "config")
configFullPath := path.Join(scbase.GetWaveHomeDir(), configAbsPath)
if !strings.HasPrefix(configFullPath, configBaseDir) {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error: path is not in config folder")))
return
}
fstat, err := os.Stat(configFullPath)
if errors.Is(err, fs.ErrNotExist) {
w.WriteHeader(404)
w.Write([]byte(fmt.Sprintf("file not found: ", configAbsPath)))
return
} else if err != nil {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("file stat err", err)))
return
}
if fstat.IsDir() {
AuthKeyMiddleWare(dirHandler).ServeHTTP(w, r)
} else {
AuthKeyMiddleWare(fileHandler).ServeHTTP(w, r)
}
})
}
func AuthKeyMiddleWare(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reqAuthKey := r.Header.Get("X-AuthKey")
@ -857,6 +891,39 @@ func doShutdown(reason string) {
})
}
func configDirHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("running?")
configPath := r.URL.Path
configFullPath := path.Join(scbase.GetWaveHomeDir(), configPath)
dirFile, err := os.Open(configFullPath)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error opening specified dir: ", err)))
return
}
entries, err := dirFile.Readdir(0)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error getting files: ", err)))
return
}
var files []*packet.FileStatPacketType
for index := 0; index < len(entries); index++ {
curEntry := entries[index]
curFile := packet.MakeFileStatPacketFromFileInfo(curEntry, "", false)
files = append(files, curFile)
}
dirListJson, err := json.Marshal(files)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("json err: ", err)))
return
}
w.WriteHeader(200)
w.Header().Set("Content-Type", "application/json")
w.Write(dirListJson)
}
func main() {
scbase.BuildTime = BuildTime
scbase.WaveVersion = WaveVersion
@ -953,7 +1020,9 @@ func main() {
gr.HandleFunc("/api/write-file", AuthKeyWrap(HandleWriteFile)).Methods("POST")
configPath := path.Join(scbase.GetWaveHomeDir(), "config") + "/"
log.Printf("[wave] config path: %q\n", configPath)
gr.PathPrefix("/config/").Handler(AuthKeyMiddleWare(http.StripPrefix("/config/", http.FileServer(http.Dir(configPath)))))
isFileHandler := http.StripPrefix("/config/", http.FileServer(http.Dir(configPath)))
isDirHandler := http.HandlerFunc(configDirHandler)
gr.PathPrefix("/config/").Handler(CheckIsDir(isDirHandler, isFileHandler))
serverAddr := MainServerAddr
if scbase.IsDevMode() {