package cmd import ( "fmt" "io" "log" "net/http" "os" "path/filepath" "github.com/gorilla/mux" "github.com/spf13/cobra" ) var ( serveAddress string serveDirectory string ) var serveCmd = &cobra.Command{ Use: "serve", Short: "Run a backup server", Long: "Run an HTTP server to upload and download backups.", Run: func(cmd *cobra.Command, args []string) { if serveDirectory == "" { home, err := os.UserHomeDir() if err != nil { log.Fatalf("Failed to get user home directory: %v", err) } serveDirectory = filepath.Join(home, "backups") } if err := os.MkdirAll(serveDirectory, 0755); err != nil { log.Fatalf("Failed to create backup directory: %v", err) } r := mux.NewRouter() r.HandleFunc("/backup/{filename}", uploadHandler).Methods("POST") r.HandleFunc("/backup/{filename}", downloadHandler).Methods("GET") log.Printf("Starting server on %s", serveAddress) log.Printf("Using backup directory: %s", serveDirectory) if err := http.ListenAndServe(serveAddress, r); err != nil { log.Fatalf("Server failed: %v", err) } }, } func uploadHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) filename := vars["filename"] filePath := filepath.Join(serveDirectory, filename) file, err := os.Create(filePath) if err != nil { http.Error(w, "Failed to create file", http.StatusInternalServerError) log.Printf("Error creating file %s: %v", filename, err) return } defer file.Close() _, err = io.Copy(file, r.Body) if err != nil { http.Error(w, "Failed to write to file", http.StatusInternalServerError) log.Printf("Error writing to file %s: %v", filename, err) return } w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "File %s uploaded successfully.", filename) log.Printf("Uploaded %s", filename) } func downloadHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) filename := vars["filename"] filePath := filepath.Join(serveDirectory, filename) // Проверяем, существует ли файл, перед отправкой if _, err := os.Stat(filePath); os.IsNotExist(err) { http.NotFound(w, r) log.Printf("File not found: %s", filename) return } // http.ServeFile - это идиоматический способ отправки файлов в Go. // Он автоматически устанавливает Content-Type, Content-Length и другие заголовки. http.ServeFile(w, r, filePath) log.Printf("Downloaded %s", filename) } func init() { rootCmd.AddCommand(serveCmd) serveCmd.Flags().StringVarP(&serveAddress, "address", "a", "localhost:8080", "Address and port for the server") serveCmd.Flags().StringVarP(&serveDirectory, "directory", "d", "", "Directory to store backups (defaults to ~/backups)") }