From 5d7774cdd8726ce07029d96d7e7c5fcb30ecb012 Mon Sep 17 00:00:00 2001 From: Viswamedha Nalabotu Date: Wed, 19 Nov 2025 13:30:51 +0000 Subject: [PATCH] Added serve view with settings cleaned up --- compose/prod/docker-compose.yml | 3 + config/__pycache__/__init__.cpython-313.pyc | Bin 148 -> 148 bytes config/__pycache__/settings.cpython-313.pyc | Bin 3472 -> 4081 bytes config/settings.py | 74 ++++++-------------- config/urls.py | 9 ++- config/views.py | 14 ++++ 6 files changed, 48 insertions(+), 52 deletions(-) create mode 100644 config/views.py 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 ed6cd84e3331bd38b9fed746e25bd9c6e2464816..8f87f04d195e9a9a9fec5b0d60cf0f30c1d03850 100644 GIT binary patch delta 19 ZcmbQjIE9h>GcPX}0}xc~lby)j2>>ng1jPUV delta 19 ZcmbQjIE9h>GcPX}0}upnlbOif2>>kV1eX8+ diff --git a/config/__pycache__/settings.cpython-313.pyc b/config/__pycache__/settings.cpython-313.pyc index 53df004b63534b134d82042d6e83426ca6ad987d..f58be6d02e69733addb965f3e546281babacfc88 100644 GIT binary patch delta 2127 zcma)6O-vg{6rNqL&3e85=f~if9|s7qW=$a^rES_QwgCr&9ghj3apXGKfK!9%tP>O} z)vGp#DmetYs#^7wBWf!(RivutR+Xxr?6?wSt4QJ48-Yrddg`o=A&H7qOP=@Ud*7S) z=6$2xa6WG{9T<%az`x=zuFP%Yrpa;cD-8gj0|+272`&+cXjgr5lDwornu@QPq!1O? zxItVyOsKg4M2(vfXTen6ytqdH14DG2z{xbE`V9a;?Fi8h;#Ac3VqVpE1E|BiI+Zs} zRx!k!#C;3WwSJHx&f*d$9jdSKYBuWJjI=P2c@x(_rZ^=J5^gfuCcjIx+pZ9z#tTH! z4kkJP$bo1F=3cx&d|>T4>^Oy6n{onh)qMzn2sw2c00U?TqGp9y0m$}$=ha<1;@G14 z6E9+d=A*eZoXE03>k+H*m{m9~0RSGg4UgLX(x{t`$**whF}L9*uEPsp<7EOh!zM3* zTJX++cg}coyrmx}$DQhmPQhm6f-R^O#|utOXp7gI06aBBqV|gC!d^$kYsH=$wqasB z?7*x0GBH5F(>#TbKMgx!7jB`u@`BFrVGrzueFK<2->)ZVlByVilu`#eYed9uFr*XM!Pr!XJz(+Uqwn*_2{E48VY|AsUD! zCIfQ{dx-f1qch|3CmODx&*tWn*`=JAR|wB>PE3deeTkW9Xr1(wC74Qdz zO03SioK9tuUd2|cRn3cr!!boy%{w}?npO#ED`~yD^?LaS%@g?^-A4A(@+LLV(YLmg z!I#cu#5BL0Nu{#swIoXOvno2RqVk7Yr);F?M^7jx;nDIpQC3{H@@wK^hEJ}pW;64+ z+1v_-lCMCY>Hd-Jb)z9ewXcOtHZ7uNJn!}MFBBE?Ih0&X^S<2jDoTrDIyIGC$t5yD?Mpwy{CWi!_iC4a2v z3yW!0Rz-iT7=tycNu&|;NYQ3og<452rxim)hz6!(iD_XfpfK=AO+`NyXg^d>F&yF*Ntk1KWIHouxA_BO!b~WZ z5N2ZG#8@y8^2-Uv>9fqj@nAxTL_$Fyek0*&#Ui|l=wpGYNJxkUprZE+F=14I*rj1C z92Ld`Q2vG)cblRC7)y*raf7qr=wvUcQ0jHi5FVJQet-w$*9}4;f<5)iFW$k}o`xWZ zr=ayIaQp%EyR2oyac_E?b(dIoneE(Wxf085vx6meP@;E%;me)}S4u71W8isaG~b`Q zJ6CM#-I{o~R`R@EHohb2_jJHysp5TQBPTIWXv3$Y_k(wX8!KhDt4w!q7idq3_LOP9 zI2bC?AxZm;>;G=#>yhH{&vsZF5)2+*LN8**5c~f|=ho50%VA zlHnQdHFGypbZ}cY%BB&C*{K12TXSX8c}af+c(#^)bd-3ZY#NmeM<|EUrV@X#Y!W19 zpQkrK1V0Gq4W<}F#h59LMHII`1lwg_zdl)e&e1WoRZ zYY)1L_8y7dY3$gzR&);)?U-VDdAG;Nci2wCEq~9t>b15V+FG=S)LObd4aJfYEg!HK wnp%O?AsKgAEAHQ5mgpV5QPSbmt45dOz^741Dzf+i{Paf4c%ANN2ZPst0cIi?MF0Q* delta 1455 zcmZuw&rcgi6rNqL*X!N&Z-Z@!v55`AHnt%tZIPO)1}_OMp|iQumQJV!>obI zhqA{gIRtCfHplc(HHRJ|MXl7TQl$1Du;Z$btx|D|dUL3%sy%hqgq}LmeDmg;_rCXI zwXXt)gVsMR78c;OX7E?Obu_Ks==UT5p8^OVu?Q9jLv4~;Ow7?)H2t)=8(U6LB06^n3ff=%S;<3@tG=;P43$Z%obwcxh z3@7q9j8n&mIH9|%TLecVn`p}os|_M(m&qiMNf)>t0iXtQp{6+C9y?P5B`mw-1E6rm zAKW09VgPU;h$E^1IgelT=CNaPp&=XDnG zL_}Kj>rvtbIAFx?FmS1dFo-h*;BXHw_AoR-Bq=lm!zchpP!RT^VZ1NGQ53>E3@@P( zI0hq05=N5*j3r1kx=zZZ-u2s6um&RIzw7lx2#vZ?tZyu1gWIeXOH!#;FIV>(R05+; zp53Kw?4&v<*9VQ6(uc+BdM#e5%H`EvRE}@2g{F()_x5%sYtxg}(ALNMD@Cca6_4#r zk4AUjSi2Gqt<>d_MzOjZ4^Kq*H$Kw%^g?R+T2@L6Glgp!l}Ec}^)eNsfJTg~$J7fl zVp4xGPN;v;_9ruRh~T-^T6L|mp4dg3vf4EL<7;?cGyC#-B~jeo-mI(^>y=tny~oTr ztmpbO(sF7^K;9=lb3b8|HA5jk+c3#>?7Es%zcB{L#uasw9a0PI*Zd{Tq_??QSwRMM zfJ>~N^^Pb!D2-k(>x0o)D9q-BytF9X)>x@jUfErjblxD>k!IYi?U(U!))y^p)a1IX z8D(5u9Upa6+14znOeT9vNK4nVFb~z2+)|ibnop-Q!mU(J(D+<7o0kf?%xrdfPBZ6) zr5l-4UVv(mce%~!R6aG6f&$bSsk9n+Y+@!+%NdqGA9B>cD9=_imh&==2T_>>G@87Ouerld9`1E4359*maCa8&*^U^0oAGy;gFq{Ewar{rj9nVA{C(eD zU&|GFbh%?rDD-hS`c>wO%wyNj;i-;&_E~u9!Om09cb=y{)pzLl+|%@Y=4*$i+V)w+ za>|-)q{4OGz}VL{kZ!UC?57N6C2z#YfIU90+*%|F6h?@!;IZuO0HxKV}q&A=UpVvjc3p*NNJJC!!&ebC>g zg6f9tYHSoZyo%+7cPQo)&ZaOYW{X1O+*Xgm9%JCTEa;zV0Vh`e!RGg#?ihe0O~^c+ P{*Mu_;H7>tx-tF>BZPhf 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