Skip to content

Commit e5ad0fc

Browse files
Merge pull request #6 from py-package/feature/email
added email backup feature
2 parents 241eb2b + 29d643d commit e5ad0fc

File tree

10 files changed

+114
-16
lines changed

10 files changed

+114
-16
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ dist
1414
.env
1515
*.db
1616
src/masonite_backup.egg-info
17-
storage
17+
storage
18+
*.gz
19+
*.zip

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ Backup solution for Masonite.
2323
- [x] Backup Database
2424
- [x] Backup Files
2525
- [x] Store Backup Locally in the filesystem
26-
- [ ] Send Backup Notifications
27-
- [ ] Email Backup
26+
- [x] Email Backup
2827
- [ ] Store Backup in other Masonite Supported Storage Drivers [s3]
2928

3029

@@ -78,6 +77,20 @@ SOURCE = {
7877
# add more...
7978
],
8079
}
80+
81+
EMAIL_BACKUP = False # Whether or not to email the backup.
82+
EMAIL_BACKUP_TO = "" # The email address to send the backup to.
83+
EMAIL_SUBJECT = "System Backup" # The email subject.
84+
```
85+
> Note: Make sure you have `EMAIL_BACKUP` set to `True` and `EMAIL_BACKUP_TO` set to a valid email address, to send the backup via email. Also don't forget to setup SMTP in `config/mail.py` configuration file or in `.env` file.
86+
87+
```sh
88+
MAIL_DRIVER=smtp
89+
MAIL_FROM=
90+
MAIL_HOST=
91+
MAIL_PORT=
92+
MAIL_USERNAME=
93+
MAIL_PASSWORD=
8194
```
8295

8396
**Backup Database and Files**

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
# Versions should comply with PEP440. For a discussion on single-sourcing
99
# the version across setup.py and the project code, see
1010
# https://packaging.python.org/en/latest/single_source_version.html
11-
version="0.0.2",
11+
version="0.0.3",
1212
packages=[
1313
"backup",
1414
"backup.commands",
15+
"backup.mailables",
1516
"backup.providers",
1617
"backup.config",
1718
],

src/backup/Backup.py

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,18 @@
99
from masonite.utils.location import base_path
1010
import subprocess
1111
import gzip
12+
from masonite.facades import Mail
13+
from .mailables.Backup import Backup as BackupMailable
1214

1315

1416
class Backup:
1517
def __init__(self, application) -> None:
1618
self.app = application
1719
self.backup_config = config("backup")
1820

21+
self.db_file_path = None
22+
self.archive_file_path = None
23+
1924
def accept(self, path):
2025
for pattern in self.backup_config.get("source").get("excludes"):
2126
if pattern in path:
@@ -32,7 +37,7 @@ def database(self):
3237
connection = db_config.get(default)
3338
driver = connection.get("driver")
3439

35-
db_file_path = base_path(
40+
self.db_file_path = base_path(
3641
"{}.gz".format("database-" + str(datetime.timestamp(datetime.now())))
3742
)
3843

@@ -54,7 +59,7 @@ def database(self):
5459
command_str = f"sqlplus -S{connection.get('user')}/{connection.get('password')}@{connection.get('host')}:{connection.get('port')}/{connection.get('database')}"
5560

5661
if command_str:
57-
with gzip.open(db_file_path, "wb") as f:
62+
with gzip.open(self.db_file_path, "wb") as f:
5863
popen = subprocess.Popen(
5964
[command_str],
6065
stdout=subprocess.PIPE,
@@ -65,7 +70,7 @@ def database(self):
6570
f.write(stdout_line.encode("utf-8"))
6671
popen.stdout.close()
6772
popen.wait()
68-
return db_file_path
73+
return self.db_file_path
6974

7075
def files(self):
7176
"""
@@ -82,7 +87,7 @@ def files(self):
8287
if not pathlib.Path(output_dir).exists():
8388
pathlib.Path(output_dir).mkdir(parents=True, exist_ok=True)
8489

85-
path_to_archive = pathlib.Path(output_dir).joinpath(filename)
90+
self.archive_file_path = pathlib.Path(output_dir).joinpath(filename)
8691

8792
with tempfile.TemporaryDirectory() as tmp:
8893
shutil.copytree(
@@ -92,5 +97,36 @@ def files(self):
9297
)
9398

9499
with patch("os.path.isfile", side_effect=self.accept):
95-
make_archive(path_to_archive, "zip", tmp)
96-
return path_to_archive
100+
make_archive(self.archive_file_path, "zip", tmp)
101+
102+
return f"{self.archive_file_path}.zip"
103+
104+
def email(self):
105+
"""
106+
Email the backup.
107+
"""
108+
109+
if not self.backup_config.get("email_backup", False):
110+
return
111+
112+
if self.archive_file_path != None and pathlib.Path(f"{self.archive_file_path}.zip").exists():
113+
Mail.mailable(
114+
BackupMailable().attach(f"System Backup.zip", f"{self.archive_file_path}.zip")
115+
).send()
116+
117+
if self.db_file_path != None and pathlib.Path(self.db_file_path).exists():
118+
ext = self.db_file_path.split(".")[-1]
119+
Mail.mailable(
120+
BackupMailable().attach(f"Database Backup.{ext}", self.db_file_path)
121+
).send()
122+
123+
self.cleanup()
124+
125+
def cleanup(self):
126+
"""
127+
Cleanup the backup files.
128+
"""
129+
if self.archive_file_path != None and pathlib.Path(self.archive_file_path).exists():
130+
pathlib.Path(self.archive_file_path).unlink()
131+
if self.db_file_path != None and pathlib.Path(self.db_file_path).exists():
132+
pathlib.Path(self.db_file_path).unlink()

src/backup/commands/BackupRunCommand.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,32 @@ class BackupRunCommand(Command):
1515
def __init__(self, application):
1616
super().__init__()
1717
self.app = application
18+
self.database_file_path = None
19+
self.assets_file_path = None
1820

1921
def handle(self):
22+
backup = self.app.make("backup")
23+
2024
if not self.validate_options():
2125
return
2226

27+
self.info("============ Backup starting ============")
28+
2329
if self.option("only-db"):
24-
self.app.make("backup").database()
30+
self.database_file_path = backup.database()
2531
elif self.option("only-files"):
26-
self.app.make("backup").files()
32+
self.assets_file_path = backup.files()
2733
else:
2834
self.info("Backuping database...")
29-
database_file_path = self.app.make("backup").database()
35+
self.database_file_path = backup.database()
3036
self.info("Backuping files...")
31-
self.app.make("backup").files()
37+
self.assets_file_path = backup.files()
3238

3339
# delete the database file
34-
os.remove(database_file_path)
40+
os.remove(self.database_file_path)
41+
self.database_file_path = None
3542

43+
backup.email()
3644
self.info("============ Backup complete ============")
3745

3846
def validate_options(self):

src/backup/config/backup.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,6 @@
2424
"__pycache__",
2525
],
2626
}
27+
EMAIL_BACKUP = False # Whether or not to email the backup.
28+
EMAIL_BACKUP_TO = "" # The email address to send the backup to.
29+
EMAIL_SUBJECT = "System Backup" # The email subject.

src/backup/mailables/Backup.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# flake8: noqa F501
2+
from masonite.mail import Mailable
3+
from masonite.configuration import config
4+
from masonite.environment import env
5+
6+
7+
class Backup(Mailable):
8+
def build(self):
9+
backup_config = config("backup")
10+
email = backup_config.get("email_backup_to")
11+
12+
if not email:
13+
raise Exception("No email address found in backup config.")
14+
15+
return (
16+
self.to(email)
17+
.subject(backup_config.get("email_subject"))
18+
.from_(env("MAIL_FROM"))
19+
.html(
20+
"""
21+
<div>
22+
<h2 style="margin-bottom: 24px">Backup Complete</h2>
23+
<p style="margin-bottom: 24px">
24+
Your backup has been completed and is now available for download.<br />Please, find the file attached here within.
25+
</p><br />
26+
<i>Thanks,<br /> <strong>Masonite Backup</strong></i>
27+
</div>
28+
"""
29+
)
30+
)

src/backup/mailables/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# flake8: noqa F401
2+
from .Backup import Backup

tests/integrations/config/backup.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,6 @@
2424
"__pycache__",
2525
],
2626
}
27+
EMAIL_BACKUP = True # Whether or not to email the backup.
28+
EMAIL_BACKUP_TO = "yubara@pypackage.com" # The email address to send the backup to.
29+
EMAIL_SUBJECT = "System Backup" # The email subject.

tests/integrations/templates/welcome.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@
3131
</nav>
3232

3333
</div>
34-
{% endblock %}
34+
{% endblock %}

0 commit comments

Comments
 (0)