Skip to content

Commit c83b159

Browse files
fix(video): Resolve anonymous room link 404 error (#1543)
1 parent 74e237d commit c83b159

File tree

4 files changed

+52
-6
lines changed

4 files changed

+52
-6
lines changed

app/eventyay/base/models/event.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ def default_feature_flags():
190190
'submission_public_review': True,
191191
'chat-moderation': True,
192192
'polls': True,
193+
'schedule-control': True,
193194
}
194195

195196

app/eventyay/multidomain/maindomain_urlconf.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
organizer_patterns,
1818
)
1919

20-
from .views import VideoAssetView, VideoSPAView
20+
from .views import VideoAssetView, VideoSPAView, AnonymousInviteRedirectView
2121

2222
logger = logging.getLogger(__name__)
2323

@@ -144,26 +144,42 @@
144144
[
145145
# Video patterns under {organizer}/{event}/video/
146146
# Match static assets with file extensions (js, css, png, etc.)
147+
re_path(r'^video/assets/(?P<path>.*)$', VideoAssetView.as_view(), name='video.assets'),
147148
re_path(
148-
r'^video/(?P<path>[^?]*\.[a-zA-Z0-9._-]+)$', VideoAssetView.as_view(), name='video.assets.file'
149+
r'^video/(?P<path>[^?]*\.[a-zA-Z0-9._-]+)$',
150+
VideoAssetView.as_view(),
151+
name='video.assets.file',
149152
),
150153
# The frontend Video SPA app is not served by Nginx so the Django view needs to
151154
# serve all paths under /video/ to allow client-side routing.
152155
# This catch-all must come after the asset pattern to allow SPA routes like /video/admin/rooms
153156
re_path(r'^video(?:/.*)?$', VideoSPAView.as_view(), name='video.spa'),
154-
path('talk/', EventStartpage.as_view(), name='event.talk'),
157+
re_path(r'^talk/?$', EventStartpage.as_view(), name='event.talk'),
155158
path('', include(('eventyay.agenda.urls', 'agenda'))),
156159
path('', include(('eventyay.cfp.urls', 'cfp'))),
157160
]
158161
),
159162
),
160163
]
161164

165+
# Anonymous room invite short token pattern (6 characters)
166+
# The token uses characters: abcdefghijklmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789
167+
# (excludes visually confusing characters: l, o, I, O, 0)
168+
anonymous_invite_patterns = [
169+
re_path(
170+
r'^(?P<token>[a-km-np-zA-HJ-NP-Z1-9]{6})/?$',
171+
AnonymousInviteRedirectView.as_view(),
172+
name='anonymous.invite.redirect',
173+
),
174+
]
175+
162176
urlpatterns = (
163177
common_patterns
164178
+ storage_patterns
165179
# The plugins patterns must be before presale_patterns_main
166180
# to avoid misdetection of plugin prefixes and organizer/event slugs.
181+
# Anonymous invite short token redirects (before presale to avoid slug conflict)
182+
+ anonymous_invite_patterns
167183
+ plugin_patterns
168184
+ presale_patterns_main
169185
+ unified_event_patterns

app/eventyay/multidomain/views.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@
44
from mimetypes import guess_type
55
from pathlib import Path
66
from typing import cast
7-
7+
from django.shortcuts import redirect
88
from django.conf import settings
99
from django.core.serializers.json import DjangoJSONEncoder
1010
from django.http import Http404, HttpResponse
1111
from django.urls import reverse
1212
from django.urls.exceptions import NoReverseMatch
1313
from django.utils.encoding import force_str
14+
from django.utils.timezone import now
1415
from django.utils.functional import Promise
1516
from django.views.generic import View
1617
from django.views.static import serve as static_serve
1718
from django_scopes import scope
1819
from i18nfield.strings import LazyI18nString
19-
20+
from eventyay.base.models.room import AnonymousInvite
2021
from eventyay.base.models import Event # Added for /video event context
2122

2223
WEBAPP_DIST_DIR = cast(Path, settings.STATIC_ROOT) / 'webapp'
@@ -149,3 +150,31 @@ def get(self, request, path='', *args, **kwargs):
149150
return resp
150151
logger.warning('Video asset not found: %s', path)
151152
raise Http404()
153+
154+
class AnonymousInviteRedirectView(View):
155+
"""
156+
Handle anonymous room invite short tokens (e.g., /eGHhXr/).
157+
Redirects to the video SPA standalone anonymous room view:
158+
/{organizer}/{event}/video/standalone/{room_id}/anonymous#invite={token}
159+
"""
160+
def get(self, request, token, *args, **kwargs):
161+
try:
162+
invite = AnonymousInvite.objects.select_related(
163+
'event', 'event__organizer', 'room'
164+
).get(
165+
short_token=token,
166+
expires__gte=now(),
167+
)
168+
except AnonymousInvite.DoesNotExist:
169+
raise Http404("Invalid or expired anonymous room link")
170+
171+
# Build redirect URL to the video SPA standalone anonymous view
172+
event = invite.event
173+
organizer_slug = event.organizer.slug
174+
event_slug = event.slug
175+
room_id = invite.room_id
176+
177+
# Redirect to /{organizer}/{event}/video/standalone/{room_id}/anonymous#invite={token}
178+
redirect_url = f"/{organizer_slug}/{event_slug}/video/standalone/{room_id}/anonymous#invite={token}"
179+
return redirect(redirect_url)
180+

app/eventyay/webapp/src/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ async function init({ token, inviteToken }) {
5656
// resolved when components (RouterLink) are rendered.
5757
await router.replace(relativePath).catch(() => {})
5858

59-
const route = router.resolve(relativePath).route
59+
const route = router.resolve(relativePath)
6060
const anonymousRoomId = route?.name === 'standalone:anonymous' ? route?.params?.roomId : null
6161

6262
window.vapp = app.mount('#app')

0 commit comments

Comments
 (0)