Skip to content

Commit

Permalink
refactor: prepare docker (compose) setup
Browse files Browse the repository at this point in the history
docker compose setup to run complete obr-web. e.g. to use for (iOS) app development.
  • Loading branch information
ohrstrom committed Jul 18, 2024
1 parent 2cc3aa8 commit 58868c0
Show file tree
Hide file tree
Showing 24 changed files with 249 additions and 154 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"javascriptreact"
],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"vetur.validation.template": false,
"editor.formatOnPaste": true,
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,4 @@ run-fe:

.PHONY: run-hypercorn
run-hypercorn:
hypercorn core.asgi:application --bind :${PORT_BE} --access-logfile - --error-logfile - --reload
hypercorn config.asgi:application --bind :${PORT_BE} --access-logfile - --error-logfile - --reload
12 changes: 12 additions & 0 deletions compose/cdn-cookie-validator/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM python:3.12-alpine

ENV PYTHONUNBUFFERED True

RUN pip3 install -U pip gunicorn pytz

WORKDIR /app
COPY compose/cdn-cookie-validator/ ./

EXPOSE 8000

CMD exec gunicorn -w 4 main:app --bind :8000
41 changes: 41 additions & 0 deletions compose/cdn-cookie-validator/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env python3
import re
import pytz
import http.cookies
import datetime

"""
curl --cookie "Cloud-CDN-Cookie=URLPrefix=aHR0cHM6Ly9tZWRpYS5vcGVuYnJvYWRjYXN0LmNoL2VuY29kZWQ=:Expires=1721308780:KeyName=cdn-key:Signature=YZ1MOde2rsKV-sucFvddUBTCDKs=" http://localhost:8008/
"""

TIMEZONE = pytz.timezone("Europe/Zurich")

def app(environ, start_response):
cookie_string = environ.get('HTTP_COOKIE', '')
cookies = http.cookies.SimpleCookie(cookie_string)
cloud_cdn_cookie = cookies.get('Cloud-CDN-Cookie', None)

is_valid = True

status = '200 OK'
response = 'Valid'

if cloud_cdn_cookie:
match = re.search(r'Expires=(\d+)', cloud_cdn_cookie.value)
if match:
now = datetime.datetime.now(pytz.utc).astimezone(TIMEZONE).replace(tzinfo=pytz.utc)
expires_at = datetime.datetime.fromtimestamp(int(match.group(1)), pytz.utc)
# print("now: ", now)
# print("expires: ", expires_at)
print("expires in:", (expires_at - now).total_seconds())
if expires_at < now:
is_valid = False
else:
is_valid = False

status = '200 OK' if is_valid else '403 Forbidden'
response = 'OK' if is_valid else 'Invalid CDN Cookie'

