Skip to content

Commit 20b076b

Browse files
committed
basic queue processor and web ui
1 parent 556d2bd commit 20b076b

File tree

8 files changed

+325
-51
lines changed

8 files changed

+325
-51
lines changed

samples/django-celery/app/django_celery/settings.py

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
import os
1414
from pathlib import Path
1515
import dj_database_url
16+
import logging
17+
18+
logger = logging.getLogger(__name__)
1619

1720
# Build paths inside the project like this: BASE_DIR / 'subdir'.
1821
BASE_DIR = Path(__file__).resolve().parent.parent
@@ -22,13 +25,22 @@
2225
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
2326

2427
# SECURITY WARNING: keep the secret key used in production secret!
25-
SECRET_KEY = os.environ.get("SECRET_KEY", "django-insecure-0=b6ui0_&=vc9xxjbla9c9i-6(a5b6g9co2md9@105rdz_kkw9")
28+
SECRET_KEY = os.environ.get("SECRET_KEY")
2629

2730
# SECURITY WARNING: don't run with debug turned on in production!
28-
DEBUG = os.environ.get("DEBUG", "True") == "True"
31+
DEBUG = os.environ.get("DEBUG") == "True"
32+
33+
if DEBUG:
34+
ALLOWED_HOSTS = ['*']
35+
else:
36+
ALLOWED_HOSTS = ['*.prod1a.defang.dev', '*'] # TODO: Update this with your own domain
2937

38+
if not DEBUG:
39+
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
3040

31-
ALLOWED_HOSTS = ['*']
41+
CSRF_TRUSTED_ORIGINS = [
42+
'https://raphaeltm-web--8000.prod1a.defang.dev', # TODO: Update this with your own domain
43+
]
3244

3345

3446
# Application definition
@@ -78,11 +90,13 @@
7890
# Database
7991
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
8092

81-
DATABASE_URL = os.environ.get("DATABASE_URL")
93+
POSTGRES_URL = os.environ.get("POSTGRES_URL")
94+
95+
logger.info(f"POSTGRES_URL: {POSTGRES_URL}")
8296

8397
DATABASES = {
8498
"default": dj_database_url.config(
85-
default=DATABASE_URL,
99+
default=POSTGRES_URL,
86100
conn_max_age=600,
87101
)
88102
}
@@ -125,18 +139,55 @@
125139
STATIC_URL = "static/"
126140
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
127141

128-
# WhiteNoise configuration
129-
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
142+
if DEBUG:
143+
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage"
144+
else:
145+
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
130146

131147
# Default primary key field type
132148
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
133149

134150
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
135151

136152
# Celery Configuration
137-
CELERY_BROKER_URL = os.environ.get("REDIS_URL", "redis://localhost:6379/0")
138-
CELERY_RESULT_BACKEND = os.environ.get("REDIS_URL", "redis://localhost:6379/0")
153+
CELERY_BROKER_URL = os.environ.get("REDIS_URL")
154+
CELERY_RESULT_BACKEND = os.environ.get("REDIS_URL")
139155
CELERY_ACCEPT_CONTENT = ['json']
140156
CELERY_TASK_SERIALIZER = 'json'
141157
CELERY_RESULT_SERIALIZER = 'json'
142158
CELERY_TIMEZONE = TIME_ZONE
159+
160+
if not DEBUG:
161+
# New logging configuration for production
162+
LOGGING = {
163+
'version': 1,
164+
'disable_existing_loggers': False,
165+
'filters': {
166+
'require_debug_false': {
167+
'()': 'django.utils.log.RequireDebugFalse',
168+
},
169+
},
170+
'handlers': {
171+
'console': {
172+
'level': 'INFO',
173+
'class': 'logging.StreamHandler',
174+
},
175+
'mail_admins': {
176+
'level': 'ERROR',
177+
'filters': ['require_debug_false'],
178+
'class': 'django.utils.log.AdminEmailHandler',
179+
},
180+
},
181+
'loggers': {
182+
'django': {
183+
'handlers': ['console', 'mail_admins'],
184+
'level': 'INFO',
185+
'propagate': True,
186+
},
187+
# Catch-all logger for other parts of the application
188+
'': {
189+
'handlers': ['console'],
190+
'level': 'INFO',
191+
},
192+
},
193+
}

samples/django-celery/app/django_celery/urls.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717

1818
from django.contrib import admin
1919
from django.urls import path
20-
from tasks.views import add, status, health
20+
from tasks.views import add, health, home
2121

2222
urlpatterns = [
23+
path("", home),
2324
path("admin/", admin.site.urls),
2425
path("add/", add),
25-
path("status/", status),
2626
path("health/", health),
2727
]
Lines changed: 18 additions & 0 deletions
Loading

