Skip to content

Commit

Permalink
fix: hook payload error
Browse files Browse the repository at this point in the history
update: Readme.md
fix: merge conflicts
  • Loading branch information
isarojdahal committed Sep 5, 2024
2 parents 83beaf3 + 0a5864e commit 0375e8f
Show file tree
Hide file tree
Showing 15 changed files with 139 additions and 141 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ crontab -e
_OR, you can use process managers as_

```
pm2 start src/index.js --name dbbackup --cron "* * * * *"
pm2 start src/index.mjs --name dbbackup --cron "* * * * *"
```

## Feel Free To Contribute 👌
Expand Down
3 changes: 2 additions & 1 deletion config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import "dotenv/config";
import { ConfigType } from "./src/@types/types";
import EnvConfig from "./src/constants/env.config";
Expand All @@ -10,13 +9,15 @@ const dbConfig: ConfigType[] = [
user: EnvConfig.POSTGRES_DB_USER,
password: EnvConfig.POSTGRES_DB_PASSWORD,
type: "postgres",
port:5432
},
{
host: EnvConfig.MYSQL_DB_HOST,
db_name: EnvConfig.MYSQL_DB_NAME,
user: EnvConfig.MYSQL_DB_USER,
password: EnvConfig.MYSQL_DB_PASSWORD,
type: "mysql",
port:3306
},
// add multiple databases here
];
Expand Down
4 changes: 1 addition & 3 deletions src/@types/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ChildProcessWithoutNullStreams } from "child_process";

export type NotifyOnMedium = "SLACK" | "DISCORD";
export type BackupDest = "GMAIL" | "S3_BUCKET";
export type NotifyOnMedium = "SLACK" | "DISCORD" | "CUSTOM";

