From 3c1a44c4ee4789eb582cc689be4e24da91be9d43 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Sun, 19 May 2024 12:35:42 +0200 Subject: [PATCH] Improve docs --- hedgedoc-expire.py | 62 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/hedgedoc-expire.py b/hedgedoc-expire.py index 2a1d911..9d8613e 100644 --- a/hedgedoc-expire.py +++ b/hedgedoc-expire.py @@ -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)