From 13ea6194d32a98c0c55a6058b2409fd089145872 Mon Sep 17 00:00:00 2001 From: Jonas Kaninda Date: Tue, 10 Sep 2024 22:59:28 +0200 Subject: [PATCH] feat: Add Telegram notification --- cmd/backup.go | 6 ++++ cmd/migrate.go | 6 ++++ cmd/restore.go | 6 ++++ cmd/root.go | 10 +++--- cmd/version.go | 10 +++--- docker/Dockerfile | 2 +- docs/reference/index.md | 2 ++ main.go | 13 ++++--- pkg/backup.go | 17 +++++++--- pkg/config.go | 10 ++++++ pkg/encrypt.go | 6 ++++ pkg/helper.go | 8 ++++- pkg/migrate.go | 7 ++++ pkg/restore.go | 8 ++++- pkg/scp.go | 6 ++++ pkg/scripts.go | 10 +++--- pkg/var.go | 6 ++++ utils/constant.go | 6 ++++ utils/logger.go | 10 ++++++ utils/notification.go | 75 +++++++++++++++++++++++++++++++++++++++++ utils/s3.go | 6 ++++ utils/utils.go | 31 +++++++++++++---- 22 files changed, 229 insertions(+), 32 deletions(-) create mode 100644 utils/notification.go diff --git a/cmd/backup.go b/cmd/backup.go index fc5f76b..575ec33 100644 --- a/cmd/backup.go +++ b/cmd/backup.go @@ -1,3 +1,9 @@ +// Package cmd / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package cmd import ( diff --git a/cmd/migrate.go b/cmd/migrate.go index 70af8b6..12320a5 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -1,3 +1,9 @@ +// Package cmd / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package cmd import ( diff --git a/cmd/restore.go b/cmd/restore.go index b81da31..5ea2193 100644 --- a/cmd/restore.go +++ b/cmd/restore.go @@ -1,3 +1,9 @@ +// Package cmd / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package cmd import ( diff --git a/cmd/root.go b/cmd/root.go index facf793..cb93a0d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,7 +1,9 @@ -// Package cmd /* -/* -Copyright © 2024 Jonas Kaninda -*/ +// Package cmd / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package cmd import ( diff --git a/cmd/version.go b/cmd/version.go index c1f19c1..ff9c06d 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -1,9 +1,11 @@ +// Package cmd / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package cmd -/* -Copyright © 2024 Jonas Kaninda -*/ - import ( "fmt" "github.com/spf13/cobra" diff --git a/docker/Dockerfile b/docker/Dockerfile index 8604c9d..afd8a29 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -36,7 +36,7 @@ ENV TARGET_DB_NAME="" ENV TARGET_DB_USERNAME="" ENV TARGET_DB_PASSWORD="" ARG DEBIAN_FRONTEND=noninteractive -ENV VERSION="v1.2.4" +ENV VERSION="v1.2.5" ENV BACKUP_CRON_EXPRESSION="" ARG WORKDIR="/config" ARG BACKUPDIR="/backup" diff --git a/docs/reference/index.md b/docs/reference/index.md index 5cb84bf..55ba6a4 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -62,6 +62,8 @@ Backup, restore and migrate targets, schedule and retention are configured using | TARGET_DB_NAME | Optional, required for database migration | Target database name | | TARGET_DB_USERNAME | Optional, required for database migration | Target database username | | TARGET_DB_PASSWORD | Optional, required for database migration | Target database password | +| TG_TOKEN | Optional, required for Telegram notification | Telegram token | +| TG_CHAT_ID | Optional, required for Telegram notification | Telegram Chat ID | --- ## Run in Scheduled mode diff --git a/main.go b/main.go index 5666f92..810be93 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,11 @@ +// Package main / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package main -//main -/***** -* MySQL Backup & Restore -* @author Jonas Kaninda -* @license MIT License -* @link https://github.com/jkaninda/pg-bkup -**/ import "github.com/jkaninda/pg-bkup/cmd" func main() { diff --git a/pkg/backup.go b/pkg/backup.go index 94c4f1d..6632747 100644 --- a/pkg/backup.go +++ b/pkg/backup.go @@ -1,7 +1,9 @@ -// Package pkg /* -/* -Copyright © 2024 Jonas Kaninda -*/ +// Package pkg / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package pkg import ( @@ -17,6 +19,7 @@ import ( ) func StartBackup(cmd *cobra.Command) { + utils.Welcome() //Set env utils.SetEnv("STORAGE_PATH", storagePath) utils.GetEnv(cmd, "period", "BACKUP_CRON_EXPRESSION") @@ -198,6 +201,8 @@ func localBackup(db *dbConfig, backupFileName string, disableCompression bool, p } utils.Info("Backup name is %s", finalFileName) moveToBackup(finalFileName, storagePath) + //Send notification + utils.NotifySuccess(finalFileName) //Delete old backup if prune { deleteOldBackup(backupRetention) @@ -240,6 +245,8 @@ func s3Backup(db *dbConfig, backupFileName string, disableCompression bool, prun } } utils.Done("Uploading backup archive to remote storage S3 ... done ") + //Send notification + utils.NotifySuccess(finalFileName) //Delete temp deleteTemp() } @@ -273,6 +280,8 @@ func sshBackup(db *dbConfig, backupFileName, remotePath string, disableCompressi } utils.Done("Uploading backup archive to remote storage ... done ") + //Send notification + utils.NotifySuccess(finalFileName) //Delete temp deleteTemp() } diff --git a/pkg/config.go b/pkg/config.go index 1080258..610bbb7 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -1,3 +1,9 @@ +// Package pkg / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package pkg import ( @@ -23,6 +29,10 @@ type targetDbConfig struct { targetDbPassword string targetDbName string } +type TgConfig struct { + Token string + ChatId string +} func getDbConfig(cmd *cobra.Command) *dbConfig { //Set env diff --git a/pkg/encrypt.go b/pkg/encrypt.go index 00127e7..bd85a32 100644 --- a/pkg/encrypt.go +++ b/pkg/encrypt.go @@ -1,3 +1,9 @@ +// Package pkg / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package pkg import ( diff --git a/pkg/helper.go b/pkg/helper.go index be6ca9b..1e5e00e 100644 --- a/pkg/helper.go +++ b/pkg/helper.go @@ -1,3 +1,9 @@ +// Package pkg / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package pkg import ( @@ -125,7 +131,7 @@ func testDatabaseConnection(db *dbConfig) { // Run the command and capture any errors err = cmd.Run() if err != nil { - utils.Error("Error running psql command: %v\nOutput: %s\n", err, out.String()) + utils.Fatal("Error running psql command: %v\nOutput: %s\n", err, out.String()) return } utils.Info("Successfully connected to %s database", db.dbName) diff --git a/pkg/migrate.go b/pkg/migrate.go index 52b2ff5..1706d3d 100644 --- a/pkg/migrate.go +++ b/pkg/migrate.go @@ -1,3 +1,9 @@ +// Package pkg / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package pkg import ( @@ -8,6 +14,7 @@ import ( ) func StartMigration(cmd *cobra.Command) { + utils.Welcome() utils.Info("Starting database migration...") //Get DB config dbConf = getDbConfig(cmd) diff --git a/pkg/restore.go b/pkg/restore.go index 9175c29..d0629e9 100644 --- a/pkg/restore.go +++ b/pkg/restore.go @@ -1,3 +1,9 @@ +// Package pkg / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package pkg import ( @@ -10,7 +16,7 @@ import ( ) func StartRestore(cmd *cobra.Command) { - + utils.Welcome() //Set env utils.SetEnv("STORAGE_PATH", storagePath) diff --git a/pkg/scp.go b/pkg/scp.go index f91b658..3554869 100644 --- a/pkg/scp.go +++ b/pkg/scp.go @@ -1,3 +1,9 @@ +// Package pkg / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package pkg import ( diff --git a/pkg/scripts.go b/pkg/scripts.go index 55cc077..9800b2d 100644 --- a/pkg/scripts.go +++ b/pkg/scripts.go @@ -1,9 +1,11 @@ package pkg -// Package pkg /* -/* -Copyright © 2024 Jonas Kaninda -*/ +// Package pkg / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ import ( "fmt" "github.com/jkaninda/pg-bkup/utils" diff --git a/pkg/var.go b/pkg/var.go index f3b2808..8919960 100644 --- a/pkg/var.go +++ b/pkg/var.go @@ -1,3 +1,9 @@ +// Package pkg / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package pkg const cronLogFile = "/var/log/pg-bkup.log" diff --git a/utils/constant.go b/utils/constant.go index c0d4e92..d92e257 100644 --- a/utils/constant.go +++ b/utils/constant.go @@ -1,3 +1,9 @@ +// Package utils / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package utils const RestoreExample = "pg-bkup restore --dbname database --file db_20231219_022941.sql.gz\n" + diff --git a/utils/logger.go b/utils/logger.go index 6adcd2f..6b0351d 100644 --- a/utils/logger.go +++ b/utils/logger.go @@ -1,3 +1,9 @@ +// Package utils / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package utils import ( @@ -51,9 +57,13 @@ func Fatal(msg string, args ...any) { formattedMessage := fmt.Sprintf(msg, args...) if len(args) == 0 { fmt.Printf("%s ERROR: %s\n", currentTime, msg) + NotifyError(msg) } else { fmt.Printf("%s ERROR: %s\n", currentTime, formattedMessage) + NotifyError(formattedMessage) + } + os.Exit(1) os.Kill.Signal() } diff --git a/utils/notification.go b/utils/notification.go new file mode 100644 index 0000000..3b9591e --- /dev/null +++ b/utils/notification.go @@ -0,0 +1,75 @@ +package utils + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" +) + +func sendMessage(msg string) { + + Info("Sending notification... ") + chatId := os.Getenv("TG_CHAT_ID") + body, _ := json.Marshal(map[string]string{ + "chat_id": chatId, + "text": msg, + }) + url := fmt.Sprintf("%s/sendMessage", getTgUrl()) + // Create an HTTP post request + request, err := http.NewRequest("POST", url, bytes.NewBuffer(body)) + if err != nil { + panic(err) + } + request.Header.Add("Content-Type", "application/json") + client := &http.Client{} + response, err := client.Do(request) + if err != nil { + panic(err) + } + code := response.StatusCode + if code == 200 { + Info("Notification has been sent") + } else { + body, _ := ioutil.ReadAll(response.Body) + Error("Message not sent, error: %s", string(body)) + } + +} +func NotifySuccess(fileName string) { + var vars = []string{ + "TG_TOKEN", + "TG_CHAT_ID", + } + + //Telegram notification + err := CheckEnvVars(vars) + if err == nil { + message := "PostgreSQL Backup \n" + + "Database has been backed up \n" + + "Backup name is " + fileName + sendMessage(message) + } +} +func NotifyError(error string) { + var vars = []string{ + "TG_TOKEN", + "TG_CHAT_ID", + } + + //Telegram notification + err := CheckEnvVars(vars) + if err == nil { + message := "PostgreSQL Backup \n" + + "An error occurred during database backup \n" + + "Error: " + error + sendMessage(message) + } +} + +func getTgUrl() string { + return fmt.Sprintf("https://api.telegram.org/bot%s", os.Getenv("TG_TOKEN")) + +} diff --git a/utils/s3.go b/utils/s3.go index f354616..57d5f8a 100644 --- a/utils/s3.go +++ b/utils/s3.go @@ -1,3 +1,9 @@ +// Package utils / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package utils import ( diff --git a/utils/utils.go b/utils/utils.go index a08fb0d..b7a246f 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1,17 +1,18 @@ +// Package utils / +/***** +@author Jonas Kaninda +@license MIT License +@Copyright © 2024 Jonas Kaninda +**/ package utils -/***** -* PostgreSQL Backup & Restore -* @author Jonas Kaninda -* @license MIT License -* @link https://github.com/jkaninda/pg-bkup -**/ import ( "fmt" "github.com/spf13/cobra" "io" "io/fs" "os" + "strconv" ) func FileExists(filename string) bool { @@ -149,6 +150,13 @@ func CheckEnvVars(vars []string) error { return nil } +func Welcome() { + fmt.Println() + fmt.Println("**********************************") + fmt.Println(" PostgreSQL Backup ") + fmt.Println(" @Copyright © 2024 jkaninda ") + fmt.Println("***********************************") +} // MakeDir create directory func MakeDir(dirPath string) error { @@ -167,3 +175,14 @@ func MakeDirAll(dirPath string) error { } return nil } +func GetIntEnv(envName string) int { + val := os.Getenv(envName) + if val == "" { + return 0 + } + ret, err := strconv.Atoi(val) + if err != nil { + Error("Error: %v", err) + } + return ret +}