|
4 | 4 | "encoding/json" |
5 | 5 | "fmt" |
6 | 6 | "os" |
| 7 | + "strconv" |
| 8 | + "strings" |
| 9 | + "time" |
7 | 10 |
|
8 | 11 | "github.com/spf13/cobra" |
9 | 12 | "github.com/portainer/portainerctl/internal/client" |
@@ -88,13 +91,8 @@ func backupCmd() *cobra.Command { |
88 | 91 | RunE: func(cmd *cobra.Command, args []string) error { |
89 | 92 | c, err := client.MustClient(); if err != nil { return err } |
90 | 93 | body := map[string]interface{}{"Password": backupPass} |
91 | | - data, err := c.RawGet("/backup") |
92 | | - if err != nil { |
93 | | - // POST for backup |
94 | | - _ = c.Post("/backup", body, nil) |
95 | | - output.Success("Backup initiated.") |
96 | | - return nil |
97 | | - } |
| 94 | + data, err := c.RawPost("/backup", body) |
| 95 | + if err != nil { return err } |
98 | 96 | outFile := backupOutput |
99 | 97 | if outFile == "" { outFile = "portainer-backup.tar.gz" } |
100 | 98 | if err := os.WriteFile(outFile, data, 0600); err != nil { return err } |
@@ -297,27 +295,41 @@ func userActivityCmd() *cobra.Command { |
297 | 295 | }, |
298 | 296 | } |
299 | 297 |
|
| 298 | + var authCsvRange, authCsvOutput string |
300 | 299 | authLogsCsvCmd := &cobra.Command{ |
301 | | - Use: "auth-logs-csv", Short: "Download authentication logs as CSV", |
| 300 | + Use: "auth-logs-csv", |
| 301 | + Short: "Download authentication logs as CSV", |
302 | 302 | RunE: func(cmd *cobra.Command, args []string) error { |
| 303 | + after, err := parseDayRange(authCsvRange) |
| 304 | + if err != nil { return err } |
303 | 305 | c, err := client.MustClient(); if err != nil { return err } |
304 | | - data, err := c.RawGet("/useractivity/authlogs.csv") |
| 306 | + path := "/useractivity/authlogs.csv" |
| 307 | + if after > 0 { path += fmt.Sprintf("?after=%d", after) } |
| 308 | + data, err := c.RawGet(path) |
305 | 309 | if err != nil { return err } |
306 | | - fmt.Print(string(data)) |
307 | | - return nil |
| 310 | + return writeCsvOutput(data, authCsvOutput) |
308 | 311 | }, |
309 | 312 | } |
| 313 | + authLogsCsvCmd.Flags().StringVar(&authCsvRange, "range", "", "Time range: 1d–7d (e.g. 2d = last 2 days)") |
| 314 | + authLogsCsvCmd.Flags().StringVar(&authCsvOutput, "output-file", "", "Save CSV to file instead of printing") |
310 | 315 |
|
| 316 | + var logsCsvRange, logsCsvOutput string |
311 | 317 | logsCsvCmd := &cobra.Command{ |
312 | | - Use: "logs-csv", Short: "Download user activity logs as CSV", |
| 318 | + Use: "logs-csv", |
| 319 | + Short: "Download user activity logs as CSV", |
313 | 320 | RunE: func(cmd *cobra.Command, args []string) error { |
| 321 | + after, err := parseDayRange(logsCsvRange) |
| 322 | + if err != nil { return err } |
314 | 323 | c, err := client.MustClient(); if err != nil { return err } |
315 | | - data, err := c.RawGet("/useractivity/logs.csv") |
| 324 | + path := "/useractivity/logs.csv" |
| 325 | + if after > 0 { path += fmt.Sprintf("?after=%d", after) } |
| 326 | + data, err := c.RawGet(path) |
316 | 327 | if err != nil { return err } |
317 | | - fmt.Print(string(data)) |
318 | | - return nil |
| 328 | + return writeCsvOutput(data, logsCsvOutput) |
319 | 329 | }, |
320 | 330 | } |
| 331 | + logsCsvCmd.Flags().StringVar(&logsCsvRange, "range", "", "Time range: 1d–7d (e.g. 2d = last 2 days)") |
| 332 | + logsCsvCmd.Flags().StringVar(&logsCsvOutput, "output-file", "", "Save CSV to file instead of printing") |
321 | 333 |
|
322 | 334 | cmd.AddCommand(authLogsCmd, logsCmd, authLogsCsvCmd, logsCsvCmd) |
323 | 335 | return cmd |
@@ -717,3 +729,29 @@ func supportCmd() *cobra.Command { |
717 | 729 | cmd.AddCommand(downloadCmd, debugLogCmd) |
718 | 730 | return cmd |
719 | 731 | } |
| 732 | + |
| 733 | +func parseDayRange(r string) (int64, error) { |
| 734 | + if r == "" { |
| 735 | + return 0, nil |
| 736 | + } |
| 737 | + if !strings.HasSuffix(r, "d") { |
| 738 | + return 0, fmt.Errorf("invalid range %q: use format like 1d, 2d, up to 7d", r) |
| 739 | + } |
| 740 | + n, err := strconv.Atoi(strings.TrimSuffix(r, "d")) |
| 741 | + if err != nil || n < 1 || n > 7 { |
| 742 | + return 0, fmt.Errorf("invalid range %q: must be between 1d and 7d", r) |
| 743 | + } |
| 744 | + return time.Now().Add(-time.Duration(n) * 24 * time.Hour).Unix(), nil |
| 745 | +} |
| 746 | + |
| 747 | +func writeCsvOutput(data []byte, outputFile string) error { |
| 748 | + if outputFile != "" { |
| 749 | + if err := os.WriteFile(outputFile, data, 0600); err != nil { |
| 750 | + return err |
| 751 | + } |
| 752 | + output.Success("Saved to " + outputFile) |
| 753 | + return nil |
| 754 | + } |
| 755 | + fmt.Print(string(data)) |
| 756 | + return nil |
| 757 | +} |
0 commit comments