feat: Add Telegram notification

This commit is contained in:
Jonas Kaninda
2024-09-10 22:59:28 +02:00
parent 90fd4642f2
commit 13ea6194d3
22 changed files with 229 additions and 32 deletions

View File

@@ -1,3 +1,9 @@
// Package cmd /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package cmd package cmd
import ( import (

View File

@@ -1,3 +1,9 @@
// Package cmd /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package cmd package cmd
import ( import (

View File

@@ -1,3 +1,9 @@
// Package cmd /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package cmd package cmd
import ( import (

View File

@@ -1,7 +1,9 @@
// Package cmd /* // Package cmd /
/* /*****
Copyright © 2024 Jonas Kaninda @author Jonas Kaninda
*/ @license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package cmd package cmd
import ( import (

View File

@@ -1,9 +1,11 @@
// Package cmd /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package cmd package cmd
/*
Copyright © 2024 Jonas Kaninda
*/
import ( import (
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"

View File

@@ -36,7 +36,7 @@ ENV TARGET_DB_NAME=""
ENV TARGET_DB_USERNAME="" ENV TARGET_DB_USERNAME=""
ENV TARGET_DB_PASSWORD="" ENV TARGET_DB_PASSWORD=""
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
ENV VERSION="v1.2.4" ENV VERSION="v1.2.5"
ENV BACKUP_CRON_EXPRESSION="" ENV BACKUP_CRON_EXPRESSION=""
ARG WORKDIR="/config" ARG WORKDIR="/config"
ARG BACKUPDIR="/backup" ARG BACKUPDIR="/backup"

View File

@@ -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_NAME | Optional, required for database migration | Target database name |
| TARGET_DB_USERNAME | Optional, required for database migration | Target database username | | TARGET_DB_USERNAME | Optional, required for database migration | Target database username |
| TARGET_DB_PASSWORD | Optional, required for database migration | Target database password | | 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 ## Run in Scheduled mode

13
main.go
View File

@@ -1,12 +1,11 @@
// Package main /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package main package main
//main
/*****
* MySQL Backup & Restore
* @author Jonas Kaninda
* @license MIT License <https://opensource.org/licenses/MIT>
* @link https://github.com/jkaninda/pg-bkup
**/
import "github.com/jkaninda/pg-bkup/cmd" import "github.com/jkaninda/pg-bkup/cmd"
func main() { func main() {

View File

@@ -1,7 +1,9 @@
// Package pkg /* // Package pkg /
/* /*****
Copyright © 2024 Jonas Kaninda @author Jonas Kaninda
*/ @license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package pkg package pkg
import ( import (
@@ -17,6 +19,7 @@ import (
) )
func StartBackup(cmd *cobra.Command) { func StartBackup(cmd *cobra.Command) {
utils.Welcome()
//Set env //Set env
utils.SetEnv("STORAGE_PATH", storagePath) utils.SetEnv("STORAGE_PATH", storagePath)
utils.GetEnv(cmd, "period", "BACKUP_CRON_EXPRESSION") 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) utils.Info("Backup name is %s", finalFileName)
moveToBackup(finalFileName, storagePath) moveToBackup(finalFileName, storagePath)
//Send notification
utils.NotifySuccess(finalFileName)
//Delete old backup //Delete old backup
if prune { if prune {
deleteOldBackup(backupRetention) 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 ") utils.Done("Uploading backup archive to remote storage S3 ... done ")
//Send notification
utils.NotifySuccess(finalFileName)
//Delete temp //Delete temp
deleteTemp() deleteTemp()
} }
@@ -273,6 +280,8 @@ func sshBackup(db *dbConfig, backupFileName, remotePath string, disableCompressi
} }
utils.Done("Uploading backup archive to remote storage ... done ") utils.Done("Uploading backup archive to remote storage ... done ")
//Send notification
utils.NotifySuccess(finalFileName)
//Delete temp //Delete temp
deleteTemp() deleteTemp()
} }

View File

@@ -1,3 +1,9 @@
// Package pkg /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package pkg package pkg
import ( import (
@@ -23,6 +29,10 @@ type targetDbConfig struct {
targetDbPassword string targetDbPassword string
targetDbName string targetDbName string
} }
type TgConfig struct {
Token string
ChatId string
}
func getDbConfig(cmd *cobra.Command) *dbConfig { func getDbConfig(cmd *cobra.Command) *dbConfig {
//Set env //Set env

View File

@@ -1,3 +1,9 @@
// Package pkg /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package pkg package pkg
import ( import (

View File

@@ -1,3 +1,9 @@
// Package pkg /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package pkg package pkg
import ( import (
@@ -125,7 +131,7 @@ func testDatabaseConnection(db *dbConfig) {
// Run the command and capture any errors // Run the command and capture any errors
err = cmd.Run() err = cmd.Run()
if err != nil { 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 return
} }
utils.Info("Successfully connected to %s database", db.dbName) utils.Info("Successfully connected to %s database", db.dbName)

View File

@@ -1,3 +1,9 @@
// Package pkg /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package pkg package pkg
import ( import (
@@ -8,6 +14,7 @@ import (
) )
func StartMigration(cmd *cobra.Command) { func StartMigration(cmd *cobra.Command) {
utils.Welcome()
utils.Info("Starting database migration...") utils.Info("Starting database migration...")
//Get DB config //Get DB config
dbConf = getDbConfig(cmd) dbConf = getDbConfig(cmd)

View File

@@ -1,3 +1,9 @@
// Package pkg /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package pkg package pkg
import ( import (
@@ -10,7 +16,7 @@ import (
) )
func StartRestore(cmd *cobra.Command) { func StartRestore(cmd *cobra.Command) {
utils.Welcome()
//Set env //Set env
utils.SetEnv("STORAGE_PATH", storagePath) utils.SetEnv("STORAGE_PATH", storagePath)

View File

@@ -1,3 +1,9 @@
// Package pkg /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package pkg package pkg
import ( import (

View File

@@ -1,9 +1,11 @@
package pkg package pkg
// Package pkg /* // Package pkg /
/* /*****
Copyright © 2024 Jonas Kaninda @author Jonas Kaninda
*/ @license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
import ( import (
"fmt" "fmt"
"github.com/jkaninda/pg-bkup/utils" "github.com/jkaninda/pg-bkup/utils"

View File

@@ -1,3 +1,9 @@
// Package pkg /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package pkg package pkg
const cronLogFile = "/var/log/pg-bkup.log" const cronLogFile = "/var/log/pg-bkup.log"

View File

@@ -1,3 +1,9 @@
// Package utils /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package utils package utils
const RestoreExample = "pg-bkup restore --dbname database --file db_20231219_022941.sql.gz\n" + const RestoreExample = "pg-bkup restore --dbname database --file db_20231219_022941.sql.gz\n" +

View File

@@ -1,3 +1,9 @@
// Package utils /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package utils package utils
import ( import (
@@ -51,9 +57,13 @@ func Fatal(msg string, args ...any) {
formattedMessage := fmt.Sprintf(msg, args...) formattedMessage := fmt.Sprintf(msg, args...)
if len(args) == 0 { if len(args) == 0 {
fmt.Printf("%s ERROR: %s\n", currentTime, msg) fmt.Printf("%s ERROR: %s\n", currentTime, msg)
NotifyError(msg)
} else { } else {
fmt.Printf("%s ERROR: %s\n", currentTime, formattedMessage) fmt.Printf("%s ERROR: %s\n", currentTime, formattedMessage)
NotifyError(formattedMessage)
} }
os.Exit(1) os.Exit(1)
os.Kill.Signal() os.Kill.Signal()
} }

75
utils/notification.go Normal file
View File

@@ -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"))
}

View File

@@ -1,3 +1,9 @@
// Package utils /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package utils package utils
import ( import (

View File

@@ -1,17 +1,18 @@
// Package utils /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package utils package utils
/*****
* PostgreSQL Backup & Restore
* @author Jonas Kaninda
* @license MIT License <https://opensource.org/licenses/MIT>
* @link https://github.com/jkaninda/pg-bkup
**/
import ( import (
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"io" "io"
"io/fs" "io/fs"
"os" "os"
"strconv"
) )
func FileExists(filename string) bool { func FileExists(filename string) bool {
@@ -149,6 +150,13 @@ func CheckEnvVars(vars []string) error {
return nil return nil
} }
func Welcome() {
fmt.Println()
fmt.Println("**********************************")
fmt.Println(" PostgreSQL Backup ")
fmt.Println(" @Copyright © 2024 jkaninda ")
fmt.Println("***********************************")
}
// MakeDir create directory // MakeDir create directory
func MakeDir(dirPath string) error { func MakeDir(dirPath string) error {
@@ -167,3 +175,14 @@ func MakeDirAll(dirPath string) error {
} }
return nil 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
}