Add support for reading a file's content or downloading it

This commit is contained in:
Dane Everitt 2019-04-06 17:32:35 -07:00
parent 314a5ad546
commit aef9521190
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
2 changed files with 63 additions and 0 deletions

44
http.go
View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"bufio"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
@ -189,6 +190,48 @@ func (rt *Router) routeServerLogs(w http.ResponseWriter, r *http.Request, ps htt
json.NewEncoder(w).Encode(struct{Data []string `json:"data"`}{Data: out }) json.NewEncoder(w).Encode(struct{Data []string `json:"data"`}{Data: out })
} }
// Handle a request to get the contents of a file on the server.
func (rt *Router) routeServerFileRead(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
s := rt.Servers.Get(ps.ByName("server"))
cleaned, err := s.Filesystem().SafePath(ps.ByName("path"))
if err != nil {
http.Error(w, "could not determine path", http.StatusInternalServerError)
return
}
st, err := os.Stat(cleaned)
if err != nil {
if !os.IsNotExist(err) {
zap.S().Errorw("failed to stat file for reading", zap.String("path", ps.ByName("path")), zap.String("server", s.Uuid), zap.Error(err))
}
http.Error(w, "failed to stat file", http.StatusInternalServerError)
return
}
f, err := os.OpenFile(cleaned, os.O_RDONLY, 0)
if err != nil {
if !os.IsNotExist(err) {
zap.S().Errorw("failed to open file for reading", zap.String("path", ps.ByName("path")), zap.String("server", s.Uuid), zap.Error(err))
}
http.Error(w, "failed to open file", http.StatusInternalServerError)
return
}
defer f.Close()
// If a download parameter is included in the URL go ahead and attach the necessary headers
// so that the file can be downloaded.
if r.URL.Query().Get("download") != "" {
w.Header().Set("Content-Disposition", "attachment; filename=" + st.Name())
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Length", strconv.Itoa(int(st.Size())))
}
bufio.NewReader(f).WriteTo(w)
}
// Configures the router and all of the associated routes. // Configures the router and all of the associated routes.
func (rt *Router) ConfigureRouter() *httprouter.Router { func (rt *Router) ConfigureRouter() *httprouter.Router {
router := httprouter.New() router := httprouter.New()
@ -197,6 +240,7 @@ func (rt *Router) ConfigureRouter() *httprouter.Router {
router.GET("/api/servers", rt.AuthenticateToken("i:servers", rt.routeAllServers)) router.GET("/api/servers", rt.AuthenticateToken("i:servers", rt.routeAllServers))
router.GET("/api/servers/:server", rt.AuthenticateToken("s:view", rt.AuthenticateServer(rt.routeServer))) router.GET("/api/servers/:server", rt.AuthenticateToken("s:view", rt.AuthenticateServer(rt.routeServer)))
router.GET("/api/servers/:server/logs", rt.AuthenticateToken("s:logs", rt.AuthenticateServer(rt.routeServerLogs))) router.GET("/api/servers/:server/logs", rt.AuthenticateToken("s:logs", rt.AuthenticateServer(rt.routeServerLogs)))
router.GET("/api/servers/:server/files/*path", rt.AuthenticateToken("s:files", rt.AuthenticateServer(rt.routeServerFileRead)))
router.POST("/api/servers/:server/power", rt.AuthenticateToken("s:power", rt.AuthenticateServer(rt.routeServerPower))) router.POST("/api/servers/:server/power", rt.AuthenticateToken("s:power", rt.AuthenticateServer(rt.routeServerPower)))

View File

@ -1,8 +1,10 @@
package server package server
import ( import (
"bytes"
"errors" "errors"
"go.uber.org/zap" "go.uber.org/zap"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@ -167,4 +169,21 @@ func (fs *Filesystem) DirectorySize(dir string) (int64, error) {
wg.Wait() wg.Wait()
return size, nil return size, nil
}
// Reads a file on the system and returns it as a byte representation in a file
// reader. This is not the most memory efficient usage since it will be reading the
// entirety of the file into memory.
func (fs *Filesystem) Readfile(p string) (io.Reader, error) {
cleaned, err := fs.SafePath(p)
if err != nil {
return nil, err
}
b, err := ioutil.ReadFile(cleaned)
if err != nil {
return nil, err
}
return bytes.NewReader(b), nil
} }