response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return [response.encode()]
File renamed without changes.
10 changes: 5 additions & 5 deletions compose/media-encoder/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ def run(

for src_dir in master_dirs:
dst_dir = encoded_dir / src_dir.stem
if (dst_dir / 'dash').is_dir() and not force:
num_skipped += 1
else:
encode_dir_to_dash(src_dir, dst_dir)
num_encoded += 1
# if (dst_dir / 'dash').is_dir() and not force:
# num_skipped += 1
# else:
# encode_dir_to_dash(src_dir, dst_dir)
# num_encoded += 1

if (dst_dir / 'hls').is_dir() and not force:
num_skipped += 1
Expand Down
39 changes: 32 additions & 7 deletions compose/nginx/default.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ upstream static {
upstream image-resizer {
server ${IMAGE_RESIZER};
}
upstream cdn-cookie-validator {
server ${CDN_COOKIE_VALIDATOR};
}

map $http_cookie $cdn_cookie_expires {
"~*Cloud-CDN-Cookie=.*:Expires=(\d+):" $1;
}

server {
listen 80 default_server;
Expand All @@ -31,13 +38,18 @@ server {
proxy_pass http://image-resizer/;
}

# location /encoded/ {
# autoindex on;
# expires 1h;
# # access_log off;
# add_header Cache-Control "public";
# alias /data/encoded/;
# }

location /validate_cdn_cookie {
internal;
proxy_set_header Content-Length "";
proxy_pass_request_body off;

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Cookie $http_cookie;
proxy_pass http://cdn-cookie-validator/;
}

location /encoded/ {
# Disable cache
Expand All @@ -46,6 +58,8 @@ server {
# CORS setup
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length';
add_header X-Session-Id $cookie_sid;
add_header X-Cloud-CDN-Expires $cdn_cookie_expires;

# allow CORS preflight requests
if ($request_method = 'OPTIONS') {
Expand All @@ -61,6 +75,8 @@ server {
video/mp2t ts;
}

auth_request /validate_cdn_cookie;

alias /data/encoded/;
}

Expand All @@ -83,7 +99,13 @@ server {
}

location / {

proxy_pass http://core/;

# if ($cdn_cookie_expires) {
# rewrite ^ /validate_cdn_cookie last;
# }

proxy_buffering off;
# proxy_set_header X-Vite-Proxied "on";
# proxy_set_header Host $host:3000;
Expand All @@ -92,5 +114,8 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Client-Geo-Location-Region "CH";
proxy_set_header X-Client-Geo-Location-City "Zurich";

add_header X-Session-Id $cookie_sid;
add_header X-Cloud-CDN-Expires $cdn_cookie_expires;
}
}
40 changes: 1 addition & 39 deletions config/asgi.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,7 @@
import os

import django
from django.core.handlers.asgi import (
ASGIHandler,
FileResponse,
RequestAborted,
set_script_prefix,
signals,
sync_to_async,
)
from django.core.asgi import get_asgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.default")


class PatchedASGIHandler(ASGIHandler):
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
return
try:
body_file = await self.read_body(receive)
except RequestAborted:
return
set_script_prefix(self.get_script_prefix(scope))
await sync_to_async(signals.request_started.send, thread_sensitive=True)(
sender=self.__class__,
scope=scope,
)
request, error_response = self.create_request(scope, body_file)
if request is None:
await self.send_response(error_response, send)
return
response = await self.get_response_async(request)
response._handler_class = self.__class__ # NOQA: SLF001
if isinstance(response, FileResponse):
response.block_size = self.chunk_size
await self.send_response(response, send)


def get_asgi_application():
django.setup(set_prefix=False)
return PatchedASGIHandler()


application = get_asgi_application()
24 changes: 18 additions & 6 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,29 @@

env = environ.Env(
DEBUG=(bool, False),
SITE_URL=(str, "https://openbroadcast.ch"),
OBP_SYNC_SKIP_MEDIA=(bool, False),
OBP_SYNC_SKIP_IMAGES=(bool, False),
# JWT
JWT_TOKEN_LIFETIME=(int, 60 * 60 * 24 * 28), # 28 days
JWT_TOKEN_REFRESH_LIFETIME=(int, 60 * 60 * 24 * 120), # 120 days
CDN_POLICY_LIFETIME=(int, 60 * 60 * 24), # 1 day
CDN_POLICY_DOMAIN=(str, "openbroadcast.ch"),
)

DEBUG = env("DEBUG")
SECRET_KEY = env(
"SECRET_KEY",
default="---secret-key---",
)
SITE_URL = env("SITE_URL").strip("/")

ALLOWED_HOSTS = ["*"]
SESSION_COOKIE_NAME = "sid"
SESSION_COOKIE_SAMESITE = None

SITE_URL = ""

INSTALLED_APPS = [
# "admin_interface",
# "colorfield",
"modeltranslation",
"django.contrib.admin",
"django.contrib.auth",
Expand All @@ -43,7 +47,6 @@
"django.contrib.staticfiles",
"django.contrib.postgres",
"storages",
# "taggit",
"rest_framework",
"rest_framework.authtoken",
"rest_framework_simplejwt",
Expand Down Expand Up @@ -454,8 +457,10 @@
SIMPLE_JWT = {
"USER_ID_FIELD": "uid",
"USER_ID_CLAIM": "user_uid",
"SLIDING_TOKEN_LIFETIME": timedelta(days=28),
"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=120),
# "SLIDING_TOKEN_LIFETIME": timedelta(days=28),
# "SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=120),
"SLIDING_TOKEN_LIFETIME": timedelta(seconds=env("JWT_TOKEN_LIFETIME")),
"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(seconds=env("JWT_TOKEN_REFRESH_LIFETIME")),
# "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.SlidingToken",),
"AUTH_TOKEN_CLASSES": ("account.jwt_token.tokens.SlidingToken",),
"SLIDING_TOKEN_OBTAIN_SERIALIZER": "account.jwt_token.serializers.TokenObtainSlidingSerializer",
Expand Down Expand Up @@ -534,6 +539,13 @@
)


##################################################################
# CDN
##################################################################
CDN_POLICY_LIFETIME = env.int("CDN_POLICY_LIFETIME")
CDN_POLICY_DOMAIN = env("CDN_POLICY_DOMAIN")


##################################################################
# context
##################################################################
Expand Down
12 changes: 7 additions & 5 deletions config/settings/development.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

from .base import *

SITE_URL = "http://local.obr-next:3000"


##################################################################
# storage & media
Expand Down Expand Up @@ -38,9 +36,13 @@
"http://local.obr-next:3000",
"http://local.obr-next:5000",
"http://local.obr-next:8080",
"http://mba.local:5000",
"http://localhost:5000",
"http://localhost:8080",
]

if SITE_URL not in CSRF_TRUSTED_ORIGINS:
CSRF_TRUSTED_ORIGINS.append(SITE_URL)

MIDDLEWARE += [
# "querycount.middleware.QueryCountMiddleware",
]
Expand Down Expand Up @@ -91,12 +93,12 @@
"propagate": False,
},
"stats": {
"level": "DEBUG",
"level": "INFO",
"handlers": ["console"],
"propagate": False,
},
"sync": {
"level": "DEBUG",
"level": "INFO",
"handlers": ["console"],
"propagate": False,
},
Expand Down
3 changes: 0 additions & 3 deletions config/settings/live.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,11 @@

from .base import * # NOQA

SITE_URL = "https://openbroadcast.ch"

##################################################################
# make sure to add further setting overrides *after*
# importing .base
##################################################################


SECRET_KEY = env("SECRET_KEY")
ALLOWED_HOSTS = ["*"]
DEBUG = env("DEBUG")
Expand Down
1 change: 0 additions & 1 deletion config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

from spa.views import SPA404View, SPAIndexView

SITE_URL = settings.SITE_URL

admin.autodiscover()
admin.site.site_header = "open broadcast radio"
Expand Down
Loading

0 comments on commit 58868c0

Please sign in to comment.