From 58868c0b0b8ea5a3752d5c53191f68798d5bc770 Mon Sep 17 00:00:00 2001 From: Jonas Ohrstrom Date: Thu, 18 Jul 2024 16:04:09 +0200 Subject: [PATCH] refactor: prepare docker (compose) setup docker compose setup to run complete obr-web. e.g. to use for (iOS) app development. --- .vscode/settings.json | 2 +- Makefile | 2 +- compose/cdn-cookie-validator/Dockerfile | 12 +++ compose/cdn-cookie-validator/main.py | 41 ++++++++++ .../{Dockerfile => Dockerfile.without-ui} | 0 compose/media-encoder/main.py | 10 +-- compose/nginx/default.conf.template | 39 ++++++++-- config/asgi.py | 40 +--------- config/settings/base.py | 24 ++++-- config/settings/development.py | 12 +-- config/settings/live.py | 3 - config/urls.py | 1 - docker-compose.yml | 24 ++++-- docker/Dockerfile | 64 ++++----------- docs/development/docker-compose.md | 77 +++++++++++++++++++ docs/development/index.md | 3 + obr_core/account/api/views.py | 12 +-- obr_core/account/cdn_credentials/policy.py | 4 +- obr_core/account/cdn_credentials/utils.py | 6 +- obr_core/account/models.py | 6 -- obr_core/tests/conftest.py | 5 +- .../unit/account/test_cdn_credentials.py | 2 +- poetry.lock | 12 +-- pyproject.toml | 2 +- 24 files changed, 249 insertions(+), 154 deletions(-) create mode 100644 compose/cdn-cookie-validator/Dockerfile create mode 100755 compose/cdn-cookie-validator/main.py rename compose/core/{Dockerfile => Dockerfile.without-ui} (100%) create mode 100644 docs/development/docker-compose.md diff --git a/.vscode/settings.json b/.vscode/settings.json index e840f369..6423cceb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,7 +5,7 @@ "javascriptreact" ], "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": "explicit" }, "vetur.validation.template": false, "editor.formatOnPaste": true, diff --git a/Makefile b/Makefile index 80c78fc2..ba031da1 100644 --- a/Makefile +++ b/Makefile @@ -124,4 +124,4 @@ run-fe: .PHONY: run-hypercorn run-hypercorn: - hypercorn core.asgi:application --bind :${PORT_BE} --access-logfile - --error-logfile - --reload \ No newline at end of file + hypercorn config.asgi:application --bind :${PORT_BE} --access-logfile - --error-logfile - --reload \ No newline at end of file diff --git a/compose/cdn-cookie-validator/Dockerfile b/compose/cdn-cookie-validator/Dockerfile new file mode 100644 index 00000000..e459baa6 --- /dev/null +++ b/compose/cdn-cookie-validator/Dockerfile @@ -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 diff --git a/compose/cdn-cookie-validator/main.py b/compose/cdn-cookie-validator/main.py new file mode 100755 index 00000000..936503bc --- /dev/null +++ b/compose/cdn-cookie-validator/main.py @@ -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()] diff --git a/compose/core/Dockerfile b/compose/core/Dockerfile.without-ui similarity index 100% rename from compose/core/Dockerfile rename to compose/core/Dockerfile.without-ui diff --git a/compose/media-encoder/main.py b/compose/media-encoder/main.py index d1207bdb..208c07bd 100755 --- a/compose/media-encoder/main.py +++ b/compose/media-encoder/main.py @@ -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 diff --git a/compose/nginx/default.conf.template b/compose/nginx/default.conf.template index 42c154d1..25df22b0 100644 --- a/compose/nginx/default.conf.template +++ b/compose/nginx/default.conf.template @@ -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; @@ -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 @@ -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') { @@ -61,6 +75,8 @@ server { video/mp2t ts; } + auth_request /validate_cdn_cookie; + alias /data/encoded/; } @@ -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; @@ -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; } } \ No newline at end of file diff --git a/config/asgi.py b/config/asgi.py index c57a0c5f..d5bad768 100644 --- a/config/asgi.py +++ b/config/asgi.py @@ -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() diff --git a/config/settings/base.py b/config/settings/base.py index 993884ea..511d901d 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -14,8 +14,14 @@ 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") @@ -23,16 +29,14 @@ "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", @@ -43,7 +47,6 @@ "django.contrib.staticfiles", "django.contrib.postgres", "storages", - # "taggit", "rest_framework", "rest_framework.authtoken", "rest_framework_simplejwt", @@ -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", @@ -534,6 +539,13 @@ ) +################################################################## +# CDN +################################################################## +CDN_POLICY_LIFETIME = env.int("CDN_POLICY_LIFETIME") +CDN_POLICY_DOMAIN = env("CDN_POLICY_DOMAIN") + + ################################################################## # context ################################################################## diff --git a/config/settings/development.py b/config/settings/development.py index b794d7a6..97b7253a 100644 --- a/config/settings/development.py +++ b/config/settings/development.py @@ -2,8 +2,6 @@ from .base import * -SITE_URL = "http://local.obr-next:3000" - ################################################################## # storage & media @@ -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", ] @@ -91,12 +93,12 @@ "propagate": False, }, "stats": { - "level": "DEBUG", + "level": "INFO", "handlers": ["console"], "propagate": False, }, "sync": { - "level": "DEBUG", + "level": "INFO", "handlers": ["console"], "propagate": False, }, diff --git a/config/settings/live.py b/config/settings/live.py index 60cbdf48..6863c1e6 100644 --- a/config/settings/live.py +++ b/config/settings/live.py @@ -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") diff --git a/config/urls.py b/config/urls.py index 95cd2cae..942f992d 100644 --- a/config/urls.py +++ b/config/urls.py @@ -6,7 +6,6 @@ from spa.views import SPA404View, SPAIndexView -SITE_URL = settings.SITE_URL admin.autodiscover() admin.site.site_header = "open broadcast radio" diff --git a/docker-compose.yml b/docker-compose.yml index 52595e1f..1621a5f2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,8 +13,11 @@ services: IMAGE_RESIZER: image-resizer:8000 CORE: core:8000 STATIC: core:8000 + CDN_COOKIE_VALIDATOR: cdn-cookie-validator:8000 networks: - obr + depends_on: + - core extra_hosts: - "host.docker.internal:host-gateway" db: @@ -48,6 +51,8 @@ services: dockerfile: ${PWD}/compose/media-encoder/Dockerfile volumes: - ${PWD}/data:/data + depends_on: + - core core: user: 1000:1000 command: ./manage.py runserver 0.0.0.0:8000 @@ -60,10 +65,13 @@ services: - "8080:8000" volumes: - ${PWD}/data:/app/data + env_file: ".env" environment: DEBUG: "yes" - DATABASE_URL: psql://obr:obr@db:5432/obr DJANGO_SETTINGS_MODULE: config.settings.env + DATABASE_URL: psql://obr:obr@db:5432/obr + # EMAIL_URL: smtp://mailhog:1025 + EMAIL_URL: consolemail:// networks: - obr depends_on: @@ -71,10 +79,11 @@ services: - db sync-schedule: user: 1000:1000 - command: ./sync-schedule + command: sh -c './manage.py sync_schedule -s $(date "+%Y-%m-%d") -i 300' image: obr/core:lateest volumes: - ${PWD}/data:/app/data + env_file: ".env" environment: DATABASE_URL: psql://obr:obr@db:5432/obr OBP_SYNC_TOKEN: "${OBP_SYNC_TOKEN}" @@ -89,6 +98,7 @@ services: user: 1000:1000 command: ./manage.py migrate --noinput image: obr/core:lateest + env_file: ".env" environment: DATABASE_URL: psql://obr:obr@db:5432/obr DJANGO_SETTINGS_MODULE: config.settings.env @@ -96,15 +106,13 @@ services: - obr depends_on: - db - mailhog: + cdn-cookie-validator: + user: 1000:1000 build: context: ${PWD} - dockerfile: ${PWD}/compose/mailhog/Dockerfile - logging: - driver: "none" + dockerfile: ${PWD}/compose/cdn-cookie-validator/Dockerfile ports: - - "1025:1025" # smtp - - "5025:8025" # ui + - "8008:8000" networks: - obr diff --git a/docker/Dockerfile b/docker/Dockerfile index b9ce0bc8..1ad6ee3f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ ####################################################################### -# node based builder to build front end / vue.js +# node based builder to build front-end / vue.js ####################################################################### #FROM node:lts-alpine as node-builder FROM node:16-slim as node-builder @@ -33,14 +33,14 @@ ADD ./obr_ui/ ./obr_ui/ RUN yarn build -# TODO: maybe use webpack to handle assets? +# TODO: maybe use vite to handle these assets? ADD ./obr_ui/assets/ ./build/assets/ ####################################################################### -# python base image +# python core image ####################################################################### -FROM python:3.11-slim as python-base +FROM python:3.11-slim ARG GIT_SHORT_SHA="" WORKDIR /root/ @@ -48,23 +48,8 @@ ENV PYTHONFAULTHANDLER=1 \ PYTHONHASHSEED=random \ PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ - GIT_SHORT_SHA=$GIT_SHORT_SHA - -RUN set -ex \ - && apt-get update \ - && apt-get install -y \ - gettext \ - git - - -####################################################################### -# python builder to install poetry & dependencies -####################################################################### -FROM python-base as python-builder - -WORKDIR /root/ - -ENV PIP_DEFAULT_TIMEOUT=100 \ + GIT_SHORT_SHA=$GIT_SHORT_SHA \ + PIP_DEFAULT_TIMEOUT=100 \ PIP_DISABLE_PIP_VERSION_CHECK=1 \ PIP_NO_CACHE_DIR=1 \ POETRY_VERSION=1.2.2 @@ -72,41 +57,26 @@ ENV PIP_DEFAULT_TIMEOUT=100 \ RUN set -ex \ && apt-get update \ && apt-get install -y \ - build-essential + gettext \ + git \ + build-essential RUN pip3 install -U pip "poetry==$POETRY_VERSION" \ - && python -m venv /venv + && poetry config virtualenvs.create false COPY ["pyproject.toml","poetry.lock", "./"] -# https://stackoverflow.com/questions/53835198/integrating-python-poetry-with-docker -# NOTE: --without-hashes is temporary here to allow install pacvkages from git (only pypi supports hashes) -#RUN poetry export \ -# --without-hashes \ -# -f requirements.txt \ -# | /venv/bin/pip install \ -# -r /dev/stdin - -ADD ./config/ ./config/ -ADD ./obr_core/ ./obr_core/ - -RUN set -ex \ - && poetry build \ - && /venv/bin/pip install dist/*.whl - - -####################################################################### -# final container containing compiled front end & back end -####################################################################### -FROM python-base as final - +RUN poetry install --no-root --without dev RUN useradd -m -d /app app WORKDIR /app/ +# add node static build COPY --from=node-builder /root/build/ ./build -COPY --from=python-builder /venv /venv + +ADD ./config/ ./config/ +ADD ./obr_core/ ./obr_core/ COPY ["manage.py", "./"] ADD ./config/ ./config/ @@ -114,10 +84,6 @@ ADD ./obr_core/ ./obr_core/ ADD ./content/ ./content/ COPY CHANGELOG.md ./content/pages/changelog.md -RUN mkdir -p /app/build - -ENV PATH="/venv/bin:$PATH" - ENV DJANGO_SETTINGS_MODULE=config.settings.build RUN ./manage.py check RUN ./manage.py collectstatic --clear --no-input diff --git a/docs/development/docker-compose.md b/docs/development/docker-compose.md new file mode 100644 index 00000000..c85f033d --- /dev/null +++ b/docs/development/docker-compose.md @@ -0,0 +1,77 @@ +# Run Services (non-dev) + +Run the application as a whole using docker compse. + +This setup is intended to be used for testing & developing 3rd-party +tool - like iOS app, playout, etc. + + +## Preparation + +```shell +mkdir -p data/ data/master/ data/media data/encoded +``` + + +## Initially build images & populate DB + +```shell +export BUILDKIT_PROGRESS=plain + +docker compose build + +docker compose up migrate +docker compose run core ./manage.py createsuperuser +``` + + +## Configuration + +Create an `.env` file with the following variables: + +NOTE: `SITE_URL` has to match your situation when accessing via network. + +```env +DEBUG=True +SITE_URL=http://localhost:5000 +DJANGO_SETTINGS_MODULE=config.settings.env + +OBP_SYNC_ENDPOINT=https://www.openbroadcast.org/api/v2/obr-sync/ +OBP_SYNC_TOKEN= + +# use to override defaults defined in: +# config/settings/base.py +#JWT_TOKEN_LIFETIME=60 +#JWT_TOKEN_REFRESH_LIFETIME=300 +#CDN_POLICY_LIFETIME=120 +#CDN_POLICY_DOMAIN=localhost + +IMAGE_RESIZER_ENDPOINT=/images/ +GOOGLE_APPLICATION_CREDENTIALS=*** +STRIPE_PUBLISHABLE_KEY=*** +STRIPE_SECRET_KEY=*** + +COMPOSE_PROJECT_NAME=obr +``` + + +## Running + +NOTE: in case you have configured `OBP_SYNC_TOKEN` it will take quite +some time to download and encode the media-files ;) + +```shell +docker compose up +``` + + +## Cleanup + +```shell +# reset +docker compose down --rmi local --remove-orphans + +# reset, also delete volumes / db / data +docker compose down --rmi local --volumes --remove-orphans +rm -Rf data/* +``` diff --git a/docs/development/index.md b/docs/development/index.md index 7d5d4906..0d7de9c6 100644 --- a/docs/development/index.md +++ b/docs/development/index.md @@ -61,6 +61,9 @@ For more in-depth changes on the settings module extend `settings.development`. If needed create a `docker-compose.override.yml` file in order to override the defaults. +NTOTE: to run the application as a whole see: [Run Services (non-dev)](./docker-compose.md) + + ## Backing Services To simplify the development there is a `docker-compose` configuration to run the needed auxiliary services: diff --git a/obr_core/account/api/views.py b/obr_core/account/api/views.py index 652c085e..946ebf74 100644 --- a/obr_core/account/api/views.py +++ b/obr_core/account/api/views.py @@ -74,17 +74,7 @@ def get(request): ) if request.user.has_active_subscription: - # TODO: remove this after testing - # this is only used to fix an issue in the iOS app - if ( - hasattr(request.user, "settings") - and request.user.settings.debug_enabled - ): - cdn_policy_ttl = 60 * 2 - response = set_credentials(response, seconds_valid=cdn_policy_ttl) - - else: - response = set_credentials(response) + response = set_credentials(response) else: response = remove_credentials(response) diff --git a/obr_core/account/cdn_credentials/policy.py b/obr_core/account/cdn_credentials/policy.py index 6faa4875..3b9312f4 100644 --- a/obr_core/account/cdn_credentials/policy.py +++ b/obr_core/account/cdn_credentials/policy.py @@ -3,11 +3,13 @@ import hashlib import hmac +from django.conf import settings + COOKIE_NAME = "Cloud-CDN-Cookie" URL_PREFIX = "https://media.openbroadcast.ch/encoded" KEY_NAME = "cdn-key" KEY_BASE64 = "uH2LcPhs5zzOLQsu65rtZw==" -CDN_POLICY_SECONDS_VALID = 60 * 60 * 24 +CDN_POLICY_SECONDS_VALID = settings.CDN_POLICY_LIFETIME def get_signed_cookie(url_prefix, key_name, base64_key, expiration_time): diff --git a/obr_core/account/cdn_credentials/utils.py b/obr_core/account/cdn_credentials/utils.py index 83f87f2b..32a0aec9 100644 --- a/obr_core/account/cdn_credentials/utils.py +++ b/obr_core/account/cdn_credentials/utils.py @@ -1,10 +1,12 @@ import logging +from django.conf import settings + from .policy import get_cdn_policy -CLOUD_CDN_DOMAIN = "openbroadcast.ch" CLOUD_CDN_COOKIE_NAME = "Cloud-CDN-Cookie" -CDN_POLICY_SECONDS_VALID = 60 * 60 * 24 +CLOUD_CDN_DOMAIN = settings.CDN_POLICY_DOMAIN +CDN_POLICY_SECONDS_VALID = settings.CDN_POLICY_LIFETIME logger = logging.getLogger(__name__) diff --git a/obr_core/account/models.py b/obr_core/account/models.py index cdf276fe..a7c618f9 100644 --- a/obr_core/account/models.py +++ b/obr_core/account/models.py @@ -181,12 +181,6 @@ def access_token(self): @property def cdn_policy(self): - # TODO: remove this after testing - # this is only used to fix an issue in the iOS app - if hasattr(self, "settings") and self.settings.debug_enabled: - cdn_policy_ttl = 60 * 2 - return get_cdn_policy(seconds_valid=cdn_policy_ttl) - if self.has_active_subscription: return get_cdn_policy() diff --git a/obr_core/tests/conftest.py b/obr_core/tests/conftest.py index ef571cb2..52640434 100644 --- a/obr_core/tests/conftest.py +++ b/obr_core/tests/conftest.py @@ -1,6 +1,9 @@ import pytest - +from django.conf import settings def pytest_collectreport(report): if report.failed: raise pytest.UsageError("Errors during collection, aborting") + + +settings.CDN_POLICY_DOMAIN="openbroadcast.ch" \ No newline at end of file diff --git a/obr_core/tests/unit/account/test_cdn_credentials.py b/obr_core/tests/unit/account/test_cdn_credentials.py index e1fc2323..cee6cb72 100644 --- a/obr_core/tests/unit/account/test_cdn_credentials.py +++ b/obr_core/tests/unit/account/test_cdn_credentials.py @@ -22,7 +22,7 @@ def test_get_signed_cookie(): @freeze_time("2012-01-01 00:00:00", tz_offset=0) def test_get_cdn_policy(): - policy = get_cdn_policy() + policy = get_cdn_policy(seconds_valid=60 * 60 * 24) assert ( policy diff --git a/poetry.lock b/poetry.lock index 8789b4e1..efa4df75 100644 --- a/poetry.lock +++ b/poetry.lock @@ -792,14 +792,14 @@ Pillow = ">=9.0.0" [[package]] name = "django-countries" -version = "7.5.1" +version = "7.6.1" description = "Provides a country field for Django models." category = "main" optional = false python-versions = "*" files = [ - {file = "django-countries-7.5.1.tar.gz", hash = "sha256:22915d9b9403932b731622619940a54894a3eb0da9a374e7249c8fc453c122d7"}, - {file = "django_countries-7.5.1-py3-none-any.whl", hash = "sha256:2df707aca7a5e677254bed116cf6021a136ebaccd5c2f46860abd6452bb45521"}, + {file = "django-countries-7.6.1.tar.gz", hash = "sha256:c772d4e3e54afcc5f97a018544e96f246c6d9f1db51898ab0c15cd57e19437cf"}, + {file = "django_countries-7.6.1-py3-none-any.whl", hash = "sha256:1ed20842fe0f6194f91faca21076649513846a8787c9eb5aeec3cbe1656b8acc"}, ] [package.dependencies] @@ -807,8 +807,8 @@ asgiref = "*" typing-extensions = "*" [package.extras] -dev = ["black", "django", "djangorestframework", "graphene-django", "pytest", "pytest-django", "tox"] -maintainer = ["django", "transifex-client", "zest.releaser[recommended]"] +dev = ["black", "django", "djangorestframework", "graphene-django", "pytest", "pytest-django", "tox (>=4.0.0,<5.0.0)"] +maintainer = ["django", "zest.releaser[recommended]"] pyuca = ["pyuca"] test = ["djangorestframework", "graphene-django", "pytest", "pytest-cov", "pytest-django"] @@ -4461,4 +4461,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.10, <3.12" -content-hash = "5a1332532476e61585ceca66a6a470e25dfcd0bdf4d63b10b6583bc567ac455e" +content-hash = "0361bf9c0e3e1025d930223ddf41c1087da979de16f53f5f1e12a9aa29c730af" diff --git a/pyproject.toml b/pyproject.toml index 1713a866..498a267f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ Markdown = "^3.3.4" setoptconf = "^0.3.0" whitenoise = "^6.2.0" bleach = "^6.0" -django-countries = "^7.2.1" +django-countries = "~7.6.1" #grpcio = "<1.52.0" django-qsstats-magic = "^1.1.0" pyembed-markdown = "^1.1.0"