diff --git a/compose/prod/docker-compose.yml b/compose/prod/docker-compose.yml index 99c133a..093de6e 100644 --- a/compose/prod/docker-compose.yml +++ b/compose/prod/docker-compose.yml @@ -16,6 +16,9 @@ services: - "traefik.http.routers.fyp-web.tls.certresolver=${CERTRESOLVER}" - "traefik.http.services.fyp-web.loadbalancer.server.port=${PORT}" - "com.centurylinklabs.watchtower.enable=true" + volumes: + - ../../static:/app/static + - ../../media:/app/media networks: - proxy diff --git a/config/__pycache__/__init__.cpython-313.pyc b/config/__pycache__/__init__.cpython-313.pyc index ed6cd84..8f87f04 100644 Binary files a/config/__pycache__/__init__.cpython-313.pyc and b/config/__pycache__/__init__.cpython-313.pyc differ diff --git a/config/__pycache__/settings.cpython-313.pyc b/config/__pycache__/settings.cpython-313.pyc index 53df004..f58be6d 100644 Binary files a/config/__pycache__/settings.cpython-313.pyc and b/config/__pycache__/settings.cpython-313.pyc differ diff --git a/config/settings.py b/config/settings.py index 5ca0315..105196e 100644 --- a/config/settings.py +++ b/config/settings.py @@ -1,26 +1,25 @@ +from dotenv import load_dotenv from pathlib import Path import os -# Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent +BASE_DIR = Path(__file__).resolve().parent.parent - -from dotenv import load_dotenv load_dotenv(dotenv_path = BASE_DIR / '.env') +BUILD_DIR = os.getenv('DJANGO_BUILD_DIR', BASE_DIR / 'build') -SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 'django-insecure-pf#9a$@vq1o91n#mxwba_dm-+v9&*u4f3$#bts%zanu-$0*whk') +SECRET_KEY = os.getenv('DJANGO_SECRET_KEY') +DEBUG = str(os.getenv('DJANGO_DEBUG')).lower() in ('1', 'true', 'yes', 'on') +ALLOWED_HOSTS = [stripped_host for host in os.getenv('DJANGO_ALLOWED_HOSTS', 'localhost').split(',') if (stripped_host:=host.strip())] -_debug_env = os.getenv('DJANGO_DEBUG', 'True') -DEBUG = str(_debug_env).lower() in ('1', 'true', 'yes', 'on') +PARENT_NAME = Path(__file__).resolve().parent.name -_hosts = os.getenv('DJANGO_ALLOWED_HOSTS', '') -ALLOWED_HOSTS = [h.strip() for h in _hosts.split(',') if h.strip()] - - -# Application definition +STATIC_URL = os.getenv('DJANGO_STATIC_URL', '/static/') +MEDIA_URL = os.getenv('DJANGO_MEDIA_URL', '/media/') +STATIC_ROOT = os.getenv('DJANGO_STATIC_ROOT', BASE_DIR / 'static') +MEDIA_ROOT = os.getenv('DJANGO_MEDIA_ROOT', BASE_DIR / 'media') DJANGO_APPS = [ 'django.contrib.admin', @@ -30,24 +29,23 @@ DJANGO_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', ] - THIRD_PARTY_APPS = [ 'rest_framework', ] - LOCAL_APPS = [ 'apps.users', 'apps.domains', 'apps.agents', ] - INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS AUTH_USER_MODEL = 'users.User' +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', + 'whitenoise.middleware.WhiteNoiseMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -56,7 +54,10 @@ MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] -ROOT_URLCONF = 'config.urls' +ROOT_URLCONF = f'{PARENT_NAME}.urls' +WSGI_APPLICATION = f'{PARENT_NAME}.wsgi.application' +ASGI_APPLICATION = f'{PARENT_NAME}.asgi.application' + TEMPLATES = [ { @@ -73,11 +74,6 @@ TEMPLATES = [ }, ] -WSGI_APPLICATION = 'config.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/5.2/ref/settings/#databases DATABASES = { 'default': { @@ -86,14 +82,11 @@ DATABASES = { } } -# Allow overriding sqlite file via DJANGO_DB_NAME environment variable (relative to project root). -_db_name = os.getenv('DJANGO_DB_NAME') -if _db_name: - DATABASES['default']['NAME'] = BASE_DIR / _db_name - - -# Password validation -# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators +STORAGES = { + "staticfiles": { + "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", + }, +} AUTH_PASSWORD_VALIDATORS = [ { @@ -110,33 +103,12 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] - -# Internationalization -# https://docs.djangoproject.com/en/5.2/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - +LANGUAGE_CODE = 'en-uk' TIME_ZONE = 'UTC' - USE_I18N = True - USE_TZ = True -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/5.2/howto/static-files/ - -STATIC_URL = 'static/' -STATIC_ROOT = 'static/' - -# Default primary key field type -# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field - -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' - -# Django REST framework basic configuration for the POC. Adjust auth/permissions -# as features become defined. For production, tighten permissions and use token -# or JWT authentication as appropriate. REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', diff --git a/config/urls.py b/config/urls.py index b44230e..4073f3c 100644 --- a/config/urls.py +++ b/config/urls.py @@ -1,7 +1,14 @@ from django.contrib import admin -from django.urls import path, include +from django.urls import path, include, re_path +from django.conf import settings +from django.conf.urls.static import static + +from .views import serve_frontend urlpatterns = [ path('admin/', admin.site.urls), path('api/', include('config.api')), + re_path(r'^(?!static/|media/)(?P.*)$', serve_frontend, {'document_root': settings.BUILD_DIR}), + *static(settings.STATIC_URL, document_root=settings.STATIC_ROOT), + *static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT), ] diff --git a/config/views.py b/config/views.py new file mode 100644 index 0000000..a164f33 --- /dev/null +++ b/config/views.py @@ -0,0 +1,14 @@ +from django.utils._os import safe_join +from django.views.static import serve as static_serve +from django.views.decorators.csrf import ensure_csrf_cookie +import posixpath +from pathlib import Path + +@ensure_csrf_cookie +def serve_frontend(request, path, document_root = None): + path = posixpath.normpath(path).lstrip("/") + fullpath = Path(safe_join(document_root, path)) + if fullpath.is_file(): + return static_serve(request, path, document_root) + else: + return static_serve(request, "index.html", document_root) \ No newline at end of file