Skip to content

Commit d03420e

Browse files
committed
Custom admin fixes, more flexible system
1 parent abf5430 commit d03420e

File tree

20 files changed

+592
-20
lines changed

20 files changed

+592
-20
lines changed

Dockerfile.node.local

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ RUN npm install -g pnpm; \
1616

1717
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 1
1818

19-
RUN pip3 install websockets --break-system-packages
19+
RUN pip3 install websockets==14.1 --break-system-packages
2020

2121
COPY . .

backend/custom_admin/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
from custom_admin.views import astro_proxy, DjangoAdminGraphQLView
22
from api.schema import schema
33
from django.contrib.admin.views.decorators import staff_member_required
4+
from django.views.decorators.csrf import csrf_exempt
45

56
from django.urls import path
67

78

89
urlpatterns = [
910
path(
1011
"admin/graphql",
11-
DjangoAdminGraphQLView.as_view(schema=schema),
12+
csrf_exempt(DjangoAdminGraphQLView.as_view(schema=schema)),
1213
name="django-admin-graphql",
1314
),
1415
path("astro/<path:path>", staff_member_required(astro_proxy), name="astro-proxy"),

backend/custom_admin/ws-proxy/ws.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,17 @@ def modify_message(message):
1919
return json.dumps(parsed_message)
2020

2121

22-
async def health_check(path, request_headers):
23-
if request_headers.get("Accept") == "text/x-vite-ping":
22+
async def health_check(connection, request):
23+
if request.headers.get("Accept") == "text/x-vite-ping":
2424
return http.HTTPStatus.OK, [], b"OK\n"
2525

2626

27-
async def handler(websocket, path):
27+
async def handler(websocket):
2828
print("Connected", websocket.remote_address)
2929

3030
async with websockets.connect(
3131
"ws://custom-admin:3002", subprotocols=["vite-hmr"]
3232
) as server_ws:
33-
# Vite HMR starts by sending a type connected message
34-
response = await server_ws.recv()
35-
await websocket.send(response)
3633

3734
async def forward_to_client():
3835
try:
@@ -52,9 +49,15 @@ async def forward_to_server():
5249
await asyncio.gather(forward_to_client(), forward_to_server())
5350

5451

55-
start_server = websockets.serve(
56-
handler, "0.0.0.0", 3003, process_request=health_check, subprotocols=["vite-hmr"]
57-
)
52+
async def main():
53+
async with websockets.serve(
54+
handler,
55+
"0.0.0.0",
56+
3003,
57+
process_request=health_check,
58+
subprotocols=["vite-hmr"],
59+
):
60+
await asyncio.get_running_loop().create_future()
5861

59-
asyncio.get_event_loop().run_until_complete(start_server)
60-
asyncio.get_event_loop().run_forever()
62+
63+
asyncio.run(main())

backend/pycon/celery_utils.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def renew_lock(lock, interval, _stop_event, extend_time):
2525
break
2626

2727

28-
def make_lock_id(func, *args):
28+
def make_lock_id(func, *args, **kwargs):
2929
key = f"celery_lock_{func.__module__}_{func.__name__}"
3030

3131
hash = hashlib.md5()
@@ -34,7 +34,18 @@ def make_lock_id(func, *args):
3434
arg = str(arg)
3535
hash.update(arg.encode("utf-8"))
3636

37-
if args:
37+
for key, value in kwargs.items():
38+
if not isinstance(key, str):
39+
key = str(key)
40+
41+
if not isinstance(value, str):
42+
value = str(value)
43+
44+
key = key.encode("utf-8")
45+
value = value.encode("utf-8")
46+
hash.update(f"{key}={value}".encode("utf-8"))
47+
48+
if args or kwargs:
3849
key = f"{key}_{hash.hexdigest()}"
3950

4051
if xdist_worker := os.environ.get("PYTEST_XDIST_WORKER"):
@@ -50,7 +61,7 @@ def __init__(self, *args, **kwargs):
5061
self.client = redis.Redis.from_url(settings.REDIS_URL)
5162

5263
def acquire_lock(self, *args, **kwargs):
53-
lock_id = make_lock_id(self, *args)
64+
lock_id = make_lock_id(self, *args, **kwargs)
5465
self.lock = self.client.lock(lock_id, timeout=self.timeout, thread_local=False)
5566
return self.lock.acquire(blocking=False)
5667

@@ -64,7 +75,10 @@ def __call__(self, *args, **kwargs):
6475
"Task %s.%s[%s] is already running, skipping",
6576
self.__module__,
6677
self.__name__,
67-
",".join([safe_repr(arg) for arg in args]),
78+
",".join(
79+
[safe_repr(arg) for arg in args]
80+
+ [f"{key}={safe_repr(value)}" for key, value in kwargs.items()]
81+
),
6882
)
6983
return
7084

backend/pycon/settings/base.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
"organizers.apps.OrganizersConfig",
129129
"billing.apps.BillingConfig",
130130
"privacy_policy.apps.PrivacyPolicyConfig",
131+
"visa.apps.VisaConfig",
131132
]
132133

133134
MIDDLEWARE = [
@@ -229,6 +230,12 @@
229230
default="pycon.storages.CustomFileSystemStorage",
230231
)
231232
},
233+
"private": {
234+
"BACKEND": env(
235+
"MEDIA_FILES_PRIVATE_STORAGE_BACKEND",
236+
default="pycon.storages.CustomFileSystemStorage",
237+
)
238+
},
232239
"localstorage": {
233240
"BACKEND": "pycon.storages.CustomFileSystemStorage",
234241
"LOCATION": "/tmp/",

backend/pycon/storages.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ def generate_upload_url(self, file_obj):
6060
)
6161