export type ConfigType = {
host?: string;
Expand All @@ -10,7 +9,6 @@ export type ConfigType = {
password?: string;
type: "postgres" | "mysql";
port?: number;
backupDest?: BackupDest;
};

export interface DumpType {
Expand Down
4 changes: 2 additions & 2 deletions src/constants/Print.ts → src/constants/Log.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Logger } from "./logger";

class Print {
class Log {
static error(message: string) {
Logger.error(message);
}
Expand All @@ -19,4 +19,4 @@ class Print {
}
}

export default Print;
export default Log;
12 changes: 5 additions & 7 deletions src/constants/env.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,24 @@ class EnvConfig {
public static POSTGRES_DB_HOST = process.env.POSTGRES_DB_HOST as string;
public static POSTGRES_DB_NAME = process.env.POSTGRES_DB_NAME as string;
public static POSTGRES_DB_USER = process.env.POSTGRES_DB_USER as string;
public static POSTGRES_DB_PASSWORD = process.env
.POSTGRES_DB_PASSWORD as string;
public static POSTGRES_DB_PASSWORD = process.env.POSTGRES_DB_PASSWORD as string;

// MYSQL DATABASE CONFIGURATION
public static MYSQL_DB_HOST = process.env.MYSQL_DB_HOST as string;
public static MYSQL_DB_NAME = process.env.MYSQL_DB_NAME as string;
public static MYSQL_DB_USER = process.env.MYSQL_DB_USER as string;
public static MYSQL_DB_PASSWORD = process.env.MYSQL_DB_PASSWORD as string;

// DISCORD CONFIGURATION
// WEBHOOK NOTIFICATION CONFIGURATION
public static BACKUP_NOTIFICATION = process.env.BACKUP_NOTIFICATION as string;
public static DISCORD_WEBHOOK_URL = process.env.DISCORD_WEBHOOK_URL as string;

// SLACK CONFIGURATION
public static SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL as string;
public static CUSTOM_WEBHOOK_URL = process.env.CUSTOM_WEBHOOK_URL as string;


// S3 CONFIGURATION
public static AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID as string;
public static AWS_SECRET_ACCESS_KEY = process.env
.AWS_SECRET_ACCESS_KEY as string;
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;
}
Expand Down
4 changes: 2 additions & 2 deletions src/constants/message.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const NotificationMessage = {
SUCCESS: `Backup successful at ${new Date()}`,
FAILURE: `Backup failed at ${new Date()}`,
SUCCESS: `[+] Backup successful at ${new Date()}`,
FAILURE: `[-] Backup failed at ${new Date()}`,
};
11 changes: 5 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import backupHelper from "./utils/backup.utils";
import Print from "./constants/Print";
import dbConfig from "../config";
import { exec } from "child_process";
import { ConfigType } from "./@types/types";
import { promisify } from "util";
import { notify } from "./utils/notify.utils";
import Log from "./constants/Log";
import { sendNotification } from "./utils/notify.utils";
import EnvConfig from "./constants/env.config";

// Promisify exec to use with async/await
export const execAsync = promisify(exec);

Expand All @@ -17,15 +16,15 @@ const main = async (configs: ConfigType[]) => {
try {
const dumpInfo = await backupHelper(config);
if (!dumpInfo) {
Print.error("Backup failed.");
Log.error("Backup failed.");
return;
}

await notify(EnvConfig.BACKUP_NOTIFICATION, {
await sendNotification(EnvConfig.BACKUP_NOTIFICATION, {
databaseName: dumpInfo.databaseName,
});
} catch (e: unknown) {
Print.error("Backup failed." + e);
Log.error("Backup failed." + e);
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/notifiers/CustomNotifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Notifier } from "./Notifier";

export class CustomNotifier extends Notifier {
constructor(webhookUrl: string, message: string) {
super(webhookUrl, message);
}
sendNotification(): void {
this.notify();
}

}
43 changes: 4 additions & 39 deletions src/notifiers/DiscordNotifier.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,11 @@
import Print from "../constants/Print";
import { Notifier } from "./Notifier";

export class DiscordNotifier extends Notifier {
private message: string = "";

constructor(private readonly webhookUrl: string) {
super();
this.webhookUrl = webhookUrl;
constructor(webhookUrl: string, message: string) {
super(webhookUrl, message);
}

validate() {
if (!this.webhookUrl) {
Print.error("DISCORD_WEBHOOK_URL is not set");
throw new Error("DISCORD_WEBHOOK_URL is not set");
}
sendNotification(): void {
this.notify();
}

async notify(message?: string) {
if (message) {
this.message = message;
}
this.validate();
const discordMessage = {
content: this.message,
username: "Captain Hook",
};
try {
await fetch(this.webhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(discordMessage),
});
console.log("Discord Notification Successfully Sent...");
} catch (error) {
console.error(error);
}
}

withMessage(message: string) {
this.message = message;
return this;
}
}
58 changes: 55 additions & 3 deletions src/notifiers/Notifier.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,59 @@
import Log from "../constants/Log";

export abstract class Notifier {
abstract validate(): void;
// eslint-disable-next-line
abstract notify(message?: string): void;
protected webhookUrl: string;
protected message: string;

constructor(webhookUrl: string, message: string) {
this.webhookUrl = webhookUrl;
this.message = message;
}

abstract sendNotification(): void;

validate(): void {
if (!this.webhookUrl) {
throw new Error("[-] Webhook URL is not set");
}

const newUrl = new URL(this.webhookUrl);
if (newUrl.protocol !== "http:" && newUrl.protocol !== "https:") {
throw new Error("[-] Webhook URL is invalid.");
}
}

async notify(): Promise<void> {
try {
const response = await fetch(this.webhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
text: this.message,
}),
});

if (!response.ok) {
console.error(
`[-] Failed to send notification to ${new URL(
this.webhookUrl
).hostname.replace(".com", "")}: ${response.status} ${
response.statusText
} `
);
} else {
console.log(
`[+] Notification sent successfully to ${new URL(
this.webhookUrl
).hostname.replace(".com", "")}`
);
}
} catch (error) {
Log.error(`Error sending notification: ${error}`);
console.error(`[-] Error sending notification: ${error}`);
}
}
}

export interface NotifierOption {
Expand Down
42 changes: 4 additions & 38 deletions src/notifiers/SlackNotifier.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,11 @@
import Print from "../constants/Print";
import { Notifier } from "./Notifier";

export class SlackNotifier extends Notifier {
private message: string = "";

constructor(private readonly webhookUrl: string) {
super();
this.webhookUrl = webhookUrl;
constructor(webhookUrl: string, message: string) {
super(webhookUrl, message);
}

validate() {
if (!this.webhookUrl) {
Print.error("SLACK_WEBHOOK_URL is not set");
throw new Error("SLACK_WEBHOOK_URL is not set");
}
sendNotification(): void {
this.notify();
}

async notify(message?: string) {
if (message) {
this.message = message;
}
this.validate();
const slackMessage = {
text: `AutoBackup🤖: ${this.message}`,
};
try {
await fetch(this.webhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(slackMessage),
});
console.log("Slack Notification Successfully Sent...");
} catch (error) {
console.error(error);
}
}

withMessage(message: string) {
this.message = message;
return this;
}
}
26 changes: 13 additions & 13 deletions src/utils/backup.utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createWriteStream, existsSync, mkdirSync, rmSync } from "fs";
import * as fs from "fs";
import Print from "../constants/Print";
import Log from "../constants/Log";
import path from "path";
import { ConfigType, DumpInfo, DumpType } from "../@types/types";
import { execAsync } from "..";
Expand All @@ -22,8 +22,8 @@ const handleDumpError = (
databaseName: string,
dumpFilePath: string
) => {
console.error(`Error spawning dump process: ${err}`);
Print.error(`Cannot backup ${databaseName}`);
console.error(`[-] Error spawning dump process: ${err}`);
Log.error(`Cannot backup ${databaseName}`);
if (existsSync(dumpFilePath)) {
rmSync(dumpFilePath);
rmSync(`${dumpFilePath}.zip`);
Expand All @@ -36,8 +36,8 @@ const handleDumpFailure = (
databaseName: string,
dumpFilePath: string
) => {
console.error(`Dump process failed with code ${code}. Error: ${errorMsg}`);
Print.error(`Cannot backup ${databaseName}`);
console.error(`[-] Dump process failed with code ${code}. Error: ${errorMsg}`);
Log.error(`Cannot backup ${databaseName}`);
if (existsSync(dumpFilePath)) {
rmSync(dumpFilePath);
rmSync(`${dumpFilePath}.zip`);
Expand Down Expand Up @@ -68,9 +68,9 @@ const finalizeBackup = async (dumpFilePath: string, databaseName: string) => {
return compressedFilePath;
} catch (err: unknown) {
console.error(
`Error compressing ${databaseName}: ${(err as Error).message}`
`[-] Error compressing ${databaseName}: ${(err as Error).message}`
);
Print.error(`Error compressing ${databaseName}`);
Log.error(`Error compressing ${databaseName}`);
return "";
}
};
Expand All @@ -90,7 +90,7 @@ const backupHelper = async (data: ConfigType): Promise<DumpInfo | null> => {
break;
default:
return Promise.reject(
new Error(`Unsupported database type: ${data.type}`)
new Error(`[-] Unsupported database type: ${data.type}`)
);
}

Expand All @@ -109,29 +109,29 @@ const backupHelper = async (data: ConfigType): Promise<DumpInfo | null> => {

dumpedContent.stderr.on("data", (chunk) => {
errorMsg = chunk.toString();
Print.error(errorMsg ?? "Error occurred while dumping");
Log.error(errorMsg ?? "Error occurred while dumping");
});

dumpedContent.on("error", (err) => {
handleDumpError(err.message, databaseName, dumpFilePath);
reject(new Error(`Cannot backup ${databaseName}`));
reject(new Error(`[-] Cannot backup ${databaseName}`));
});

dumpedContent.on("close", async (code: number) => {
if (code !== 0 || errorMsg) {
handleDumpFailure(code, errorMsg, databaseName, dumpFilePath);
reject(new Error(`Cannot backup ${databaseName}`));
reject(new Error(`[-] Cannot backup ${databaseName}`));
return;
}

console.log(`Backup of ${databaseName} completed successfully`);
console.log(`[+] Backup of ${databaseName} completed successfully`);
const compressedFilePath = await finalizeBackup(
dumpFilePath,
databaseName
);
if (compressedFilePath) {
// Remove locally created dump files.
console.log(`Removing dump file.. ${dumpFilePath}`);
console.log(`[+] Removing dump file.. ${dumpFilePath}`);
rmSync(dumpFilePath);
rmSync(`${dumpFilePath}.zip`);

Expand Down
Loading

0 comments on commit 0375e8f

Please sign in to comment.