1 package main 2 3 import ( 4 "io" 5 "net/http" 6 "os" 7 "path" 8 "regexp" 9 "sort" 10 "time" 11 ) 12 13 const ( 14 backupDir = "backup" 15 maxBackups = 5 16 ) 17 18 func backupDatabase(fname string) error { 19 if _, err := os.Stat(fname); err != nil { 20 if os.IsNotExist(err) { 21 return nil 22 } 23 return err 24 } 25 nfn := "backup_" + time.Now().Format("2006-01-02") + ".db" 26 if _, err := os.Stat(backupDir); err != nil { 27 if os.IsNotExist(err) { 28 if err := os.Mkdir(backupDir, 0o755); err != nil { 29 return err 30 } 31 } else { 32 return err 33 } 34 } else if _, err = os.Stat(path.Join(backupDir, nfn)); err == nil || !os.IsNotExist(err) { 35 return err 36 } 37 38 nf, err := os.Create(path.Join(backupDir, nfn)) 39 if err != nil { 40 return err 41 } 42 defer nf.Close() 43 of, err := os.Open(fname) 44 if err != nil { 45 return err 46 } 47 defer of.Close() 48 _, err = io.Copy(nf, of) 49 if err != nil { 50 os.Remove(path.Join(backupDir, nfn)) 51 return err 52 } 53 54 files, err := readBackupDir(nfn) 55 if err != nil { 56 return err 57 } 58 for len(files) >= maxBackups { 59 os.Remove(path.Join(backupDir, files[0])) 60 files = files[1:] 61 } 62 63 return nil 64 } 65 66 func readBackupDir(except string) ([]string, error) { 67 d, _ := os.Open(backupDir) 68 fis, err := d.Readdir(-1) 69 d.Close() 70 if err != nil { 71 return nil, err 72 } 73 files := make([]string, 0, maxBackups) 74 regex := regexp.MustCompile(`backup_[0-9]{4}-[0-9]{2}-[0-9]{2}\.db`) 75 for _, file := range fis { 76 filename := file.Name() 77 if filename == except { 78 continue 79 } 80 if regex.MatchString(filename) { 81 files = append(files, filename) 82 } 83 } 84 sort.Strings(files) 85 return files, nil 86 } 87 88 func getDatabase(w http.ResponseWriter, r *http.Request) { 89 files, err := readBackupDir("") 90 if err != nil { 91 w.WriteHeader(http.StatusInternalServerError) 92 io.WriteString(w, err.Error()) 93 return 94 } 95 if len(files) == 0 { 96 w.WriteHeader(http.StatusNotFound) 97 return 98 } 99 http.ServeFile(w, r, path.Join(backupDir, files[len(files)-1])) 100 } 101