diff --git a/backup/.env b/backup/.env index 34d409a..c530031 100644 --- a/backup/.env +++ b/backup/.env @@ -1,25 +1,35 @@ -# Database Configuration -DB_HOST=1xxxx +## Database Configuration +DB_HOST=147.93.98.152 DB_USER=devuser -DB_PASSWORD=xxxxx -DB_NAME_PROD=MarcoBMSProd -DB_NAME_STAGE=MarcoBMSStage -DB_NAME_GITA=gitea -DB_NAME_MEDIAWIKI=mediawiki -DB_NAME_REDMINE=redmine +DB_PASSWORD=xxxxxxx -# AWS S3 Configuration -ACCESS_KEY=xxxxxxxx -SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -S3_BUCKET_NAME=your-s3-bucket-name +DB_LIST_TO_BACKUP=MarcoBMSProd,MarcoBMSStage,gitea,mediawiki,redmine + +UPLOAD_TO_S3=true +UPLOAD_TO_GIT=false + +## AWS S3 Configuration +ACCESS_KEY=xxxxxxx +SECRET_KEY=xxxxxx +S3_BUCKET_NAME=xxxxxxxx S3_REGION=us-east-1 -# Windows Specific Paths (if applicable, uncomment and adjust) -# WIN_BACKUP_DIR=C:/gita/database/backup -# WIN_MYSQLDUMP_PATH=C:/Program Files/MySQL/MySQL Server 8.0/bin/mysqldump.exe -# WIN_LOG_FILE=C:/gita/backup_log.txt +## send mail once process is complete +EMAIL_HOST=smtp.gmail.com +EMAIL_PORT=587 +EMAIL_USER=marcoioitsoft@gmail.com +EMAIL_PASS= "" +EMAIL_SUBJECT="Database backup process" +EMAIL_RECEIVERS=vikas@marcoaiot.com,hr@marcoaiot.com -# Linux Specific Paths (if applicable, uncomment and adjust) -# LINUX_BACKUP_DIR=/var/lib/mysql-backups -# LINUX_MYSQLDUMP_PATH=/usr/bin/mysqldump -# LINUX_LOG_FILE=/var/log/mysql_backup.log \ No newline at end of file +## Windows Specific Paths (if applicable, uncomment and adjust) +BACKUP_DIR=E:\Office\Marco\gitea\database\test +MYSQLDUMP_PATH=C:/Program Files/MySQL/MySQL Server 8.0/bin/mysqldump.exe +LOG_DIR=E:\Office\Marco\gitea\database\test\ +LOG_FILE=E:\Office\Marco\gitea\database\test\backup_log.txt + +## Linux Specific Paths (if applicable, uncomment and adjust) +# BACKUP_DIR=/var/lib/mysql-backups +# MYSQLDUMP_PATH=/usr/bin/mysqldump +# LOG_FILE=/var/www/apps/db-backup-script/logs/mysql_backup.log +# LOG_DIR=/var/www/apps/db-backup-script/logs \ No newline at end of file diff --git a/backup/database_backup-script.py b/backup/database_backup-script.py index cc40a62..f17298b 100644 --- a/backup/database_backup-script.py +++ b/backup/database_backup-script.py @@ -6,6 +6,7 @@ import sys import boto3 from botocore.exceptions import ClientError from dotenv import load_dotenv # Import load_dotenv +from email_utils import send_email # Load environment variables from .env file load_dotenv() @@ -15,11 +16,7 @@ load_dotenv() DB_HOST = os.getenv('DB_HOST') DB_USER = os.getenv('DB_USER') DB_PASSWORD = os.getenv('DB_PASSWORD') -DB_NAME_PROD = os.getenv('DB_NAME_PROD') -DB_NAME_STAGE = os.getenv('DB_NAME_STAGE') -DB_NAME_GITA = os.getenv('DB_NAME_GITA') -DB_NAME_MEDIAWIKI = os.getenv('DB_NAME_MEDIAWIKI') -DB_NAME_REDMINE = os.getenv('DB_NAME_REDMINE') + ACCESS_KEY = os.getenv('ACCESS_KEY') SECRET_KEY = os.getenv('SECRET_KEY') @@ -27,20 +24,25 @@ SECRET_KEY = os.getenv('SECRET_KEY') S3_BUCKET_NAME = os.getenv('S3_BUCKET_NAME') S3_REGION = os.getenv('S3_REGION') +# Initialize a message list +email_body_parts = [] + # --- Platform-Specific Paths --- # Determine OS and set paths accordingly if sys.platform.startswith('win'): # Paths for Windows # You can also load these from .env if you prefer fine-grained control - BACKUP_DIR = os.getenv('WIN_BACKUP_DIR', "C:/gita/database/backup") # Default if not in .env - MYSQLDUMP_PATH = os.getenv('WIN_MYSQLDUMP_PATH', r'C:\Program Files\MySQL\MySQL Server 8.0\bin\mysqldump.exe') - LOG_FILE = os.getenv('WIN_LOG_FILE', r'C:\gita\backup_log.txt') + BACKUP_DIR = os.getenv('BACKUP_DIR', "C:/gita/database/backup") # Default if not in .env + MYSQLDUMP_PATH = os.getenv('MYSQLDUMP_PATH', r'C:\Program Files\MySQL\MySQL Server 8.0\bin\mysqldump.exe') + LOG_DIR = os.getenv('BACKUP_DIR', "C:/gita/database/backup") # Default if not in .env + LOG_FILE = os.getenv('LOG_FILE', r'C:\gita\backup_log.txt') GIT_EXECUTABLE = "git" # Assuming git is in PATH on Windows else: # Paths for Ubuntu/Linux - BACKUP_DIR = os.getenv('LINUX_BACKUP_DIR', "/var/lib/mysql-backups") # Default if not in .env - MYSQLDUMP_PATH = os.getenv('LINUX_MYSQLDUMP_PATH', "/usr/bin/mysqldump") - LOG_FILE = os.getenv('LINUX_LOG_FILE', "/var/log/mysql_backup.log") + BACKUP_DIR = os.getenv('BACKUP_DIR', "/var/lib/mysql-backups") # Default if not in .env + MYSQLDUMP_PATH = os.getenv('MYSQLDUMP_PATH', "/usr/bin/mysqldump") + LOG_FILE = os.getenv('LOG_FILE', "/var/logs/mysql-backup/mysql_backup.log") + LOG_DIR = os.getenv('LOG_DIR', "/var/logs/mysql-backup") GIT_EXECUTABLE = "git" # Assuming git is in PATH on Linux # --- Logging Setup --- @@ -71,6 +73,12 @@ def build_mysqldump_command(database_name): database_name ] return command +def clean_backup_folder(): + folder_path = BACKUP_DIR + for filename in os.listdir(folder_path): + file_path = os.path.join(folder_path, filename) + if os.path.isfile(file_path): + os.remove(file_path) def start_backup(database): backup_file_path = build_path(database) @@ -80,10 +88,12 @@ def start_backup(database): with open(backup_file_path, "w", encoding="utf-8") as out_file: subprocess.run(command, stdout=out_file, check=True, text=True) logging.info(f"Successfully backed up {database}.") + email_body_parts.append(f"Successfully backed up {database}.") return backup_file_path except subprocess.CalledProcessError as e: logging.error(f"MySQL dump failed for {database}: {e}") logging.error(f"Command: {' '.join(e.cmd)}") + email_body_parts.append(f"MySQL dump failed for {database}: {e}") if e.stderr: logging.error(f"Stderr: {e.stderr}") return None @@ -128,14 +138,33 @@ def upload_to_git(): finally: os.chdir(original_cwd) +def remove_before_first_dash(input_string): + """ + Removes the part of a string before the first dash. + + Args: + input_string: The string to process. + + Returns: + The part of the string after the first dash, or the original string + if no dash is found. + """ + parts = input_string.split('-', 1) # Split only at the first dash + if len(parts) > 1: + return parts[1] + else: + return input_string + def upload_to_s3(file_paths): if not file_paths: logging.info("No backup files to upload to S3.") return # Basic validation for S3 configuration - if not S3_BUCKET_NAME or not S3_REGION: - logging.error("S3_BUCKET_NAME or S3_REGION is not set. Cannot upload to S3. Please check your .env file.") + if not all([S3_BUCKET_NAME, S3_REGION, ACCESS_KEY, SECRET_KEY]): + logging.error("S3_BUCKET_NAME, S3_REGION, ACCESS_KEY or SECRET_KEY is not set. Cannot upload to S3. Please check your .env file.") + email_body_parts.append(f"S3_BUCKET_NAME, S3_REGION, ACCESS_KEY or SECRET_KEY is not set. Cannot upload to S3. Please check your .env file.") + return try: @@ -151,23 +180,35 @@ def upload_to_s3(file_paths): continue s3_object_key = os.path.basename(file_path) + s3_object_key = remove_before_first_dash(s3_object_key) try: logging.info(f"Uploading {s3_object_key} to s3://{S3_BUCKET_NAME}/{s3_object_key}") s3_client.upload_file(file_path, S3_BUCKET_NAME, s3_object_key) logging.info(f"Successfully uploaded {s3_object_key} to S3.") + email_body_parts.append(f"Successfully uploaded {s3_object_key} to S3.") + except ClientError as ce: logging.error(f"Failed to upload {s3_object_key} to S3: {ce}") + email_body_parts.append("Failed to upload {s3_object_key} to S3: {ce}") + if ce.response['Error']['Code'] == 'AccessDenied': logging.error("S3 upload access denied. Check your AWS credentials and bucket policy.") + email_body_parts.append("S3 upload access denied. Check your AWS credentials and bucket policy.") elif ce.response['Error']['Code'] == 'NoSuchBucket': logging.error(f"S3 bucket '{S3_BUCKET_NAME}' does not exist or you don't have access.") + email_body_parts.append(f"S3 bucket '{S3_BUCKET_NAME}' does not exist or you don't have access.") + else: logging.error(f"S3 ClientError details: {ce.response['Error']['Code']} - {ce.response['Error']['Message']}") + email_body_parts.append(f"S3 ClientError details: {ce.response['Error']['Code']} - {ce.response['Error']['Message']}") + except Exception as e: logging.error(f"An unexpected error occurred during S3 upload of {s3_object_key}: {e}") logging.info("All S3 uploads attempted.") except Exception as e: logging.critical(f"Failed to initialize S3 client or a critical S3 error occurred: {e}") + email_body_parts.append(f"Failed to initialize S3 client or a critical S3 error occurred: {e}") + # --- Main Execution (unchanged from previous version) --- @@ -176,34 +217,80 @@ if __name__ == "__main__": backup_files_created = [] # Basic validation that essential DB connection variables are loaded - if not all([DB_HOST, DB_USER, DB_PASSWORD, DB_NAME_PROD]): + if not all([DB_HOST, DB_USER, DB_PASSWORD]): logging.critical("Missing essential database connection variables. Please check your .env file.") exit(1) try: os.makedirs(BACKUP_DIR, exist_ok=True) logging.info(f"Ensured backup directory exists: {BACKUP_DIR}") + os.makedirs(LOG_DIR, exist_ok=True) + logging.info(f"Ensured log directory exists: {LOG_DIR}") - databases_to_backup = [ - DB_NAME_PROD, - DB_NAME_STAGE, - DB_NAME_GITA, - DB_NAME_REDMINE, - DB_NAME_MEDIAWIKI - ] + # Get the database list array from the environment variable + DB_LIST_TO_BACKUP = os.getenv('DB_LIST_TO_BACKUP') - for db_name in databases_to_backup: + # Check if the variable exists and is not empty before splitting + if DB_LIST_TO_BACKUP: + # Split the string by the comma delimiter + databases_array_to_backup = [item.strip() for item in DB_LIST_TO_BACKUP.split(',')] + logging.info(f"Backup databases for: {databases_array_to_backup}") + else: + logging.error(f"database list array (DB_LIST_TO_BACKUP) not found or is empty.") + + clean_backup_folder() + email_body_parts.append(f"Starting backup for database: {databases_array_to_backup}") + email_body_parts.append(f"-------------------------------------------------------------") + for db_name in databases_array_to_backup: file_path = start_backup(db_name) if file_path: backup_files_created.append(file_path) + email_body_parts.append(f"") + + email_body_parts.append(f"Starting Git upload process...") + email_body_parts.append(f"-------------------------------------------------------------") - logging.info("Starting Git upload process...") - upload_to_git() + if os.getenv('UPLOAD_TO_GIT', 'false').lower() == 'true': + logging.info("Starting Git upload process...") + upload_to_git() + else: + logging.info("Disabled Git upload process...") + email_body_parts.append(f"Disabled Git upload process...") - logging.info("Starting S3 upload process...") - upload_to_s3(backup_files_created) + email_body_parts.append(f"
Starting S3 upload process... ") + email_body_parts.append(f"-------------------------------------------------------------") + + + if os.getenv('UPLOAD_TO_S3', 'false').lower() == 'true': + logging.info("Starting S3 upload process...") + upload_to_s3(backup_files_created) + else: + logging.info("Disabled S3 upload process...") + email_body_parts.append(f"Disabled S3 upload process...") + + + # Send HTML email to multiple recipients + # Final stage: send the mail + email_body_parts.append(f"


