Skip to content

Commit

Permalink
Setup attendee role and Configure video settings for talks (#285)
Browse files Browse the repository at this point in the history
* change create_world api response

* Update code

* Update code

* Update code

* Fix isort, flake8 in pipeline

* Config attendee role

* Fix black in pipeline

* Update code

* Update code

* Update code

* Configure video settings for talks

* Fix black in pipeline

* Update code

* rework code

* update get utc time function

---------

Co-authored-by: lcduong <[email protected]>
Co-authored-by: Mario Behling <[email protected]>
  • Loading branch information
3 people authored Jan 8, 2025
1 parent 08f252b commit 0dd1db6
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 1 deletion.
143 changes: 143 additions & 0 deletions server/venueless/api/task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import datetime as dt
import logging
import uuid
from http import HTTPStatus

import jwt
import requests
from celery import shared_task
from django.conf import settings

from venueless.core.models.auth import ShortToken
from venueless.core.models.world import World

logger = logging.getLogger(__name__)


def generate_video_token(world, days, number, traits, long=False):
"""
Generate video token
:param world: World object
:param days: A integer representing the number of days the token is valid
:param number: A integer representing the number of tokens to generate
:param traits: A dictionary representing the traits of the token
:param long: A boolean representing if the token is long or short
:return: A list of tokens
"""
jwt_secrets = world.config.get("JWT_secrets", [])
if not jwt_secrets:
logger.error("JWT_secrets is missing or empty in the configuration")
return
jwt_config = jwt_secrets[0]
secret = jwt_config.get("secret")
audience = jwt_config.get("audience")
issuer = jwt_config.get("issuer")
iat = dt.datetime.now(dt.timezone.utc)
exp = iat + dt.timedelta(days=days)
result = []
bulk_create = []
for _ in range(number):
payload = {
"iss": issuer,
"aud": audience,
"exp": exp,
"iat": iat,
"uid": str(uuid.uuid4()),
"traits": traits,
}
token = jwt.encode(payload, secret, algorithm="HS256")
if long:
result.append(token)
else:
st = ShortToken(world=world, long_token=token, expires=exp)
result.append(st.short_token)
bulk_create.append(st)

if not long:
ShortToken.objects.bulk_create(bulk_create)
return result


def generate_talk_token(video_settings, video_tokens, event_slug):
"""
Generate talk token
:param video_settings: A dictionary representing the video settings
:param video_tokens: A list of video tokens
:param event_slug: A string representing the event slug
:return: A token
"""
iat = dt.datetime.now(dt.timezone.utc)
exp = iat + dt.timedelta(days=30)
payload = {
"exp": exp,
"iat": iat,
"video_tokens": video_tokens,
"slug": event_slug,
}
token = jwt.encode(payload, video_settings.get("secret"), algorithm="HS256")
return token


@shared_task(bind=True, max_retries=5, default_retry_delay=60)
def configure_video_settings_for_talks(
self, world_id, days, number, traits, long=False
):
"""
Configure video settings for talks
:param self: instance of the task
:param world_id: A integer representing the world id
:param days: A integer representing the number of days the token is valid
:param number: A integer representing the number of tokens to generate
:param traits: A dictionary representing the traits of the token
:param long: A boolean representing if the token is long or short
"""
try:
if not isinstance(world_id, str) or not world_id.isalnum():
raise ValueError("Invalid world_id format")
world = World.objects.get(id=world_id)
event_slug = world_id
jwt_secrets = world.config.get("JWT_secrets", [])
if not jwt_secrets:
logger.error("JWT_secrets is missing or empty in the configuration")
return
jwt_config = jwt_secrets[0]
video_tokens = generate_video_token(world, days, number, traits, long)
talk_token = generate_talk_token(jwt_config, video_tokens, event_slug)
header = {
"Content-Type": "application/json",
"Authorization": f"Bearer {talk_token}",
}
payload = {
"video_settings": {
"audience": jwt_config.get("audience"),
"issuer": jwt_config.get("issuer"),
"secret": jwt_config.get("secret"),
}
}
requests.post(
"{}/api/configure-video-settings/".format(settings.EVENTYAY_TALK_BASE_PATH),
json=payload,
headers=header,
)
world.config["pretalx"] = {
"event": event_slug,
"domain": "{}".format(settings.EVENTYAY_TALK_BASE_PATH),
"pushed": dt.datetime.now(dt.timezone.utc).isoformat(),
"connected": True,
}
world.save()
except requests.exceptions.ConnectionError as e:
logger.error("Connection error: %s", e)
self.retry(exc=e)
except requests.exceptions.HTTPError as e:
if e.response.status_code in (
HTTPStatus.UNAUTHORIZED.value,
HTTPStatus.FORBIDDEN.value,
HTTPStatus.NOT_FOUND.value,
):
logger.error("Non-retryable error: %s", e)
raise
logger.error("HTTP error: %s", e)
self.retry(exc=e)
except ValueError as e:
logger.error("Error configuring video settings: %s", e)
19 changes: 18 additions & 1 deletion server/venueless/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from venueless.core.services.world import notify_schedule_change, notify_world_change

from ..core.models import Room, World
from .task import configure_video_settings_for_talks
from .utils import get_protocol

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -149,6 +150,18 @@ def post(request, *args, **kwargs) -> JsonResponse:

title = titles.get(locale) or titles.get("en") or title_default

attendee_trait_grants = request.data.get("traits", {}).get("attendee", "")
if not isinstance(attendee_trait_grants, str):
raise ValidationError("Attendee traits must be a string")

trait_grants = {
"admin": ["admin"],
"attendee": (
[attendee_trait_grants] if attendee_trait_grants else ["attendee"]
),
"scheduleuser": ["schedule-update"],
}

# if world already exists, update it, otherwise create a new world
world_id = request.data.get("id")
domain_path = "{}{}/{}".format(
Expand All @@ -165,6 +178,7 @@ def post(request, *args, **kwargs) -> JsonResponse:
world.domain = domain_path
world.locale = request.data.get("locale") or "en"
world.timezone = request.data.get("timezone") or "UTC"
world.trait_grants = trait_grants
world.save()
else:
world = World.objects.create(
Expand All @@ -174,8 +188,11 @@ def post(request, *args, **kwargs) -> JsonResponse:
locale=request.data.get("locale") or "en",
timezone=request.data.get("timezone") or "UTC",
config=config,
trait_grants=trait_grants,
)

configure_video_settings_for_talks.delay(
world_id, days=30, number=1, traits=["schedule-update"], long=True
)
site_url = settings.SITE_URL
protocol = get_protocol(site_url)
world.domain = "{}://{}".format(protocol, domain_path)
Expand Down
3 changes: 3 additions & 0 deletions server/venueless/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,9 @@
MEDIA_URL = os.getenv(
"VENUELESS_MEDIA_URL", config.get("urls", "media", fallback="/media/")
)
EVENTYAY_TALK_BASE_PATH = config.get(
"urls", "eventyay-talk", fallback="https://app-test.eventyay.com/talk"
)

WEBSOCKET_PROTOCOL = os.getenv(
"VENUELESS_WEBSOCKET_PROTOCOL",
Expand Down

0 comments on commit 0dd1db6

Please sign in to comment.