mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 08:49:13 +00:00
feat(logs): make log files downloadable (#706)
* feat(logs): show and download log files * feat(logs): make logs settings dropdown * feat(logs): minor cosmetic changes * fix(logs): send empty response when lohpath not configured * fix(logs): remove unused imports * feat(logs): check if logs dir exists * feat(logs): list log files in settings
This commit is contained in:
parent
21724f29f6
commit
b21c01a7df
7 changed files with 394 additions and 79 deletions
141
internal/http/logs.go
Normal file
141
internal/http/logs.go
Normal file
|
@ -0,0 +1,141 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/config"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
type logsHandler struct {
|
||||
cfg *config.AppConfig
|
||||
}
|
||||
|
||||
func newLogsHandler(cfg *config.AppConfig) *logsHandler {
|
||||
return &logsHandler{cfg: cfg}
|
||||
}
|
||||
|
||||
func (h logsHandler) Routes(r chi.Router) {
|
||||
r.Get("/files", h.files)
|
||||
r.Get("/files/{logFile}", h.downloadFile)
|
||||
}
|
||||
|
||||
func (h logsHandler) files(w http.ResponseWriter, r *http.Request) {
|
||||
response := LogfilesResponse{
|
||||
Files: []logFile{},
|
||||
Count: 0,
|
||||
}
|
||||
|
||||
if h.cfg.Config.LogPath == "" {
|
||||
render.JSON(w, r, response)
|
||||
return
|
||||
}
|
||||
|
||||
logsDir := path.Dir(h.cfg.Config.LogPath)
|
||||
|
||||
// check if dir exists before walkDir
|
||||
if _, err := os.Stat(logsDir); os.IsNotExist(err) {
|
||||
render.JSON(w, r, response)
|
||||
return
|
||||
}
|
||||
|
||||
var walk = func(path string, d fs.DirEntry, err error) error {
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if filepath.Ext(path) == ".log" {
|
||||
i, err := d.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response.Files = append(response.Files, logFile{
|
||||
Name: d.Name(),
|
||||
SizeBytes: i.Size(),
|
||||
Size: humanize.Bytes(uint64(i.Size())),
|
||||
UpdatedAt: i.ModTime(),
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := filepath.WalkDir(logsDir, walk); err != nil {
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
render.JSON(w, r, errorResponse{
|
||||
Message: err.Error(),
|
||||
Status: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
response.Count = len(response.Files)
|
||||
|
||||
render.JSON(w, r, response)
|
||||
}
|
||||
|
||||
func (h logsHandler) downloadFile(w http.ResponseWriter, r *http.Request) {
|
||||
if h.cfg.Config.LogPath == "" {
|
||||
render.Status(r, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
logsDir := path.Dir(h.cfg.Config.LogPath)
|
||||
|
||||
// check if dir exists before walkDir
|
||||
if _, err := os.Stat(logsDir); os.IsNotExist(err) {
|
||||
render.Status(r, http.StatusNotFound)
|
||||
render.JSON(w, r, errorResponse{
|
||||
Message: "log directory not found or inaccessible",
|
||||
Status: http.StatusNotFound,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
logFile := chi.URLParam(r, "logFile")
|
||||
if logFile == "" {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
render.JSON(w, r, errorResponse{
|
||||
Message: "empty log file",
|
||||
Status: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
} else if !strings.Contains(logFile, ".log") {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
render.JSON(w, r, errorResponse{
|
||||
Message: "invalid file",
|
||||
Status: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Disposition", "attachment; filename="+strconv.Quote(logFile))
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
|
||||
filePath := filepath.Join(logsDir, logFile)
|
||||
|
||||
http.ServeFile(w, r, filePath)
|
||||
}
|
||||
|
||||
type logFile struct {
|
||||
Name string `json:"filename"`
|
||||
SizeBytes int64 `json:"size_bytes"`
|
||||
Size string `json:"size"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type LogfilesResponse struct {
|
||||
Files []logFile `json:"files"`
|
||||
Count int `json:"count"`
|
||||
}
|
|
@ -133,6 +133,7 @@ func (s Server) Handler() http.Handler {
|
|||
r.Route("/irc", newIrcHandler(encoder, s.ircService).Routes)
|
||||
r.Route("/indexer", newIndexerHandler(encoder, s.indexerService, s.ircService).Routes)
|
||||
r.Route("/keys", newAPIKeyHandler(encoder, s.apiService).Routes)
|
||||
r.Route("/logs", newLogsHandler(s.config).Routes)
|
||||
r.Route("/notification", newNotificationHandler(encoder, s.notificationService).Routes)
|
||||
r.Route("/release", newReleaseHandler(encoder, s.releaseService).Routes)
|
||||
r.Route("/updates", newUpdateHandler(encoder, s.updateService).Routes)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue