Skip to content

Commit

Permalink
Merge pull request #23 from mukezhz/main
Browse files Browse the repository at this point in the history
REFACTOR | Used yaml for the config
  • Loading branch information
isarojdahal authored Sep 16, 2024
2 parents 7fdcab0 + 8f79f75 commit a4b8860
Show file tree
Hide file tree
Showing 36 changed files with 1,206 additions and 218 deletions.
26 changes: 26 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#MAIL
MAIL_USER='[email protected]'
MAIL_PASSWORD='examplepassword'

#BACKUP_CONFIG
BACKUP_DEST= # To implement other Future features will be S3_BUCKET, DROPBOX, GOOGLE_DRIVE
# To be implemented in future
# BACKUP_NOTFICATION= DISCORD # other Future features will be SLACK, Telegram


# POSTGRES DATABASE CONFIGURATION
POSTGRES_DB_HOST='127.0.0.1'
POSTGRES_DB_NAME='dbname'
POSTGRES_DB_USER='postgres'
POSTGRES_DB_PASSWORD='pass'

# MYSQL DATABASE CONFIGURATION
MYSQL_HOST='127.0.0.1'
MYSQL_DB_NAME='mysqldb'
MYSQL_DB_USER='root'
MYSQL_DB_PASSWORD='my-secret-pw'

SLACK_WEBHOOK_URL="https://hooks.slack.com/services/XXXX/XXXX/XXXX"
BACKUP_NOTIFICATION=SLACK

CONFIG=backupbee.json
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ backups/
.zip
.vscode/
limited_dump.sql
tmp/
config.ts
backupdbee.yaml
backupdbee.yml
68 changes: 68 additions & 0 deletions backupdbee.yaml.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
general:
backup_location: backups
log_location: logs
log_level: INFO
retention_policy_days: 7
backup_schedule: "0 3 * * *"

destinations:
local:
enabled: true
path: backups

s3:
enabled: false
bucket_name: backupbee
region: us-east-1
access_key: XXXXXXXXXXX
secret_key: XXXXXXXXXXX

email:
enabled: false
smtp_server: smtp.gmail.com
smtp_port: 587
smtp_username:
smtp_password:

notifications:
email:
enabled: false
from: [email protected]
to:
- [email protected]

slack:
enabled: true
webhook_url: https://hooks.slack.com/services/XXXXXXXXXXXXXXXXXXXXXXXX

custom:
enabled: false
web_url: https://backupbee.com/api/v1/backup

discord:
enabled: false
webhook_url: https://discord.com/api/webhooks/XXXXXXXXX/XXXXXXXXX

telegram:
enabled: true
webhook_url: https://api.telegram.org/botXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/sendMessage
chatId: XXXXXXX

databases:
- name: primary_db
type: postgres
host: localhost
port: 5432
username: postgres
password: pass
database_name: asterconsult
backup_schedule: "0 3 * * *"

# - name: secondary_db
# type: mysql
# host: 192.168.1.10
# port: 3306
# username: backup_user
# password: supersecret
# database_name: my_secondary_db
# backup_schedule: "0 4 * * *"
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"dotenv": "^16.4.5",
"nodemailer": "^6.9.14",
"ts-node": "^10.9.2",
"winston": "^3.14.1"
"winston": "^3.14.1",
"yaml": "^2.5.1"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
Expand Down
77 changes: 77 additions & 0 deletions src/@types/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
export interface General {
backup_location: string;
log_location: string;
log_level: string;
retention_policy_days: number;
backup_schedule: string;
}

export interface Notifications {
email: Email;
slack: Slack;
custom: Custom;
discord: Discord;
telegram: Telegram;
}

export interface Email {
enabled: boolean;
from: string;
to: string[];
}

export interface Slack {
enabled: boolean;
webhook_url: string;
}

export interface Custom {
enabled: boolean;
webhook_url: string;
}

export interface Discord {
enabled: boolean;
webhook_url: string;
}

export interface Telegram {
enabled: boolean;
webhook_url: string;
chatId: number;
}

export interface Database {
name: string;
type: string;
host: string;
port: number;
username: string;
password: string;
database_name: string;
backup_schedule: string;
}

export interface Local {
enabled: boolean;
path: string;
}

export interface S3Bucket {
enabled: boolean;
bucket: string;
region: string;
}

export interface Destinations {
local: Local;
s3_bucket: S3Bucket;
email: Email;
}

export interface DataBeeConfig {
general: General;
notifications: Notifications;
databases: Database[];
destinations: Destinations;
}
11 changes: 6 additions & 5 deletions src/@types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { ChildProcessWithoutNullStreams } from "child_process";
export type NotifyOnMedium = "SLACK" | "DISCORD" | "CUSTOM";

export type ConfigType = {
host?: string;
db_name?: string;
user?: string;
password?: string;
name: string;
host: string;
db_name: string;
user: string;
password: string;
type: "postgres" | "mysql";
port?: number;
port: number;
};

