- Introduced `SetLogLevel` utility function for unified log level management. - Updated dynamic log level handling across server and watcher components. - Extended auth files response by extracting and including the `type` field from file content. - Updated management API documentation with the new `type` field in auth files response.
150 lines
4.0 KiB
Go
150 lines
4.0 KiB
Go
package management
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/tidwall/gjson"
|
|
)
|
|
|
|
// List auth files
|
|
func (h *Handler) ListAuthFiles(c *gin.Context) {
|
|
entries, err := os.ReadDir(h.cfg.AuthDir)
|
|
if err != nil {
|
|
c.JSON(500, gin.H{"error": fmt.Sprintf("failed to read auth dir: %v", err)})
|
|
return
|
|
}
|
|
files := make([]gin.H, 0)
|
|
for _, e := range entries {
|
|
if e.IsDir() {
|
|
continue
|
|
}
|
|
name := e.Name()
|
|
if !strings.HasSuffix(strings.ToLower(name), ".json") {
|
|
continue
|
|
}
|
|
if info, errInfo := e.Info(); errInfo == nil {
|
|
fileData := gin.H{"name": name, "size": info.Size(), "modtime": info.ModTime()}
|
|
|
|
// Read file to get type field
|
|
full := filepath.Join(h.cfg.AuthDir, name)
|
|
if data, errRead := os.ReadFile(full); errRead == nil {
|
|
typeValue := gjson.GetBytes(data, "type").String()
|
|
fileData["type"] = typeValue
|
|
}
|
|
|
|
files = append(files, fileData)
|
|
}
|
|
}
|
|
c.JSON(200, gin.H{"files": files})
|
|
}
|
|
|
|
// Download single auth file by name
|
|
func (h *Handler) DownloadAuthFile(c *gin.Context) {
|
|
name := c.Query("name")
|
|
if name == "" || strings.Contains(name, string(os.PathSeparator)) {
|
|
c.JSON(400, gin.H{"error": "invalid name"})
|
|
return
|
|
}
|
|
if !strings.HasSuffix(strings.ToLower(name), ".json") {
|
|
c.JSON(400, gin.H{"error": "name must end with .json"})
|
|
return
|
|
}
|
|
full := filepath.Join(h.cfg.AuthDir, name)
|
|
data, err := os.ReadFile(full)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
c.JSON(404, gin.H{"error": "file not found"})
|
|
} else {
|
|
c.JSON(500, gin.H{"error": fmt.Sprintf("failed to read file: %v", err)})
|
|
}
|
|
return
|
|
}
|
|
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", name))
|
|
c.Data(200, "application/json", data)
|
|
}
|
|
|
|
// Upload auth file: multipart or raw JSON with ?name=
|
|
func (h *Handler) UploadAuthFile(c *gin.Context) {
|
|
if file, err := c.FormFile("file"); err == nil && file != nil {
|
|
name := filepath.Base(file.Filename)
|
|
if !strings.HasSuffix(strings.ToLower(name), ".json") {
|
|
c.JSON(400, gin.H{"error": "file must be .json"})
|
|
return
|
|
}
|
|
dst := filepath.Join(h.cfg.AuthDir, name)
|
|
if errSave := c.SaveUploadedFile(file, dst); errSave != nil {
|
|
c.JSON(500, gin.H{"error": fmt.Sprintf("failed to save file: %v", errSave)})
|
|
return
|
|
}
|
|
c.JSON(200, gin.H{"status": "ok"})
|
|
return
|
|
}
|
|
name := c.Query("name")
|
|
if name == "" || strings.Contains(name, string(os.PathSeparator)) {
|
|
c.JSON(400, gin.H{"error": "invalid name"})
|
|
return
|
|
}
|
|
if !strings.HasSuffix(strings.ToLower(name), ".json") {
|
|
c.JSON(400, gin.H{"error": "name must end with .json"})
|
|
return
|
|
}
|
|
data, err := io.ReadAll(c.Request.Body)
|
|
if err != nil {
|
|
c.JSON(400, gin.H{"error": "failed to read body"})
|
|
return
|
|
}
|
|
dst := filepath.Join(h.cfg.AuthDir, filepath.Base(name))
|
|
if errWrite := os.WriteFile(dst, data, 0o600); errWrite != nil {
|
|
c.JSON(500, gin.H{"error": fmt.Sprintf("failed to write file: %v", errWrite)})
|
|
return
|
|
}
|
|
c.JSON(200, gin.H{"status": "ok"})
|
|
}
|
|
|
|
// Delete auth files: single by name or all
|
|
func (h *Handler) DeleteAuthFile(c *gin.Context) {
|
|
if all := c.Query("all"); all == "true" || all == "1" || all == "*" {
|
|
entries, err := os.ReadDir(h.cfg.AuthDir)
|
|
if err != nil {
|
|
c.JSON(500, gin.H{"error": fmt.Sprintf("failed to read auth dir: %v", err)})
|
|
return
|
|
}
|
|
deleted := 0
|
|
for _, e := range entries {
|
|
if e.IsDir() {
|
|
continue
|
|
}
|
|
name := e.Name()
|
|
if !strings.HasSuffix(strings.ToLower(name), ".json") {
|
|
continue
|
|
}
|
|
full := filepath.Join(h.cfg.AuthDir, name)
|
|
if err = os.Remove(full); err == nil {
|
|
deleted++
|
|
}
|
|
}
|
|
c.JSON(200, gin.H{"status": "ok", "deleted": deleted})
|
|
return
|
|
}
|
|
name := c.Query("name")
|
|
if name == "" || strings.Contains(name, string(os.PathSeparator)) {
|
|
c.JSON(400, gin.H{"error": "invalid name"})
|
|
return
|
|
}
|
|
full := filepath.Join(h.cfg.AuthDir, filepath.Base(name))
|
|
if err := os.Remove(full); err != nil {
|
|
if os.IsNotExist(err) {
|
|
c.JSON(404, gin.H{"error": "file not found"})
|
|
} else {
|
|
c.JSON(500, gin.H{"error": fmt.Sprintf("failed to remove file: %v", err)})
|
|
}
|
|
return
|
|
}
|
|
c.JSON(200, gin.H{"status": "ok"})
|
|
}
|