6262

63+
class PrivateCustomS3Boto3Storage(CustomS3Boto3Storage):
64+
default_acl = "private"
65+
66+
def get_object_parameters(self, name):
67+
params = super().get_object_parameters(name)
68+
params.pop("CacheControl", None)
69+
return params
70+
71+
6372
class CustomInMemoryStorage(InMemoryStorage):
6473
is_remote = False
6574

backend/pyproject.toml

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,67 @@ known-first-party = [
4343

4444
[project]
4545
requires-python = "<3.13,>=3.11"
46-
dependencies = ["django==5.1.4", "django-environ==0.10.0", "django-model-utils==4.5.1", "django-timezone-field==7.0", "certifi==2024.7.4", "chardet==5.2.0", "idna==3.7", "requests==2.32.3", "urllib3==2.2.2", "aniso8601==9.0.1", "whitenoise==6.7.0", "django-ordered-model==3.5", "uWSGI==2.0.26", "django-storages==1.14.4", "sentry-sdk==2.11.0", "boto3==1.35.76", "pycountry<20.0,>=19.8", "countries<1.0.0,>=0.2.0", "django-ses==4.1.1", "hashids<2.0.0,>=1.2.0", "django-autocomplete-light==3.9.4", "django-queryinspect<2.0.0,>=1.1.0", "lxml<5.0.0,>=4.5.0", "unidecode<2.0.0,>=1.1.1", "strawberry-graphql==0.243.1", "Werkzeug<2.0.0,>=1.0.1", "django-import-export<4.0.0,>=3.2.0", "dal-admin-filters==1.1.0", "django-markdownify==0.9.5", "psycopg2==2.9.9", "django-imagekit==5.0.0", "pillow==10.4.0", "redis[hiredis]==5.0.2", "google-api-python-client<3.0.0,>=2.94.0", "google-auth<3.0.0,>=2.22.0", "opencv-python<5.0.0.0,>=4.8.0.74", "argon2-cffi<24.0.0,>=23.1.0", "stripe==10.5.0", "djangorestframework==3.15.2", "l18n<2022.0,>=2021.3", "wagtail==6.2.2", "wagtail-localize==1.10", "celery==5.4.0", "wagtail-headless-preview==0.8.0", "Jinja2>=3.1.3", "icalendar>=5.0.11", "pyclamd>=0.4.0", "magika>=0.5.1", "tzdata>=2024.1", "wagtail-factories==4.2.1", "google-auth-oauthlib>=1.2.1", "PyJWT>=2.8.0", "cryptography>=43.0.0", "openai>=1.52.0", "pydantic>=2.9.2", "gunicorn>=23.0.0", "tinycss2>=1.4.0"]
46+
dependencies = [
47+
"django==5.1.4",
48+
"django-environ==0.10.0",
49+
"django-model-utils==4.5.1",
50+
"django-timezone-field==7.0",
51+
"certifi==2024.7.4",
52+
"chardet==5.2.0",
53+
"idna==3.7",
54+
"requests==2.32.3",
55+
"urllib3==2.2.2",
56+
"aniso8601==9.0.1",
57+
"whitenoise==6.7.0",
58+
"django-ordered-model==3.5",
59+
"uWSGI==2.0.26",
60+
"django-storages==1.14.4",
61+
"sentry-sdk==2.11.0",
62+
"boto3==1.35.76",
63+
"pycountry<20.0,>=19.8",
64+
"countries<1.0.0,>=0.2.0",
65+
"django-ses==4.1.1",
66+
"hashids<2.0.0,>=1.2.0",
67+
"django-autocomplete-light==3.9.4",
68+
"django-queryinspect<2.0.0,>=1.1.0",
69+
"lxml<5.0.0,>=4.5.0",
70+
"unidecode<2.0.0,>=1.1.1",
71+
"strawberry-graphql==0.243.1",
72+
"Werkzeug<2.0.0,>=1.0.1",
73+
"django-import-export<4.0.0,>=3.2.0",
74+
"dal-admin-filters==1.1.0",
75+
"django-markdownify==0.9.5",
76+
"psycopg2==2.9.9",
77+
"django-imagekit==5.0.0",
78+
"pillow==10.4.0",
79+
"redis[hiredis]==5.0.2",
80+
"google-api-python-client<3.0.0,>=2.94.0",
81+
"google-auth<3.0.0,>=2.22.0",
82+
"opencv-python<5.0.0.0,>=4.8.0.74",
83+
"argon2-cffi<24.0.0,>=23.1.0",
84+
"stripe==10.5.0",
85+
"djangorestframework==3.15.2",
86+
"l18n<2022.0,>=2021.3",
87+
"wagtail==6.2.2",
88+
"wagtail-localize==1.10",
89+
"celery==5.4.0",
90+
"wagtail-headless-preview==0.8.0",
91+
"Jinja2>=3.1.3",
92+
"icalendar>=5.0.11",
93+
"pyclamd>=0.4.0",
94+
"magika>=0.5.1",
95+
"tzdata>=2024.1",
96+
"wagtail-factories==4.2.1",
97+
"google-auth-oauthlib>=1.2.1",
98+
"PyJWT>=2.8.0",
99+
"cryptography>=43.0.0",
100+
"openai>=1.52.0",
101+
"pydantic>=2.9.2",
102+
"gunicorn>=23.0.0",
103+
"tinycss2>=1.4.0",
104+
"pypdf>=5.1.0",
105+
"ipython>=8.30.0",
106+
]
47107
name = "backend"
48108
version = "0.1.0"
49109
description = "PyCon Italia backend"

0 commit comments

Comments
 (0)