This commit is contained in:
parent
7eb261a945
commit
3c1a44c4ee
|
@ -17,10 +17,11 @@ import pgsql
|
|||
|
||||
|
||||
class Config:
|
||||
"""
|
||||
Get config from environment variables
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Get config from environment variables
|
||||
"""
|
||||
self.postgres_hostname = getenv('POSTGRES_HOSTNAME', 'localhost')
|
||||
self.postgres_username = getenv('POSTGRES_USERNAME', 'hedgedoc')
|
||||
self.postgres_password = getenv('POSTGRES_PASSWORD', 'geheim')
|
||||
|
@ -36,6 +37,10 @@ class Config:
|
|||
|
||||
|
||||
class EmailSender:
|
||||
"""
|
||||
Send email message through SMTP
|
||||
"""
|
||||
|
||||
def __init__(self, hostname: str, port: int, username: str, password: str, mail_from: str):
|
||||
self.hostname = hostname
|
||||
self.port = port
|
||||
|
@ -44,6 +49,12 @@ class EmailSender:
|
|||
self.mail_from = mail_from
|
||||
|
||||
def send(self, message: email.message.Message) -> None:
|
||||
"""
|
||||
Using the configured SMTP coordinates, send the message out. The code assumes the submission protocol with
|
||||
StartTLS enabled, and authentication required.
|
||||
:param message: to be sent
|
||||
:return:
|
||||
"""
|
||||
smtp_server = smtplib.SMTP(self.hostname, port=self.port)
|
||||
context = ssl.create_default_context()
|
||||
smtp_server.starttls(context=context)
|
||||
|
@ -51,7 +62,14 @@ class EmailSender:
|
|||
smtp_server.send_message(message)
|
||||
|
||||
|
||||
def email_from_email_or_profile(row):
|
||||
def email_from_email_or_profile(row) -> str:
|
||||
"""
|
||||
Get the email address of the creator from a database row. If the email column is populated, use that, otherwise
|
||||
try to extract it from the login profile. The profile is a JSON object that has an emails array. We're using the
|
||||
first address from there.
|
||||
:param row: database row as a dict with email and profile columns
|
||||
:return: email address
|
||||
"""
|
||||
if row['email'] is not None:
|
||||
return row['email']
|
||||
profile = json.loads(row['profile'])
|
||||
|
@ -59,6 +77,11 @@ def email_from_email_or_profile(row):
|
|||
|
||||
|
||||
def notes_to_be_expired(cutoff: datetime) -> list[any]:
|
||||
"""
|
||||
Get a list of all notes to be expired.
|
||||
:param cutoff: notes that have last beed updated before this date are designated to be expired.
|
||||
:return:
|
||||
"""
|
||||
notes = []
|
||||
with db.prepare('''SELECT
|
||||
"Notes"."alias",
|
||||
|
@ -129,6 +152,12 @@ def revisions_to_be_expired(cutoff: datetime) -> list[any]:
|
|||
|
||||
|
||||
def check_notes_to_be_expired(age: timedelta, config: Config) -> None:
|
||||
"""
|
||||
Print a list of notes that will be expired.
|
||||
:param age: expire notes not updated in this timespan
|
||||
:param config: configuration parameters used in output
|
||||
:return:
|
||||
"""
|
||||
cutoff = datetime.now(timezone.utc) - age
|
||||
print(f'Notes to be deleted older than {cutoff} ({humanize.naturaldelta(age)}):')
|
||||
for note in notes_to_be_expired(cutoff):
|
||||
|
@ -138,6 +167,12 @@ def check_notes_to_be_expired(age: timedelta, config: Config) -> None:
|
|||
|
||||
|
||||
def check_revisions_to_be_expired(age: timedelta, config: Config) -> None:
|
||||
"""
|
||||
Print a list of revisions that will be expired.
|
||||
:param age: expire revisions created before this timespan
|
||||
:param config: configuration parameters used in output
|
||||
:return:
|
||||
"""
|
||||
cutoff = datetime.now(timezone.utc) - age
|
||||
print(f'Revisions to be deleted older than {cutoff} ({humanize.naturaldelta(age)}):')
|
||||
notes = {}
|
||||
|
@ -148,13 +183,21 @@ def check_revisions_to_be_expired(age: timedelta, config: Config) -> None:
|
|||
notes[row['noteId']].append(row)
|
||||
for id, revisions in notes.items():
|
||||
email = email_from_email_or_profile(revisions[0])
|
||||
url = config.url + '/' + (revisions[0]["alias"] if revisions[0]["alias"] is not None else revisions[0]["shortid"])
|
||||
url = config.url + '/' + (
|
||||
revisions[0]["alias"] if revisions[0]["alias"] is not None else revisions[0]["shortid"])
|
||||
print(f' {email} {url}: {revisions[0]["title"]}')
|
||||
for rev in revisions:
|
||||
print(f' {humanize.naturaldelta(rev["age"])}: {rev["revisionId"]}')
|
||||
|
||||
|
||||
def expire_old_notes(age: timedelta, config: Config, mail: EmailSender) -> None:
|
||||
"""
|
||||
Email old notes to their owners, then delete them.
|
||||
:param age: expire notes not updated in this timespan
|
||||
:param config: configuration parameters used in output
|
||||
:param mail: how to send the mail
|
||||
:return:
|
||||
"""
|
||||
cutoff = datetime.now(timezone.utc) - age
|
||||
with db.prepare('DELETE FROM "Notes" WHERE "id" = $1') as delete_statement:
|
||||
for note in notes_to_be_expired(cutoff):
|
||||
|
@ -174,7 +217,7 @@ def expire_old_notes(age: timedelta, config: Config, mail: EmailSender) -> None:
|
|||
The admin team for {config.url}
|
||||
|
||||
'''
|
||||
)))
|
||||
)))
|
||||
md = MIMEBase('text', "markdown")
|
||||
md.add_header('Content-Disposition', f'attachment; filename={note["title"]}')
|
||||
md.set_payload(note["content"])
|
||||
|
@ -186,6 +229,7 @@ def expire_old_notes(age: timedelta, config: Config, mail: EmailSender) -> None:
|
|||
except Exception as e:
|
||||
print(f'Unable to send email to {note["email"]}: {e}', file=sys.stderr)
|
||||
|
||||
|
||||
def expire_old_revisions(age: timedelta) -> None:
|
||||
"""
|
||||
Removes all revision on all notes that have been modified earlier than age.
|
||||
|
@ -214,9 +258,11 @@ if __name__ == '__main__':
|
|||
notes_delta = timedelta(days=args.notes)
|
||||
|
||||
config = Config()
|
||||
mail = EmailSender(config.smtp_hostname, config.smtp_port, config.smtp_username, config.smtp_password, config.smtp_from)
|
||||
mail = EmailSender(config.smtp_hostname, config.smtp_port, config.smtp_username, config.smtp_password,
|
||||
config.smtp_from)
|
||||
|
||||
with pgsql.Connection((config.postgres_hostname, config.postgres_port), config.postgres_username, config.postgres_password) as db:
|
||||
with pgsql.Connection((config.postgres_hostname, config.postgres_port), config.postgres_username,
|
||||
config.postgres_password) as db:
|
||||
if args.check:
|
||||
check_revisions_to_be_expired(revisions_delta, config)
|
||||
check_notes_to_be_expired(notes_delta, config)
|
||||
|
|
Loading…
Reference in a new issue