Starting sending mail") + email_body ="
".join(email_body_parts) # for plain text + EMAIL_RECEIVERS = os.getenv('EMAIL_RECEIVERS') + if EMAIL_RECEIVERS: + # Split the string by the comma delimiter + email_receivers_array = [item.strip() for item in EMAIL_RECEIVERS.split(',')] + send_email( + subject=os.getenv("EMAIL_SUBJECT", "Database backup process"), + body=email_body, + to_emails=email_receivers_array, + html=True + ) + logging.info(f"Send Mail to: {email_receivers_array}") + else: + logging.info(f"database list array (DB_LIST_TO_BACKUP) not found or is empty.") + logging.info("--- Database Backup Process Completed Successfully ---") + + exit(0) except Exception as ex: diff --git a/backup/email_utils.py b/backup/email_utils.py new file mode 100644 index 0000000..f2da80d --- /dev/null +++ b/backup/email_utils.py @@ -0,0 +1,36 @@ +import os +import smtplib +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart +from dotenv import load_dotenv + +load_dotenv() + +EMAIL_HOST = os.getenv("EMAIL_HOST", "smtp.gmail.com") +EMAIL_PORT = int(os.getenv("EMAIL_PORT", 587)) +EMAIL_USER = os.getenv("EMAIL_USER") +EMAIL_PASS = os.getenv("EMAIL_PASS") + +def send_email(subject, body, to_emails, html=False): + if isinstance(to_emails, str): + to_emails = [to_emails] + + msg = MIMEMultipart() + msg["From"] = EMAIL_USER + msg["To"] = ", ".join(to_emails) + msg["Subject"] = subject + + if html: + msg.attach(MIMEText(body, "html")) + else: + msg.attach(MIMEText(body, "plain")) + + try: + with smtplib.SMTP(EMAIL_HOST, EMAIL_PORT) as server: + server.starttls() + server.login(EMAIL_USER, EMAIL_PASS) + server.sendmail(EMAIL_USER, to_emails, msg.as_string()) + return True + except Exception as e: + print(f"Error sending email: {e}") + return False \ No newline at end of file