package main import ( "concord.hectabit.org/Hectabit/burgerbackup/lib/common" "errors" "fmt" "github.com/gin-gonic/gin" "github.com/spf13/viper" "golang.org/x/crypto/argon2" "io" "log" "mime/multipart" "net/http" "os" "strconv" "time" ) func main() { if _, err := os.Stat("config.ini"); err == nil { log.Println("[INFO] Config loaded at", time.Now().Unix()) } else if os.IsNotExist(err) { log.Fatalln("[FATAL] config.ini does not exist") } else { log.Fatalln("[FATAL] File is in quantum uncertainty:", err) } viper.SetConfigName("config") viper.AddConfigPath("./") viper.AutomaticEnv() err := viper.ReadInConfig() if err != nil { log.Fatalln("[FATAL] Error in config file at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) } backupKey := viper.GetString("BACKUP_KEY") cryptoKey := viper.GetString("CRYPTO_KEY") port := viper.GetInt("PORT") host := viper.GetString("HOST") backupFolder := viper.GetString("BACKUP_FOLDER") if host == "" { log.Fatalln("[FATAL] HOST is not set") } if port == 0 { log.Fatalln("[FATAL] PORT is not set") } if backupKey == "" { log.Fatalln("[FATAL] BACKUP_KEY is not set") } else if backupKey == "supersecretkey" { log.Println("[WARN] SECRET_KEY is set to a default value. Please set it to another value.") } if cryptoKey == "" { log.Fatalln("[FATAL] CRYPTO_KEY is not set") } else if cryptoKey == "supersecretkey" { log.Println("[WARN] SECRET_KEY is set to a default value. If it is also set to this on the client, please set both to another value.") } if _, err := os.Stat(backupFolder); os.IsNotExist(err) { log.Println("[INFO] Backup folder does not exist, creating") if err := os.Mkdir(backupFolder, 0755); err != nil { log.Fatalln("[FATAL] Could not create backup folder:", err) } } gin.SetMode(gin.ReleaseMode) router := gin.Default() router.Use(func(c *gin.Context) { c.Writer.Header().Set("Access-Control-Allow-Origin", "*") c.Writer.Header().Set("Access-Control-Allow-Headers", "*, Authorization") c.Writer.Header().Set("Access-Control-Allow-Methods", "*") if c.Request.Method == "OPTIONS" { c.AbortWithStatus(200) return } c.Next() }) router.POST("/api/backup", func(c *gin.Context) { clientBackupKey := c.GetHeader("Authorization") if clientBackupKey == "" { c.JSON(400, gin.H{"error": "BackupKey not found", "goErr": "backupKey not found"}) return } var authenticated bool authenticated, err = common.VerifyHash(clientBackupKey, backupKey) if err != nil { c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerbackup and refer to the documentation for more info. Your error code is: UNKNOWN-API-BACKUP-VERIFYERR", "goErr": err.Error()}) log.Println("[ERROR] Error in /api/backup hash verify:", err) return } if authenticated { file, err := c.FormFile("file") if err != nil { if errors.Is(err, http.ErrMissingFile) { c.JSON(400, gin.H{"error": "No file uploaded", "goErr": err.Error()}) } else { c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerbackup and refer to the documentation for more info. Your error code is: UNKNOWN-API-BACKUP-FILEERR", "goErr": err.Error()}) log.Println("[ERROR] Error in /api/backup file upload:", err) } return } fileName := "database_" + strconv.FormatInt(time.Now().Unix(), 10) + ".db" encryptedFile, err := file.Open() if err != nil { c.JSON(422, gin.H{"error": "Could not open file", "goErr": err.Error()}) } defer func(encryptedFile multipart.File) { err := encryptedFile.Close() if err != nil { c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerbackup and refer to the documentation for more info. Your error code is: UNKNOWN-API-BACKUP-FILEDEFERERR", "goErr": err.Error()}) log.Println("[ERROR] Error in /api/backup file defer:", err) return } }(encryptedFile) encryptedData, err := io.ReadAll(encryptedFile) if err != nil { c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerbackup and refer to the documentation for more info. Your error code is: UNKNOWN-API-BACKUP-FILECONTENTERR", "goErr": err.Error()}) log.Println("[ERROR] Error in /api/backup file read:", err) return } cryptoKeyHashed := argon2.IDKey([]byte(cryptoKey), []byte("burgerbackup"), 1, 64*1024, 4, 32) decryptedData, err := common.DecryptAES(cryptoKeyHashed, encryptedData) if err != nil { c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerbackup and refer to the documentation for more info. Your error code is: UNKNOWN-API-BACKUP-DECRYPTERR", "goErr": err.Error()}) log.Println("[ERROR] Error in /api/backup file decrypt:", err) return } filePath := backupFolder + "/" + fileName if err := os.WriteFile(filePath, decryptedData, 0644); err != nil { c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerbackup and refer to the documentation for more info. Your error code is: UNKNOWN-API-BACKUP-WRITEERR", "goErr": err.Error()}) log.Println("[ERROR] Error in /api/backup file write:", err) return } fmt.Println("[INFO] Received and decrypted backup from", c.ClientIP()) c.JSON(200, gin.H{"success": "true", "timeTaken": time.Now().Unix(), "goErr": ""}) } else { c.JSON(401, gin.H{"error": "Incorrect backup key", "goErr": "incorrect backup key"}) } }) log.Println("[INFO] Server started at", time.Now().Unix()) log.Println("[INFO] Welcome to Burgerbackup! Today we are running on IP " + host + " on port " + strconv.Itoa(port) + ".") err = router.Run(host + ":" + strconv.Itoa(port)) if err != nil { log.Fatalln("[FATAL] Server failed to begin operations at", time.Now().Unix(), err) } }