package service import ( "encoding/json" "fmt" "io/fs" "net/http" "os" "path/filepath" "strings" "time" "git.towk2.me/towk/configurator/pkg/util" "github.com/rs/zerolog/log" ) func (s *Service) Download() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var ( path = s.PathForData() + strings.TrimPrefix(r.URL.Path, "/download") fileInfo os.FileInfo out *os.File contents []byte err error ) log.Debug(). Str("path", path). Str("client_host", r.Host). Msg("Service.Download()") // determine if path is directory, file, or exists if fileInfo, err = os.Stat(path); err == nil { if fileInfo.IsDir() { // create an archive of the directory and download log.Debug(). Str("type", "directory"). Msg("Service.Download()") archivePath := fmt.Sprintf("%d.tar.gz", time.Now().Unix()) out, err = os.Create(archivePath) if err != nil { s.writeErrorResponse(w, fmt.Sprintf("failed to create named file: %v", err), http.StatusInternalServerError) return } filesToArchive := []string{} filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error { if !d.IsDir() { filesToArchive = append(filesToArchive, path) } return nil }) log.Debug().Strs("files", filesToArchive).Send() err = util.CreateArchive(filesToArchive, out) if err != nil { s.writeErrorResponse(w, fmt.Sprintf("failed to create archive: %v", err.Error()), http.StatusInternalServerError) return } contents, err = os.ReadFile(archivePath) if err != nil { s.writeErrorResponse(w, fmt.Sprintf("failed to read archive: %v", err.Error()), http.StatusInternalServerError) return } w.Write(contents) err = os.Remove(archivePath) if err != nil { log.Error().Err(err).Msg("failed to remove temporary archive") return } } else { // download individual file log.Debug(). Str("type", "file"). Msg("Service.Download()") contents, err = os.ReadFile(path) if err != nil { s.writeErrorResponse(w, fmt.Sprintf("failed to read file to download: %v", err.Error()), http.StatusInternalServerError) return } w.Write(contents) } } else { s.writeErrorResponse(w, err.Error(), http.StatusBadRequest) return } } } func (s *Service) Upload() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { } } func (s *Service) List() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var ( path = s.PathForData() + strings.TrimPrefix(r.URL.Path, "/list") entries []string body []byte err error ) // show what we're listing log.Debug().Str("path", path).Msg("Service.List()") // walk directory and show all entries "ls" err = filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } entries = append(entries, d.Name()) return nil }) if err != nil { switch err { case fs.ErrNotExist, fs.ErrInvalid: http.Error(w, "No such file or directory...", http.StatusBadRequest) case fs.ErrPermission: http.Error(w, "Invalid permissions...", http.StatusForbidden) default: http.Error(w, "Something went wrong (file or directory *probably* does not exist)...", http.StatusInternalServerError) } return } body, err = json.Marshal(entries) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Write(body) } } func (s *Service) GetStatus(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") err := json.NewEncoder(w).Encode(map[string]any{ "code": http.StatusOK, "message": "Configurator is healthy", }) if err != nil { fmt.Printf("failed to encode JSON response body: %v\n", err) return } }