github.com/nvkalinin/business-calendar@v1.0.2-0.20220515154925-e7df8a3d0c34/cmd/backup.go (about) 1 package cmd 2 3 import ( 4 "fmt" 5 "io" 6 "mime" 7 "net/http" 8 "os" 9 "time" 10 11 "github.com/nvkalinin/business-calendar/log" 12 ) 13 14 type Backup struct { 15 ServerUrl string `long:"server-url" short:"s" env:"SERVER_URL" default:"http://localhost" description:"URL сервера с REST API календаря."` 16 AdminPasswd string `long:"passwd" short:"p" env:"WEB_ADMIN_PASSWD" description:"Пароль пользователя admin."` 17 OutFile string `long:"out" short:"o" env:"OUT" description:"Путь к файлу, куда сохранить бекап. По умолчанию: cal_YYYY-MM-DD.bolt.gz. Значение '-' выводит в стандартный поток вывода."` 18 Timeout time.Duration `long:"timeout" short:"t" env:"TIMEOUT" default:"600s" description:"Макс. время выполнения запроса."` 19 } 20 21 func (b *Backup) Execute(args []string) error { 22 url := makeUrl(b.ServerUrl, "/api/admin/backup") 23 req, err := http.NewRequest(http.MethodGet, url, http.NoBody) 24 if err != nil { 25 log.Fatalf("[ERROR] cannot create request: %v", err) 26 } 27 req.SetBasicAuth("admin", b.AdminPasswd) 28 log.Printf("[DEBUG] backup request: URL=%s, %#v", url, req) 29 30 client := &http.Client{Timeout: b.Timeout} 31 resp, err := client.Do(req) 32 if err != nil { 33 log.Fatalf("[ERROR] cannot make request: %v", err) 34 } 35 defer func() { 36 if err := resp.Body.Close(); err != nil { 37 log.Printf("[WARN] cannot close resp body: %v", err) 38 } 39 }() 40 log.Printf("[DEBUG] backup response: %#v", resp) 41 42 if resp.StatusCode != 200 { 43 respBody, err := io.ReadAll(resp.Body) 44 if err != nil { 45 log.Fatalf("[ERROR] cannot read err response (status %d): %v", resp.StatusCode, err) 46 } 47 err = readJsonError(respBody) 48 log.Fatalf("[ERROR] backup error (status %d): %v", resp.StatusCode, err) 49 } 50 51 fname := b.filename(resp) 52 log.Printf("[DEBUG] saving backup to file '%s'", fname) 53 var f *os.File 54 if fname == "-" { 55 f = os.Stdout 56 } else { 57 f, err = os.Create(fname) 58 if err != nil { 59 log.Fatalf("[ERROR] cannot open %s: %v", fname, err) 60 } 61 defer func() { 62 if err := f.Close(); err != nil { 63 log.Printf("[WARN] cannot close %s: %v", fname, err) 64 } 65 }() 66 } 67 68 written, err := io.Copy(f, resp.Body) 69 if err != nil { 70 log.Fatalf("[ERROR] cannot save backup to %s: %v", fname, err) 71 } 72 log.Printf("[DEBUG] %d bytes written", written) 73 74 return nil 75 } 76 77 func (b *Backup) filename(resp *http.Response) string { 78 if len(b.OutFile) > 0 { 79 return b.OutFile 80 } 81 82 defName := fmt.Sprintf("cal_%s.bolt.gz", time.Now().Format("2006-01-02")) 83 84 vals, ok := resp.Header["Content-Disposition"] 85 if !ok || len(vals) == 0 { 86 return defName 87 } 88 89 _, params, err := mime.ParseMediaType(vals[0]) 90 if err != nil { 91 return defName 92 } 93 94 name, ok := params["filename"] 95 if !ok || len(name) == 0 { 96 return defName 97 } 98 99 return name 100 }