samples/django-celery/app/tasks/tasks.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,9 @@ def sample_task(param=None):
1111
A sample task that can be called asynchronously
1212
"""
1313
# artificial delay as if the task is doing something
14-
# random between 10 and 30 sec
14+
# random between 1 and 5 sec
1515
sleep = random.randint(1, 5)
1616

1717
time.sleep(sleep)
1818

19-
logger.info("Potato: ")
20-
logger.info(param['potato'])
21-
2219
return f"Task completed with param: {param}"
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<html>
2+
3+
<head>
4+
<title>Task Overview</title>
5+
{% load static %}
6+
<!-- Add Google Fonts for Inter -->
7+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
8+
<style>
9+
/* Apply Inter font */
10+
body {
11+
font-family: 'Inter', sans-serif;
12+
}
13+
14+
/* Two column layout; responsive stacking */
15+
.container {
16+
display: flex;
17+
flex-wrap: wrap;
18+
}
19+
20+
.left-column,
21+
.right-column {
22+
flex: 1 1 300px;
23+
padding: 1rem;
24+
}
25+
26+
@media (max-width: 600px) {
27+
28+
.left-column,
29+
.right-column {
30+
flex: 1 1 100%;
31+
}
32+
}
33+
34+
/* Updated header to navbar style */
35+
.navbar {
36+
display: flex;
37+
align-items: center;
38+
padding: 1rem;
39+
background-color: #f8f9fa;
40+
border-bottom: 1px solid #e9ecef;
41+
margin-bottom: 1rem;
42+
}
43+
44+
.logo {
45+
max-height: 40px;
46+
margin-right: 2rem;
47+
}
48+
49+
.nav-links a {
50+
color: #1769ff;
51+
text-decoration: none;
52+
margin-right: 1.5rem;
53+
}
54+
55+
.nav-links a:hover {
56+
text-decoration: underline;
57+
}
58+
59+
/* Form styles */
60+
form.task-form {
61+
display: flex;
62+
flex-direction: column;
63+
}
64+
65+
form.task-form input,
66+
form.task-form button {
67+
margin-top: 0.5rem;
68+
padding: 0.5rem;
69+
border: 1px solid #ccc;
70+
border-radius: 4px;
71+
}
72+
73+
form.task-form button {
74+
background-color: #1769ff;
75+
color: #fff;
76+
cursor: pointer;
77+
}
78+
79+
form.task-form button:hover {
80+
background-color: #093e9f #093e9f;
81+
}
82+
83+
a.show-status-button {
84+
background-color: #1769ff;
85+
color: #fff;
86+
padding: 0.5rem;
87+
border-radius: 4px;
88+
text-decoration: none;
89+
}
90+
</style>
91+
</head>
92+
93+
<body>
94+
<div class="navbar">
95+
<img src="{% static 'tasks/images/defang-blue.svg' %}" alt="Logo" class="logo">
96+
<div class="nav-links">
97+
<a href="https://docs.defang.io/docs/getting-started" target="_blank">Get Started Guide</a>
98+
<a href="https://portal.defang.io/projects" target="_blank">View Deployed Apps</a>
99+
</div>
100+
</div>
101+
<div class="container">
102+
<div class="left-column">
103+
{% if added_task %}
104+
<p>{{ added_task }}</p>
105+
{% endif %}
106+
107+
<h2>Add Task</h2>
108+
<form action="/add" method="get" class="task-form">
109+
<label for="key">Key:</label>
110+
<input type="text" name="key" id="key" required value="process-thing">
111+
<br>
112+
<label for="value">Value:</label>
113+
<input type="text" name="value" id="value" required value="thing-id-123">
114+
<br>
115+
<button type="submit">Submit</button>
116+
</form>
117+
</div>
118+
<div class="right-column">
119+
{% if show_status %}
120+
121+
<h2>Active Tasks</h2>
122+
<p>Total: {{ active.total }}</p>
123+
<ul>
124+
{% for worker, count in active.per_worker.items %}
125+
<li>{{ worker }}: {{ count }}</li>
126+
{% endfor %}
127+
</ul>
128+
129+
<h2>Scheduled Tasks</h2>
130+
<p>Total: {{ scheduled.total }}</p>
131+
<ul>
132+
{% for worker, count in scheduled.per_worker.items %}
133+
<li>{{ worker }}: {{ count }}</li>
134+
{% endfor %}
135+
</ul>
136+
137+
<h2>Reserved Tasks</h2>
138+
<p>Total: {{ reserved.total }}</p>
139+
<ul>
140+
{% for worker, count in reserved.per_worker.items %}
141+
<li>{{ worker }}: {{ count }}</li>
142+
{% endfor %}
143+
</ul>
144+
{% else %}
145+
<a href="?status=true" class="show-status-button">Show Queue Status</a>
146+
{% endif %}
147+
</div>
148+
</div>
149+
</body>
150+
151+
</html>

0 commit comments

Comments
 (0)