mirror of
https://github.com/healthchecks/healthchecks.git
synced 2024-11-23 16:07:39 +00:00
602ff2b667
The "senddeletionscheduled" management command creates dummy Flip objects, but does not save them to the database. Some transport classes expect the flip object to have a non-null "created" field. Normally it gets set when saving the flip object to the database, here we need to do that manually.
96 lines
3.5 KiB
Python
96 lines
3.5 KiB
Python
from __future__ import annotations
|
|
|
|
import time
|
|
from datetime import timedelta as td
|
|
from typing import Any
|
|
|
|
from django.conf import settings
|
|
from django.contrib.auth.models import User
|
|
from django.core.management.base import BaseCommand
|
|
from django.db.models import QuerySet
|
|
from django.utils.timezone import now
|
|
|
|
from hc.accounts.models import Profile
|
|
from hc.api.models import Channel, Check, Flip
|
|
from hc.lib import emails
|
|
|
|
|
|
class Command(BaseCommand):
|
|
help = """Send warnings to accounts marked for deletion. """
|
|
|
|
def pause(self) -> None:
|
|
time.sleep(1)
|
|
|
|
def members(self, user: User) -> QuerySet[User]:
|
|
q = User.objects.filter(memberships__project__owner=user)
|
|
q = q.exclude(last_login=None)
|
|
return q.order_by("email")
|
|
|
|
def send_channel_notifications(
|
|
self, profile: Profile, skip_emails: list[str]
|
|
) -> None:
|
|
# Sending deletion notices to configured notification channels is
|
|
# a last ditch effort: only do this if 14 or fewer days are left.
|
|
assert profile.deletion_scheduled_date
|
|
delta = profile.deletion_scheduled_date - now()
|
|
if delta.days > 14:
|
|
return
|
|
|
|
formatted = profile.deletion_scheduled_date.strftime("%B %-d, %Y")
|
|
name = f"{settings.SITE_NAME} Account Deletion on {formatted}"
|
|
desc = (
|
|
f"The {settings.SITE_NAME} account registered to {profile.user.email} "
|
|
f"is scheduled for deletion on {formatted}. To keep the account, "
|
|
f"please contact {settings.SUPPORT_EMAIL} ASAP."
|
|
)
|
|
for channel in Channel.objects.filter(project__owner_id=profile.user_id):
|
|
if channel.kind == "email" and channel.email.value in skip_emails:
|
|
continue
|
|
|
|
dummy = Check(name=name, desc=desc, status="down", project=channel.project)
|
|
dummy.last_ping = now() - td(days=1)
|
|
dummy.n_pings = 1
|
|
|
|
dummy_flip = Flip(owner=dummy)
|
|
dummy_flip.created = now()
|
|
dummy_flip.old_status = "up"
|
|
dummy_flip.new_status = "down"
|
|
|
|
self.stdout.write(f" * Sending notification to {channel.kind}")
|
|
error = channel.notify(dummy_flip, is_test=True)
|
|
if error == "no-op":
|
|
# This channel may be configured to send "up" notifications only.
|
|
dummy.status = "up"
|
|
error = channel.notify(dummy_flip, is_test=True)
|
|
|
|
if error:
|
|
self.stdout.write(f" Error sending notification: {error}")
|
|
|
|
def handle(self, **options: Any) -> str:
|
|
q = Profile.objects.order_by("id")
|
|
q = q.filter(deletion_scheduled_date__gt=now())
|
|
|
|
sent = 0
|
|
for profile in q:
|
|
recipients = [profile.user.email]
|
|
# Include team members in the recipient list too:
|
|
for u in self.members(profile.user):
|
|
if u.email not in recipients:
|
|
recipients.append(u.email)
|
|
|
|
self.stdout.write(f"Sending notice to {recipients}")
|
|
ctx = {
|
|
"owner_email": profile.user.email,
|
|
"num_checks": profile.num_checks_used(),
|
|
"support_email": settings.SUPPORT_EMAIL,
|
|
"deletion_scheduled_date": profile.deletion_scheduled_date,
|
|
}
|
|
emails.deletion_scheduled(recipients, ctx)
|
|
self.send_channel_notifications(profile, skip_emails=recipients)
|
|
sent += 1
|
|
|
|
# Throttle so we don't send too many emails at once:
|
|
self.pause()
|
|
|
|
return f"Done!\nNotices sent: {sent}\n"
|