export interface DumpType {
Expand Down
3 changes: 3 additions & 0 deletions src/constants/env.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class EnvConfig {
public static AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY as string;
public static AWS_S3_BUCKET_NAME = process.env.AWS_S3_BUCKET_NAME as string;
public static AWS_REGION = process.env.AWS_REGION as string;

// Backup Configuration
public static CONFIG = process.env.CONFIG as string;
}

export default EnvConfig;
File renamed without changes.
14 changes: 14 additions & 0 deletions src/constants/notifications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const NOTIFICATION = {
EMAIL: "EMAIL",
SLACK: "SLACK",
CUSTOM: "CUSTOM",
DISCORD: "DISCORD",
TELEGRAM: "TELEGRAM",
};


export const DESTINATION = {
EMAIL: "EMAIL",
S3_BUCKET: "S3_BUCKET",
LOCAL: "LOCAL",
};
6 changes: 3 additions & 3 deletions src/dbs/mysql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { ConfigType, DumpType } from "../@types/types";
export const handleMysqlDump = (data: ConfigType, dumps: DumpType[]): DumpType[] => {
const dbNames = data.db_name?.includes(",")
? data.db_name.split(",")
: [data.db_name!];
const args = ["-h", data.host!, "-u", data.user!, "--databases", ...dbNames];
: [data.db_name];
const args = ["-h", data.host, "-u", data.user, "--databases", ...dbNames];
// eslint-disable-next-line no-undef
const env = { ...process.env, MYSQL_PWD: data.password };

const dumpProcess = spawnDumpProcess("mysqldump", args, env);

dumps.push({
databaseName: data.db_name!,
databaseName: data.db_name,
dumpedContent: dumpProcess,
});
return dumps;
Expand Down
8 changes: 4 additions & 4 deletions src/dbs/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ export const handlePostgresDump = (
data: ConfigType,
dumps: DumpType[]
): DumpType[] => {
const dbNames = data.db_name!.includes(",")
? data.db_name!.split(",")
: [data.db_name!];
const dbNames = data.db_name.includes(",")
? data.db_name.split(",")
: [data.db_name];

dbNames.forEach((dbName) => {
const args = ["-h", data.host!, "-U", data.user!, "-d", dbName];
const args = ["-h", data.host, "-U", data.user, "-d", dbName];
// eslint-disable-next-line no-undef
const env = { ...process.env, PGPASSWORD: data.password };

Expand Down
76 changes: 76 additions & 0 deletions src/destinations/email.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { createTransport } from "nodemailer";
import EnvConfig from "../constants/env.config";
import Log from "../constants/log";
import { Sender, SenderOption } from "./sender";
import Mail from "nodemailer/lib/mailer";

export class EmailSender implements Sender {
private static transporterInstance: Mail | null = null;
private static getTransporter(): Mail {
if (!EmailSender.transporterInstance) {
EmailSender.transporterInstance = createTransport({
host: "smtp.gmail.com",
secure: true,
requireTLS: true,
auth: {
user: EnvConfig.MAIL_USER,
pass: EnvConfig.MAIL_PASSWORD,
},
});
}
return EmailSender.transporterInstance;
}

private mailOptions: Mail.Options;
constructor(readonly from: string, readonly to: string[], filePath: string) {
this.mailOptions = {
from: from,
to: to.join(","),
subject: "Backup",
html: "<h1>Date : " + new Date() + "</h1>",
attachments: [{ path: filePath }],
};
}

validate(): void {
if (!this.mailOptions.from) {
throw new Error("[-] Email address is not set");
}

const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
if (!emailRegex.test(this.mailOptions.from as string)) {
throw new Error("[-] Email address is invalid.");
}

if (!EnvConfig.MAIL_USER || !EnvConfig.MAIL_PASSWORD) {
throw new Error("[-] MAIL_USER or MAIL_PASSWORD is not set");
}
const transporter = EmailSender.getTransporter();
transporter.verify(function (error) {
if (error) {
console.log(error);
Log.error("[-] Mail setup failed");
} else {
console.log("[+] Sending backups...");
}
});
}

async send(option?: SenderOption): Promise<void> {
try {
if (option?.fileName) {
this.mailOptions.attachments = [{ path: option.fileName }];
}
const transporter = EmailSender.getTransporter();
await transporter.sendMail(this.mailOptions);
} catch (error: unknown) {
if (error instanceof Error) {
Log.error(`Error sending email: ${error.message}`);
console.error(`[-] Error sending email: ${error.message}`);
} else {
Log.error(`Unknown error occurred.`);
console.error(`[-] Unknown error occurred.`);
}
}
}
}
28 changes: 28 additions & 0 deletions src/destinations/local.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as fs from "fs";
import { ensureDirectory } from "../utils/file.utils";
import { Sender } from "./sender";
import path from "path";

export class LocalSender implements Sender {
private backupDir: string = "";
constructor(private filePath: string, private compressedFilePath: string) {
this.filePath = filePath;
this.compressedFilePath = compressedFilePath;
}

validate(): void {
const backupDir = path.resolve(this.filePath);
this.backupDir = backupDir;
ensureDirectory(backupDir);
}

async send(): Promise<void> {
fs.copyFileSync(
this.compressedFilePath,
path.resolve(
this.backupDir,
this.compressedFilePath.split("/").pop() as string
)
);
}
}
Loading

0 comments on commit a4b8860

Please sign in to comment.