package management import ( "fmt" "io" "os" "path/filepath" "strings" "github.com/gin-gonic/gin" ) // 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 { files = append(files, gin.H{"name": name, "size": info.Size(), "modtime": info.ModTime()}) } } 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"}) }