mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-21 13:23:07 +01:00
101 lines
2.7 KiB
Go
101 lines
2.7 KiB
Go
package web
|
|
|
|
import (
|
|
"archive/zip"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/amir20/dozzle/internal/auth"
|
|
"github.com/amir20/dozzle/internal/container"
|
|
"github.com/docker/docker/pkg/stdcopy"
|
|
"github.com/go-chi/chi/v5"
|
|
)
|
|
|
|
func (h *handler) downloadLogs(w http.ResponseWriter, r *http.Request) {
|
|
hostIds := strings.Split(chi.URLParam(r, "hostIds"), ",")
|
|
if len(hostIds) == 0 {
|
|
http.Error(w, "no container ids provided", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
usersFilter := h.config.Filter
|
|
if h.config.Authorization.Provider != NONE {
|
|
user := auth.UserFromContext(r.Context())
|
|
if user.ContainerFilter.Exists() {
|
|
usersFilter = user.ContainerFilter
|
|
}
|
|
}
|
|
|
|
now := time.Now()
|
|
nowFmt := now.Format("2006-01-02T15-04-05")
|
|
|
|
var stdTypes container.StdType
|
|
if r.URL.Query().Has("stdout") {
|
|
stdTypes |= container.STDOUT
|
|
}
|
|
if r.URL.Query().Has("stderr") {
|
|
stdTypes |= container.STDERR
|
|
}
|
|
|
|
if stdTypes == 0 {
|
|
http.Error(w, "stdout or stderr is required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Set headers for zip file
|
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=container-logs-%s.zip", nowFmt))
|
|
w.Header().Set("Content-Type", "application/zip")
|
|
|
|
// Create zip writer
|
|
zw := zip.NewWriter(w)
|
|
defer zw.Close()
|
|
|
|
// Process each container
|
|
for _, hostId := range hostIds {
|
|
parts := strings.Split(hostId, ":")
|
|
if len(parts) != 2 {
|
|
http.Error(w, fmt.Sprintf("invalid host id: %s", hostId), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
host := parts[0]
|
|
id := parts[1]
|
|
containerService, err := h.multiHostService.FindContainer(host, id, usersFilter)
|
|
if err != nil {
|
|
http.Error(w, fmt.Sprintf("error finding container %s: %v", id, err), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Create new file in zip for this container's logs
|
|
fileName := fmt.Sprintf("%s-%s.log", containerService.Container.Name, nowFmt)
|
|
f, err := zw.Create(fileName)
|
|
if err != nil {
|
|
http.Error(w, fmt.Sprintf("error creating zip entry: %v", err), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// Get container logs
|
|
reader, err := containerService.RawLogs(r.Context(), time.Time{}, now, stdTypes)
|
|
if err != nil {
|
|
http.Error(w, fmt.Sprintf("error getting logs for container %s: %v", id, err), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// Copy logs directly to zip entry
|
|
if containerService.Container.Tty {
|
|
if _, err := io.Copy(f, reader); err != nil {
|
|
http.Error(w, fmt.Sprintf("error copying logs for container %s: %v", id, err), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
} else {
|
|
if _, err := stdcopy.StdCopy(f, f, reader); err != nil {
|
|
http.Error(w, fmt.Sprintf("error copying logs for container %s: %v", id, err), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|