Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ AWS_SES_REGION_NAME=region_name
AWS_SES_REGION_ENDPOINT=region_endpoint
DEFAULT_FROM_EMAIL=email

# Discord Webhook URL for error logging. Leave empty for development.
# To set up: https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks
DISCORD_WEBHOOK_URL=webhook-url
Comment thread
jzgom067 marked this conversation as resolved.

# The URL of the main site, used for generating links (no trailing slash)
BASE_URL=https://example.com

Expand All @@ -26,9 +30,6 @@ COOKIE_DOMAIN=.example.com
# The folder where logs will be stored
LOG_DIR=filepath

# Comma-separated list of admin email addresses in case anything goes wrong
ADMIN_EMAILS=email,email

# Secret key for Django data signing, can stay a placeholder for development
SECRET_KEY=django-secret-key

Expand Down
56 changes: 36 additions & 20 deletions backend/api/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from urllib.parse import urlparse

import environ
import requests
from celery.schedules import crontab
from django.core.mail import send_mail
from rest_framework.response import Response
Expand Down Expand Up @@ -155,9 +156,11 @@ class ThrottleScopes:
AWS_SES_REGION_NAME = env("AWS_SES_REGION_NAME")
AWS_SES_REGION_ENDPOINT = env("AWS_SES_REGION_ENDPOINT")
DEFAULT_FROM_EMAIL = env("DEFAULT_FROM_EMAIL")
ADMIN_EMAILS = env.list("ADMIN_EMAILS", default=[])
SEND_EMAILS = env.bool("SEND_EMAILS", default=False)
CRITICAL_EMAIL_INTERVAL_SECONDS = 1800 # 30 minutes

# Discord Webhook URL
# Errors will only be sent if this is set.
DISCORD_WEBHOOK_URL = env("DISCORD_WEBHOOK_URL", default=None)

# Automated tasks
CELERY_BEAT_SCHEDULE = {
Expand Down Expand Up @@ -218,30 +221,43 @@ class ThrottleScopes:
# Custom logger just to add some of my own custom logging functions
# We love DRY!!!
class PlancakeLogger(logging.Logger):
_last_email_time = 0
def execute_webhook(self, msg, color):
if DISCORD_WEBHOOK_URL:
try:
requests.post(
DISCORD_WEBHOOK_URL,
json={
"embeds": [
{
"title": "Error",
"description": msg,
"color": color,
"timestamp": time.strftime(
"%Y-%m-%dT%H:%M:%SZ", time.gmtime()
),
}
]
},
timeout=5,
)
Comment thread
jzgom067 marked this conversation as resolved.
except Exception as e:
self.warning(
"Failed to execute Discord Webhook for error logging: %s\nIf you don't have Webhooks set up, leave the environment variable empty.",
e,
)

def db_error(self, msg, *args, **kwargs):
self.error("Database error: %s", msg, *args, **kwargs)

def error(self, msg, *args, **kwargs):
super().error(msg, *args, **kwargs)
formatted_msg = str(msg) % args if args else str(msg)
self.execute_webhook(formatted_msg, 0xFF0000)

def critical(self, msg, *args, **kwargs):
super().critical(msg, *args, **kwargs)

# Send an email to admins
if SEND_EMAILS:
now = time.time()
if now - self._last_email_time > CRITICAL_EMAIL_INTERVAL_SECONDS:
stack_trace = "".join(traceback.format_stack())
try:
send_mail(
subject=f"Plancake - Critical Error",
message=f"A critical error occurred in the application: {msg}\n\nStack Trace:\n{stack_trace}",
from_email=DEFAULT_FROM_EMAIL,
recipient_list=ADMIN_EMAILS,
fail_silently=False,
)
self._last_email_time = now
except Exception as e:
self.error("Failed to send critical error email: %s", e)
formatted_msg = str(msg) % args if args else str(msg)
self.execute_webhook(formatted_msg, 0xFF00FF)


# Now any logger in the project will have access to this class
Expand Down
4 changes: 4 additions & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ billiard==4.2.1
boto3==1.39.14
botocore==1.39.14
celery==5.5.3
certifi==2026.6.17
charset-normalizer==3.4.9
click==8.2.1
click-didyoumean==0.3.1
click-plugins==1.1.1.2
Expand All @@ -16,6 +18,7 @@ django-environ==0.12.0
django-ses==4.4.0
djangorestframework==3.16.0
gunicorn==23.0.0
idna==3.18
isort==8.0.1
jmespath==1.0.1
kombu==5.5.4
Expand All @@ -25,6 +28,7 @@ psycopg==3.2.7
psycopg2-binary==2.9.10
python-dateutil==2.9.0.post0
redis==6.2.0
requests==2.34.2
s3transfer==0.13.1
six==1.17.0
sqlparse==0.5.3
Expand Down