From 705451843890c708e47fb759a644f4ec4413bc6e Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Mon, 9 Dec 2024 15:05:06 +0300 Subject: [PATCH 01/33] test again --- src/website/core/settings.py | 16 ++++++++++++---- src/website/entrypoint.sh | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/website/core/settings.py b/src/website/core/settings.py index 149e8af3d7..83d04b779c 100644 --- a/src/website/core/settings.py +++ b/src/website/core/settings.py @@ -53,9 +53,11 @@ def require_env_var(env_var: str) -> str: # Core Settings # --------------------------------------------------------- SECRET_KEY = require_env_var('SECRET_KEY') -DEBUG = get_env_bool('DEBUG', default=False) +# DEBUG = get_env_bool('DEBUG', default=False) +DEBUG = True -ALLOWED_HOSTS = parse_env_list("ALLOWED_HOSTS") +# ALLOWED_HOSTS = parse_env_list("ALLOWED_HOSTS") +ALLOWED_HOSTS = ['*'] # --------------------------------------------------------- # Application Definitions @@ -114,7 +116,8 @@ def require_env_var(env_var: str) -> str: # --------------------------------------------------------- # CORS and CSRF Configuration # --------------------------------------------------------- -CORS_ORIGIN_ALLOW_ALL = False +# CORS_ORIGIN_ALLOW_ALL = False +CORS_ORIGIN_ALLOW_ALL = True CORS_ALLOWED_ORIGINS = parse_env_list("CORS_ALLOWED_ORIGINS") CORS_ORIGIN_REGEX_WHITELIST = parse_env_list("CORS_ORIGIN_REGEX_WHITELIST") CSRF_TRUSTED_ORIGINS = parse_env_list("CSRF_TRUSTED_ORIGINS") @@ -231,10 +234,15 @@ def require_env_var(env_var: str) -> str: # --------------------------------------------------------- # File Upload Limits # --------------------------------------------------------- -MAX_UPLOAD_SIZE = 10 * 1024 * 1024 # 10MB +# Define a constant for maximum upload size +MAX_UPLOAD_SIZE_MB = 10 # Maximum upload size in MB +MAX_UPLOAD_SIZE = MAX_UPLOAD_SIZE_MB * 1024 * 1024 # Convert to bytes + +# Apply the maximum upload size to Django settings DATA_UPLOAD_MAX_MEMORY_SIZE = MAX_UPLOAD_SIZE FILE_UPLOAD_MAX_MEMORY_SIZE = MAX_UPLOAD_SIZE + # --------------------------------------------------------- # Admin and Authentication Settings # --------------------------------------------------------- diff --git a/src/website/entrypoint.sh b/src/website/entrypoint.sh index 783590129b..915d69698b 100644 --- a/src/website/entrypoint.sh +++ b/src/website/entrypoint.sh @@ -14,3 +14,4 @@ python manage.py collectstatic --noinput # Start Gunicorn server to serve the Django application echo "Starting Gunicorn server..." exec gunicorn core.wsgi:application --bind 0.0.0.0:8000 --timeout 600 --log-level info +# exec gunicorn core.wsgi:application --bind 0.0.0.0:8000 --timeout 600 --workers ${GUNICORN_WORKERS:-3} --log-level info From d953b814922a0af392aaa5e0c13416e0657d0d37 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Mon, 9 Dec 2024 15:11:31 +0300 Subject: [PATCH 02/33] test --- src/website/core/settings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/website/core/settings.py b/src/website/core/settings.py index 83d04b779c..36beda507a 100644 --- a/src/website/core/settings.py +++ b/src/website/core/settings.py @@ -118,9 +118,9 @@ def require_env_var(env_var: str) -> str: # --------------------------------------------------------- # CORS_ORIGIN_ALLOW_ALL = False CORS_ORIGIN_ALLOW_ALL = True -CORS_ALLOWED_ORIGINS = parse_env_list("CORS_ALLOWED_ORIGINS") -CORS_ORIGIN_REGEX_WHITELIST = parse_env_list("CORS_ORIGIN_REGEX_WHITELIST") -CSRF_TRUSTED_ORIGINS = parse_env_list("CSRF_TRUSTED_ORIGINS") +# CORS_ALLOWED_ORIGINS = parse_env_list("CORS_ALLOWED_ORIGINS") +# CORS_ORIGIN_REGEX_WHITELIST = parse_env_list("CORS_ORIGIN_REGEX_WHITELIST") +# CSRF_TRUSTED_ORIGINS = parse_env_list("CSRF_TRUSTED_ORIGINS") CSRF_COOKIE_SECURE = not DEBUG SESSION_COOKIE_SECURE = not DEBUG From 720a19939c54ab14e91b4d55b1aca6477d9d2499 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Mon, 9 Dec 2024 15:20:03 +0300 Subject: [PATCH 03/33] test --- src/website/core/settings.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/website/core/settings.py b/src/website/core/settings.py index 36beda507a..6163a6605d 100644 --- a/src/website/core/settings.py +++ b/src/website/core/settings.py @@ -107,7 +107,7 @@ def require_env_var(env_var: str) -> str: 'whitenoise.middleware.WhiteNoiseMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', + # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', @@ -121,9 +121,10 @@ def require_env_var(env_var: str) -> str: # CORS_ALLOWED_ORIGINS = parse_env_list("CORS_ALLOWED_ORIGINS") # CORS_ORIGIN_REGEX_WHITELIST = parse_env_list("CORS_ORIGIN_REGEX_WHITELIST") # CSRF_TRUSTED_ORIGINS = parse_env_list("CSRF_TRUSTED_ORIGINS") +CSRF_TRUSTED_ORIGINS = ['*'] -CSRF_COOKIE_SECURE = not DEBUG -SESSION_COOKIE_SECURE = not DEBUG +# CSRF_COOKIE_SECURE = not DEBUG +# SESSION_COOKIE_SECURE = not DEBUG # --------------------------------------------------------- # URL and WSGI Configuration From c1c87c515d353ae3a112f9975b882fa4f5a36992 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Mon, 9 Dec 2024 15:37:31 +0300 Subject: [PATCH 04/33] updates --- src/website/core/settings.py | 40 ++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/website/core/settings.py b/src/website/core/settings.py index 6163a6605d..33f8e9a91e 100644 --- a/src/website/core/settings.py +++ b/src/website/core/settings.py @@ -1,4 +1,3 @@ - import os import sys from pathlib import Path @@ -102,12 +101,12 @@ def require_env_var(env_var: str) -> str: # Middleware # --------------------------------------------------------- MIDDLEWARE = [ - 'corsheaders.middleware.CorsMiddleware', + 'corsheaders.middleware.CorsMiddleware', # Must be first 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', - # 'django.middleware.csrf.CsrfViewMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', @@ -116,15 +115,29 @@ def require_env_var(env_var: str) -> str: # --------------------------------------------------------- # CORS and CSRF Configuration # --------------------------------------------------------- -# CORS_ORIGIN_ALLOW_ALL = False -CORS_ORIGIN_ALLOW_ALL = True -# CORS_ALLOWED_ORIGINS = parse_env_list("CORS_ALLOWED_ORIGINS") -# CORS_ORIGIN_REGEX_WHITELIST = parse_env_list("CORS_ORIGIN_REGEX_WHITELIST") -# CSRF_TRUSTED_ORIGINS = parse_env_list("CSRF_TRUSTED_ORIGINS") -CSRF_TRUSTED_ORIGINS = ['*'] +if DEBUG: + # Allow all CORS origins during development + CORS_ORIGIN_ALLOW_ALL = True + CORS_ALLOWED_ORIGINS = [] + CORS_ORIGIN_REGEX_WHITELIST = [] + + # Allow all CSRF origins during development + CSRF_TRUSTED_ORIGINS = [] + + # Optionally, you can add more relaxed settings + # For example, allow specific subdomains or ports if needed +else: + # Restrict CORS origins in production + CORS_ORIGIN_ALLOW_ALL = False + CORS_ALLOWED_ORIGINS = parse_env_list("CORS_ALLOWED_ORIGINS") + CORS_ORIGIN_REGEX_WHITELIST = parse_env_list("CORS_ORIGIN_REGEX_WHITELIST") -# CSRF_COOKIE_SECURE = not DEBUG -# SESSION_COOKIE_SECURE = not DEBUG + # Trust specific origins for CSRF protection in production + CSRF_TRUSTED_ORIGINS = parse_env_list("CSRF_TRUSTED_ORIGINS") + +# Security settings +CSRF_COOKIE_SECURE = not DEBUG +SESSION_COOKIE_SECURE = not DEBUG # --------------------------------------------------------- # URL and WSGI Configuration @@ -194,7 +207,6 @@ def require_env_var(env_var: str) -> str: STATICFILES_DIRS = [BASE_DIR / 'static'] STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' - if DEBUG: # Local file storage for development MEDIA_URL = '/media/' @@ -243,7 +255,6 @@ def require_env_var(env_var: str) -> str: DATA_UPLOAD_MAX_MEMORY_SIZE = MAX_UPLOAD_SIZE FILE_UPLOAD_MAX_MEMORY_SIZE = MAX_UPLOAD_SIZE - # --------------------------------------------------------- # Admin and Authentication Settings # --------------------------------------------------------- @@ -263,6 +274,9 @@ def require_env_var(env_var: str) -> str: }, } +# --------------------------------------------------------- +# Quill Editor Configuration +# --------------------------------------------------------- QUILL_CONFIGS = { 'default': { 'theme': 'snow', From 220a54a3fff4301c843b7147558830da28b4b9d6 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Mon, 9 Dec 2024 15:43:06 +0300 Subject: [PATCH 05/33] update --- src/website/core/settings.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/website/core/settings.py b/src/website/core/settings.py index 33f8e9a91e..77f0a4aa11 100644 --- a/src/website/core/settings.py +++ b/src/website/core/settings.py @@ -122,7 +122,9 @@ def require_env_var(env_var: str) -> str: CORS_ORIGIN_REGEX_WHITELIST = [] # Allow all CSRF origins during development - CSRF_TRUSTED_ORIGINS = [] + CSRF_TRUSTED_ORIGINS = [ + "https://website-trigger-3-website-preview-w7kzhvlewq-ew.a.run.app", + ] # Optionally, you can add more relaxed settings # For example, allow specific subdomains or ports if needed From 717df3525e2085fd068e27d51b0cc48aabde20cf Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Mon, 9 Dec 2024 16:10:17 +0300 Subject: [PATCH 06/33] update --- src/website/core/settings.py | 55 ++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/src/website/core/settings.py b/src/website/core/settings.py index 77f0a4aa11..4f853070e7 100644 --- a/src/website/core/settings.py +++ b/src/website/core/settings.py @@ -52,8 +52,7 @@ def require_env_var(env_var: str) -> str: # Core Settings # --------------------------------------------------------- SECRET_KEY = require_env_var('SECRET_KEY') -# DEBUG = get_env_bool('DEBUG', default=False) -DEBUG = True +DEBUG = get_env_bool('DEBUG', default=False) # ALLOWED_HOSTS = parse_env_list("ALLOWED_HOSTS") ALLOWED_HOSTS = ['*'] @@ -131,11 +130,57 @@ def require_env_var(env_var: str) -> str: else: # Restrict CORS origins in production CORS_ORIGIN_ALLOW_ALL = False - CORS_ALLOWED_ORIGINS = parse_env_list("CORS_ALLOWED_ORIGINS") - CORS_ORIGIN_REGEX_WHITELIST = parse_env_list("CORS_ORIGIN_REGEX_WHITELIST") + CORS_ALLOWED_ORIGINS = [ + "https://staging-dot-airqo-frontend.appspot.com", + "https://staging.airqo.net", + "https://airqo.net", + "https://airqo.africa", + "https://airqo.org", + "https://airqo.mak.ac.ug", + "http://127.0.0.1:8000", + "http://localhost:3000", + "https://staging-platform.airqo.net", + "https://staging-analytics.airqo.net", + "https://analytics.airqo.net", + "https://platform.airqo.net", + ] + CORS_ORIGIN_REGEX_WHITELIST = [ + # Matches subdomains under airqo.net, airqo.africa, airqo.org, airqo.io + r"^https://[a-zA-Z0-9_\-]+\.airqo\.(net|africa|org|io)$", + # Matches airqo.africa, airqo.org, and airqo.mak.ac.ug + r"^https://airqo\.(africa|org|mak\.ac\.ug)$", + # Matches staging-dot-airqo-frontend.appspot.com + r"^https://staging-dot-airqo-frontend\.appspot\.com$", + r"^https://staging-platform\.airqo\.net$", # Matches staging-platform.airqo.net + # Matches staging-analytics.airqo.net + r"^https://staging-analytics\.airqo\.net$", + r"^https://analytics\.airqo\.net$", # Matches analytics.airqo.net + r"^https://platform\.airqo\.net$", # Matches platform.airqo.net + # Matches any subpath under https://platform.airqo.net/website/admin + r"^https://platform\.airqo\.net/website/admin.*$", + # Matches any subpath under https://staging-platform.airqo.net/website/admin + r"^https://staging-platform\.airqo\.net/website/admin.*$", + ] # Trust specific origins for CSRF protection in production - CSRF_TRUSTED_ORIGINS = parse_env_list("CSRF_TRUSTED_ORIGINS") + # CSRF_TRUSTED_ORIGINS = parse_env_list("CSRF_TRUSTED_ORIGINS") + CSRF_TRUSTED_ORIGINS = [ + "https://staging-dot-airqo-frontend.appspot.com", + "https://staging.airqo.net", + "https://airqo.net", + "https://airqo.africa", + "https://airqo.org", + "https://airqo.mak.ac.ug", + "http://127.0.0.1:8000", + "http://localhost:3000", + "https://*.cloudshell.dev", + "https://staging-platform.airqo.net", + "https://staging-analytics.airqo.net", + "https://analytics.airqo.net", + "https://platform.airqo.net", + "https://website-trigger-3-website-preview-w7kzhvlewq-ew.a.run.app", + ] + # Security settings CSRF_COOKIE_SECURE = not DEBUG From 12bfe0f955b484e2e113f012864f307933d7d0bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:05:03 +0300 Subject: [PATCH 07/33] Update website staging image tag to stage-8fda0318-1733752993 --- k8s/website/values-stage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/website/values-stage.yaml b/k8s/website/values-stage.yaml index 1287978b00..01bfb5e66b 100644 --- a/k8s/website/values-stage.yaml +++ b/k8s/website/values-stage.yaml @@ -6,7 +6,7 @@ app: replicaCount: 2 image: repository: eu.gcr.io/airqo-250220/airqo-stage-website-api - tag: stage-100bea2d-1733743125 + tag: stage-8fda0318-1733752993 nameOverride: '' fullnameOverride: '' podAnnotations: {} From 3cbf2732fb05e964e6541ec08871aa8273104589 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:05:49 +0300 Subject: [PATCH 08/33] Update AirQo exceedance production image tag to prod-aa6aa4ba-1733753092 --- k8s/exceedance/values-prod-airqo.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/exceedance/values-prod-airqo.yaml b/k8s/exceedance/values-prod-airqo.yaml index ea3f6a3fbf..85ddc8196a 100644 --- a/k8s/exceedance/values-prod-airqo.yaml +++ b/k8s/exceedance/values-prod-airqo.yaml @@ -4,6 +4,6 @@ app: configmap: env-exceedance-production image: repository: eu.gcr.io/airqo-250220/airqo-exceedance-job - tag: prod-d862897c-1733743392 + tag: prod-aa6aa4ba-1733753092 nameOverride: '' fullnameOverride: '' From 9d1ad5aff2df722452f48e454e0d9b1d41c8cae1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:05:59 +0300 Subject: [PATCH 09/33] Update KCCA exceedance production image tag to prod-aa6aa4ba-1733753092 --- k8s/exceedance/values-prod-kcca.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/exceedance/values-prod-kcca.yaml b/k8s/exceedance/values-prod-kcca.yaml index a570a40013..9b6a6d3b1c 100644 --- a/k8s/exceedance/values-prod-kcca.yaml +++ b/k8s/exceedance/values-prod-kcca.yaml @@ -4,6 +4,6 @@ app: configmap: env-exceedance-production image: repository: eu.gcr.io/airqo-250220/kcca-exceedance-job - tag: prod-d862897c-1733743392 + tag: prod-aa6aa4ba-1733753092 nameOverride: '' fullnameOverride: '' From 0b69ed5c6291d9cece237b51898e821ed61b9ec6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:06:23 +0300 Subject: [PATCH 10/33] Update auth service production image tag to prod-aa6aa4ba-1733753092 --- k8s/auth-service/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/auth-service/values-prod.yaml b/k8s/auth-service/values-prod.yaml index f4469ca70e..f7461e5376 100644 --- a/k8s/auth-service/values-prod.yaml +++ b/k8s/auth-service/values-prod.yaml @@ -6,7 +6,7 @@ app: replicaCount: 3 image: repository: eu.gcr.io/airqo-250220/airqo-auth-api - tag: prod-d862897c-1733743392 + tag: prod-aa6aa4ba-1733753092 nameOverride: '' fullnameOverride: '' podAnnotations: {} From 0f8c498d8496bbd1d1378471499c130b23667a64 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:06:46 +0300 Subject: [PATCH 11/33] Update website production image tag to prod-aa6aa4ba-1733753092 --- k8s/website/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/website/values-prod.yaml b/k8s/website/values-prod.yaml index 903c1227a1..31bec7c83a 100644 --- a/k8s/website/values-prod.yaml +++ b/k8s/website/values-prod.yaml @@ -6,7 +6,7 @@ app: replicaCount: 3 image: repository: eu.gcr.io/airqo-250220/airqo-website-api - tag: prod-d862897c-1733743392 + tag: prod-aa6aa4ba-1733753092 nameOverride: '' fullnameOverride: '' podAnnotations: {} From b5f47d8ab3b796d423b9ec6435f47400e013af15 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:07:44 +0300 Subject: [PATCH 12/33] Update workflows prod image tag to prod-aa6aa4ba-1733753092 --- k8s/workflows/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/workflows/values-prod.yaml b/k8s/workflows/values-prod.yaml index 6006b1c0cd..fec033074d 100644 --- a/k8s/workflows/values-prod.yaml +++ b/k8s/workflows/values-prod.yaml @@ -10,7 +10,7 @@ images: initContainer: eu.gcr.io/airqo-250220/airqo-workflows-xcom redisContainer: eu.gcr.io/airqo-250220/airqo-redis containers: eu.gcr.io/airqo-250220/airqo-workflows - tag: prod-d862897c-1733743392 + tag: prod-aa6aa4ba-1733753092 nameOverride: '' fullnameOverride: '' podAnnotations: {} From 9adeefb9a48d7bdc346d588edc305e3287150a4b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:08:20 +0300 Subject: [PATCH 13/33] Update predict production image tag to prod-aa6aa4ba-1733753092 --- k8s/predict/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/predict/values-prod.yaml b/k8s/predict/values-prod.yaml index 504847908e..a554ba7165 100644 --- a/k8s/predict/values-prod.yaml +++ b/k8s/predict/values-prod.yaml @@ -7,7 +7,7 @@ images: predictJob: eu.gcr.io/airqo-250220/airqo-predict-job trainJob: eu.gcr.io/airqo-250220/airqo-train-job predictPlaces: eu.gcr.io/airqo-250220/airqo-predict-places-air-quality - tag: prod-d862897c-1733743392 + tag: prod-aa6aa4ba-1733753092 api: name: airqo-prediction-api label: prediction-api From 415e39315857f88f2febc5b1b78930aacf1c7042 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:14:44 +0300 Subject: [PATCH 14/33] Update spatial production image tag to prod-aa6aa4ba-1733753092 --- k8s/spatial/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/spatial/values-prod.yaml b/k8s/spatial/values-prod.yaml index e5b90c5b37..7e876b40f0 100644 --- a/k8s/spatial/values-prod.yaml +++ b/k8s/spatial/values-prod.yaml @@ -6,7 +6,7 @@ app: replicaCount: 3 image: repository: eu.gcr.io/airqo-250220/airqo-spatial-api - tag: prod-d862897c-1733743392 + tag: prod-aa6aa4ba-1733753092 nameOverride: '' fullnameOverride: '' podAnnotations: {} From 4bfbb940f4704354657776981554d2b253e489a9 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Tue, 10 Dec 2024 12:50:44 +0300 Subject: [PATCH 15/33] edits to the settings.py file --- src/website/core/settings.py | 96 ++++++------------------------------ 1 file changed, 14 insertions(+), 82 deletions(-) diff --git a/src/website/core/settings.py b/src/website/core/settings.py index 4f853070e7..b9cd32a381 100644 --- a/src/website/core/settings.py +++ b/src/website/core/settings.py @@ -52,7 +52,8 @@ def require_env_var(env_var: str) -> str: # Core Settings # --------------------------------------------------------- SECRET_KEY = require_env_var('SECRET_KEY') -DEBUG = get_env_bool('DEBUG', default=False) +# DEBUG = get_env_bool('DEBUG', default=False) +DEBUG = True # ALLOWED_HOSTS = parse_env_list("ALLOWED_HOSTS") ALLOWED_HOSTS = ['*'] @@ -121,65 +122,16 @@ def require_env_var(env_var: str) -> str: CORS_ORIGIN_REGEX_WHITELIST = [] # Allow all CSRF origins during development - CSRF_TRUSTED_ORIGINS = [ - "https://website-trigger-3-website-preview-w7kzhvlewq-ew.a.run.app", - ] + CSRF_TRUSTED_ORIGINS = [] # Optionally, you can add more relaxed settings # For example, allow specific subdomains or ports if needed else: # Restrict CORS origins in production CORS_ORIGIN_ALLOW_ALL = False - CORS_ALLOWED_ORIGINS = [ - "https://staging-dot-airqo-frontend.appspot.com", - "https://staging.airqo.net", - "https://airqo.net", - "https://airqo.africa", - "https://airqo.org", - "https://airqo.mak.ac.ug", - "http://127.0.0.1:8000", - "http://localhost:3000", - "https://staging-platform.airqo.net", - "https://staging-analytics.airqo.net", - "https://analytics.airqo.net", - "https://platform.airqo.net", - ] - CORS_ORIGIN_REGEX_WHITELIST = [ - # Matches subdomains under airqo.net, airqo.africa, airqo.org, airqo.io - r"^https://[a-zA-Z0-9_\-]+\.airqo\.(net|africa|org|io)$", - # Matches airqo.africa, airqo.org, and airqo.mak.ac.ug - r"^https://airqo\.(africa|org|mak\.ac\.ug)$", - # Matches staging-dot-airqo-frontend.appspot.com - r"^https://staging-dot-airqo-frontend\.appspot\.com$", - r"^https://staging-platform\.airqo\.net$", # Matches staging-platform.airqo.net - # Matches staging-analytics.airqo.net - r"^https://staging-analytics\.airqo\.net$", - r"^https://analytics\.airqo\.net$", # Matches analytics.airqo.net - r"^https://platform\.airqo\.net$", # Matches platform.airqo.net - # Matches any subpath under https://platform.airqo.net/website/admin - r"^https://platform\.airqo\.net/website/admin.*$", - # Matches any subpath under https://staging-platform.airqo.net/website/admin - r"^https://staging-platform\.airqo\.net/website/admin.*$", - ] - - # Trust specific origins for CSRF protection in production - # CSRF_TRUSTED_ORIGINS = parse_env_list("CSRF_TRUSTED_ORIGINS") - CSRF_TRUSTED_ORIGINS = [ - "https://staging-dot-airqo-frontend.appspot.com", - "https://staging.airqo.net", - "https://airqo.net", - "https://airqo.africa", - "https://airqo.org", - "https://airqo.mak.ac.ug", - "http://127.0.0.1:8000", - "http://localhost:3000", - "https://*.cloudshell.dev", - "https://staging-platform.airqo.net", - "https://staging-analytics.airqo.net", - "https://analytics.airqo.net", - "https://platform.airqo.net", - "https://website-trigger-3-website-preview-w7kzhvlewq-ew.a.run.app", - ] + CORS_ALLOWED_ORIGINS = parse_env_list("CORS_ALLOWED_ORIGINS") + CORS_ORIGIN_REGEX_WHITELIST = parse_env_list("CORS_ORIGIN_REGEX_WHITELIST") + CSRF_TRUSTED_ORIGINS = parse_env_list("CSRF_TRUSTED_ORIGINS") # Security settings @@ -254,24 +206,15 @@ def require_env_var(env_var: str) -> str: STATICFILES_DIRS = [BASE_DIR / 'static'] STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' -if DEBUG: - # Local file storage for development - MEDIA_URL = '/media/' - DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' - MEDIA_ROOT = BASE_DIR / 'assets' - print("DEBUG=True: Using local file storage for media.") -else: - # Cloudinary setup for production - CLOUDINARY_STORAGE = { - 'CLOUD_NAME': require_env_var('CLOUDINARY_CLOUD_NAME'), - 'API_KEY': require_env_var('CLOUDINARY_API_KEY'), - 'API_SECRET': require_env_var('CLOUDINARY_API_SECRET'), - 'SECURE': True, - 'TIMEOUT': 600, - } +CLOUDINARY_STORAGE = { + 'CLOUD_NAME': require_env_var('CLOUDINARY_CLOUD_NAME'), + 'API_KEY': require_env_var('CLOUDINARY_API_KEY'), + 'API_SECRET': require_env_var('CLOUDINARY_API_SECRET'), + 'SECURE': True, + 'TIMEOUT': 600, +} - DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage' - print("DEBUG=False: Using Cloudinary for media storage.") +DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage' # --------------------------------------------------------- # Default Primary Key Field Type @@ -291,17 +234,6 @@ def require_env_var(env_var: str) -> str: ], } -# --------------------------------------------------------- -# File Upload Limits -# --------------------------------------------------------- -# Define a constant for maximum upload size -MAX_UPLOAD_SIZE_MB = 10 # Maximum upload size in MB -MAX_UPLOAD_SIZE = MAX_UPLOAD_SIZE_MB * 1024 * 1024 # Convert to bytes - -# Apply the maximum upload size to Django settings -DATA_UPLOAD_MAX_MEMORY_SIZE = MAX_UPLOAD_SIZE -FILE_UPLOAD_MAX_MEMORY_SIZE = MAX_UPLOAD_SIZE - # --------------------------------------------------------- # Admin and Authentication Settings # --------------------------------------------------------- From 1afd91730b1ba31e96bc5031be5edcaa5427f3f3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:29:15 +0300 Subject: [PATCH 16/33] Update website staging image tag to stage-aff08031-1733826449 --- k8s/website/values-stage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/website/values-stage.yaml b/k8s/website/values-stage.yaml index 01bfb5e66b..bdf124819b 100644 --- a/k8s/website/values-stage.yaml +++ b/k8s/website/values-stage.yaml @@ -6,7 +6,7 @@ app: replicaCount: 2 image: repository: eu.gcr.io/airqo-250220/airqo-stage-website-api - tag: stage-8fda0318-1733752993 + tag: stage-aff08031-1733826449 nameOverride: '' fullnameOverride: '' podAnnotations: {} From 18f06adc3698d1502ea1d24ce72f510870fbb0eb Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Tue, 10 Dec 2024 14:30:42 +0300 Subject: [PATCH 17/33] updates to settings and events model --- src/website/apps/event/models.py | 94 ++++++++++++++++++++++++--- src/website/apps/event/views.py | 106 ++++++++++++++++++++++++++++--- src/website/core/settings.py | 97 +++++++++++----------------- 3 files changed, 222 insertions(+), 75 deletions(-) diff --git a/src/website/apps/event/models.py b/src/website/apps/event/models.py index a93214cd31..bda5d0fba8 100644 --- a/src/website/apps/event/models.py +++ b/src/website/apps/event/models.py @@ -1,15 +1,12 @@ - +import logging from django.db import models from django.contrib.auth import get_user_model from django_quill.fields import QuillField from utils.models import BaseModel from cloudinary.models import CloudinaryField from cloudinary.uploader import destroy -import logging User = get_user_model() - -# Configure logger logger = logging.getLogger(__name__) @@ -65,7 +62,6 @@ class EventCategory(models.TextChoices): blank=True, ) - # Image fields using CloudinaryField event_image = CloudinaryField( 'image', folder='website/uploads/events/images', @@ -94,8 +90,18 @@ class Meta: def __str__(self): return self.title + def save(self, *args, **kwargs): + is_new = self.pk is None + super().save(*args, **kwargs) + if is_new: + logger.info(f"Created new Event: ID={self.pk}, Title={self.title}") + else: + logger.info(f"Updated Event: ID={self.pk}, Title={self.title}") + def delete(self, *args, **kwargs): - # Delete files from Cloudinary + logger.debug( + f"Attempting to delete Event: ID={self.pk}, Title={self.title}") + # Attempt to delete images from Cloudinary if self.event_image: try: destroy(self.event_image.public_id) @@ -112,7 +118,9 @@ def delete(self, *args, **kwargs): except Exception as e: logger.error( f"Error deleting background_image from Cloudinary: {e}") + super().delete(*args, **kwargs) + logger.info(f"Deleted Event: ID={self.pk}, Title={self.title}") class Inquiry(BaseModel): @@ -134,6 +142,22 @@ class Meta: def __str__(self): return f"Inquiry - {self.inquiry}" + def save(self, *args, **kwargs): + is_new = self.pk is None + super().save(*args, **kwargs) + if is_new: + logger.info( + f"Created new Inquiry: ID={self.pk}, Inquiry={self.inquiry}") + else: + logger.info( + f"Updated Inquiry: ID={self.pk}, Inquiry={self.inquiry}") + + def delete(self, *args, **kwargs): + logger.debug( + f"Attempting to delete Inquiry: ID={self.pk}, Inquiry={self.inquiry}") + super().delete(*args, **kwargs) + logger.info(f"Deleted Inquiry: ID={self.pk}, Inquiry={self.inquiry}") + class Program(BaseModel): date = models.DateField() @@ -153,6 +177,20 @@ class Meta: def __str__(self): return f"Program - {self.date}" + def save(self, *args, **kwargs): + is_new = self.pk is None + super().save(*args, **kwargs) + if is_new: + logger.info(f"Created new Program: ID={self.pk}, Date={self.date}") + else: + logger.info(f"Updated Program: ID={self.pk}, Date={self.date}") + + def delete(self, *args, **kwargs): + logger.debug( + f"Attempting to delete Program: ID={self.pk}, Date={self.date}") + super().delete(*args, **kwargs) + logger.info(f"Deleted Program: ID={self.pk}, Date={self.date}") + class Session(BaseModel): start_time = models.TimeField() @@ -175,6 +213,23 @@ class Meta: def __str__(self): return f"Session - {self.session_title}" + def save(self, *args, **kwargs): + is_new = self.pk is None + super().save(*args, **kwargs) + if is_new: + logger.info( + f"Created new Session: ID={self.pk}, Title={self.session_title}") + else: + logger.info( + f"Updated Session: ID={self.pk}, Title={self.session_title}") + + def delete(self, *args, **kwargs): + logger.debug( + f"Attempting to delete Session: ID={self.pk}, Title={self.session_title}") + super().delete(*args, **kwargs) + logger.info( + f"Deleted Session: ID={self.pk}, Title={self.session_title}") + class PartnerLogo(BaseModel): partner_logo = CloudinaryField( @@ -201,7 +256,18 @@ class Meta: def __str__(self): return f"Partner - {self.name}" + def save(self, *args, **kwargs): + is_new = self.pk is None + super().save(*args, **kwargs) + if is_new: + logger.info( + f"Created new PartnerLogo: ID={self.pk}, Name={self.name}") + else: + logger.info(f"Updated PartnerLogo: ID={self.pk}, Name={self.name}") + def delete(self, *args, **kwargs): + logger.debug( + f"Attempting to delete PartnerLogo: ID={self.pk}, Name={self.name}") if self.partner_logo: try: destroy(self.partner_logo.public_id) @@ -211,6 +277,7 @@ def delete(self, *args, **kwargs): logger.error( f"Error deleting partner_logo from Cloudinary: {e}") super().delete(*args, **kwargs) + logger.info(f"Deleted PartnerLogo: ID={self.pk}, Name={self.name}") class Resource(BaseModel): @@ -239,13 +306,24 @@ class Meta: def __str__(self): return f"Resource - {self.title}" + def save(self, *args, **kwargs): + is_new = self.pk is None + super().save(*args, **kwargs) + if is_new: + logger.info( + f"Created new Resource: ID={self.pk}, Title={self.title}") + else: + logger.info(f"Updated Resource: ID={self.pk}, Title={self.title}") + def delete(self, *args, **kwargs): + logger.debug( + f"Attempting to delete Resource: ID={self.pk}, Title={self.title}") if self.resource: try: destroy(self.resource.public_id) logger.info( f"Deleted resource from Cloudinary: {self.resource.public_id}") except Exception as e: - logger.error( - f"Error deleting resource from Cloudinary: {e}") + logger.error(f"Error deleting resource from Cloudinary: {e}") super().delete(*args, **kwargs) + logger.info(f"Deleted Resource: ID={self.pk}, Title={self.title}") diff --git a/src/website/apps/event/views.py b/src/website/apps/event/views.py index 82cf33e439..569a381706 100644 --- a/src/website/apps/event/views.py +++ b/src/website/apps/event/views.py @@ -1,6 +1,7 @@ -# backend/apps/event/views.py - +import logging from rest_framework import viewsets +from rest_framework.permissions import IsAuthenticatedOrReadOnly + from .models import Event, Inquiry, Program, Session, PartnerLogo, Resource from .serializers import ( EventListSerializer, @@ -11,39 +12,75 @@ PartnerLogoSerializer, ResourceSerializer, ) -from rest_framework.permissions import IsAuthenticatedOrReadOnly + +logger = logging.getLogger(__name__) class EventViewSet(viewsets.ReadOnlyModelViewSet): """ - A viewset that provides the standard actions for Event model. + A viewset that provides the standard actions for the Event model. """ permission_classes = [IsAuthenticatedOrReadOnly] - lookup_field = 'id' # Use 'id' as the lookup field + lookup_field = 'id' def get_serializer_class(self): if self.action == 'retrieve': - return EventDetailSerializer - return EventListSerializer + serializer_class = EventDetailSerializer + else: + serializer_class = EventListSerializer + logger.debug( + f"Selected serializer_class={serializer_class.__name__} for action={self.action}") + return serializer_class def get_queryset(self): + category = self.request.query_params.get('category', None) + logger.debug(f"Fetching Event queryset with category={category}") queryset = Event.objects.prefetch_related( 'inquiries', 'programs__sessions', 'partner_logos', 'resources' ).all() - category = self.request.query_params.get('category', None) + if category in ['airqo', 'cleanair']: queryset = queryset.filter(website_category=category) + logger.info( + f"Filtered Event queryset by category={category}, count={queryset.count()}") + + logger.info(f"Retrieved Event queryset, count={queryset.count()}") return queryset + def list(self, request, *args, **kwargs): + logger.debug("Handling Event list request") + response = super().list(request, *args, **kwargs) + logger.info(f"Listed Events, returned {len(response.data)} records") + return response + + def retrieve(self, request, *args, **kwargs): + logger.debug(f"Handling Event retrieve request, id={kwargs.get('id')}") + response = super().retrieve(request, *args, **kwargs) + logger.info(f"Retrieved Event detail for ID={kwargs.get('id')}") + return response + class InquiryViewSet(viewsets.ReadOnlyModelViewSet): queryset = Inquiry.objects.select_related('event').all() serializer_class = InquirySerializer lookup_field = 'id' + def list(self, request, *args, **kwargs): + logger.debug("Handling Inquiry list request") + response = super().list(request, *args, **kwargs) + logger.info(f"Listed Inquiries, returned {len(response.data)} records") + return response + + def retrieve(self, request, *args, **kwargs): + logger.debug( + f"Handling Inquiry retrieve request, id={kwargs.get('id')}") + response = super().retrieve(request, *args, **kwargs) + logger.info(f"Retrieved Inquiry detail for ID={kwargs.get('id')}") + return response + class ProgramViewSet(viewsets.ReadOnlyModelViewSet): queryset = Program.objects.select_related( @@ -51,20 +88,73 @@ class ProgramViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = ProgramSerializer lookup_field = 'id' + def list(self, request, *args, **kwargs): + logger.debug("Handling Program list request") + response = super().list(request, *args, **kwargs) + logger.info(f"Listed Programs, returned {len(response.data)} records") + return response + + def retrieve(self, request, *args, **kwargs): + logger.debug( + f"Handling Program retrieve request, id={kwargs.get('id')}") + response = super().retrieve(request, *args, **kwargs) + logger.info(f"Retrieved Program detail for ID={kwargs.get('id')}") + return response + class SessionViewSet(viewsets.ReadOnlyModelViewSet): queryset = Session.objects.select_related('program__event').all() serializer_class = SessionSerializer lookup_field = 'id' + def list(self, request, *args, **kwargs): + logger.debug("Handling Session list request") + response = super().list(request, *args, **kwargs) + logger.info(f"Listed Sessions, returned {len(response.data)} records") + return response + + def retrieve(self, request, *args, **kwargs): + logger.debug( + f"Handling Session retrieve request, id={kwargs.get('id')}") + response = super().retrieve(request, *args, **kwargs) + logger.info(f"Retrieved Session detail for ID={kwargs.get('id')}") + return response + class PartnerLogoViewSet(viewsets.ReadOnlyModelViewSet): queryset = PartnerLogo.objects.select_related('event').all() serializer_class = PartnerLogoSerializer lookup_field = 'id' + def list(self, request, *args, **kwargs): + logger.debug("Handling PartnerLogo list request") + response = super().list(request, *args, **kwargs) + logger.info( + f"Listed PartnerLogos, returned {len(response.data)} records") + return response + + def retrieve(self, request, *args, **kwargs): + logger.debug( + f"Handling PartnerLogo retrieve request, id={kwargs.get('id')}") + response = super().retrieve(request, *args, **kwargs) + logger.info(f"Retrieved PartnerLogo detail for ID={kwargs.get('id')}") + return response + class ResourceViewSet(viewsets.ReadOnlyModelViewSet): queryset = Resource.objects.select_related('event').all() serializer_class = ResourceSerializer lookup_field = 'id' + + def list(self, request, *args, **kwargs): + logger.debug("Handling Resource list request") + response = super().list(request, *args, **kwargs) + logger.info(f"Listed Resources, returned {len(response.data)} records") + return response + + def retrieve(self, request, *args, **kwargs): + logger.debug( + f"Handling Resource retrieve request, id={kwargs.get('id')}") + response = super().retrieve(request, *args, **kwargs) + logger.info(f"Retrieved Resource detail for ID={kwargs.get('id')}") + return response diff --git a/src/website/core/settings.py b/src/website/core/settings.py index b9cd32a381..80b50328d9 100644 --- a/src/website/core/settings.py +++ b/src/website/core/settings.py @@ -6,25 +6,24 @@ from dotenv import load_dotenv # --------------------------------------------------------- -# Load Environment Variables from .env +# Load Environment Variables # --------------------------------------------------------- load_dotenv() # --------------------------------------------------------- -# Base Directory and Python Path Adjustments +# Base Directory & Python Path # --------------------------------------------------------- BASE_DIR = Path(__file__).resolve().parent.parent -sys.path.append(str(BASE_DIR / 'apps')) # Allow referencing apps directly +sys.path.append(str(BASE_DIR / 'apps')) # --------------------------------------------------------- -# Helper Functions for Environment Variables +# Environment Variable Helpers # --------------------------------------------------------- def parse_env_list(env_var: str, default: str = "") -> list: """ - Parse a comma-separated string from an environment variable into a list. - Trims whitespace and ignores empty entries. + Convert a comma-separated list in an env var to a Python list. """ raw_value = os.getenv(env_var, default) return [item.strip() for item in raw_value.split(',') if item.strip()] @@ -32,15 +31,14 @@ def parse_env_list(env_var: str, default: str = "") -> list: def get_env_bool(env_var: str, default: bool = False) -> bool: """ - Convert an environment variable to a boolean. - Accepts 'true', '1', 't' (case-insensitive) as True. + Convert an environment variable to boolean. """ - return os.getenv(env_var, str(default)).lower() in ['true', '1', 't'] + return os.getenv(env_var, str(default)).lower() in ['true', '1', 't', 'yes'] def require_env_var(env_var: str) -> str: """ - Ensure an environment variable is set. Raise an error if not set. + Ensure an environment variable is set. Raise ValueError if not. """ value = os.getenv(env_var) if not value: @@ -52,17 +50,15 @@ def require_env_var(env_var: str) -> str: # Core Settings # --------------------------------------------------------- SECRET_KEY = require_env_var('SECRET_KEY') -# DEBUG = get_env_bool('DEBUG', default=False) -DEBUG = True +DEBUG = get_env_bool('DEBUG', default=False) -# ALLOWED_HOSTS = parse_env_list("ALLOWED_HOSTS") -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = parse_env_list('ALLOWED_HOSTS', default='localhost,127.0.0.1') # --------------------------------------------------------- -# Application Definitions +# Applications # --------------------------------------------------------- INSTALLED_APPS = [ - # Django defaults + # Django Defaults 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', @@ -70,7 +66,7 @@ def require_env_var(env_var: str) -> str: 'django.contrib.messages', 'django.contrib.staticfiles', - # Third-party apps + # Third-party Apps 'corsheaders', 'cloudinary', 'cloudinary_storage', @@ -79,9 +75,9 @@ def require_env_var(env_var: str) -> str: 'django_extensions', 'nested_admin', 'drf_yasg', - 'django_quill', # Re-added django_quill + 'django_quill', - # Custom apps + # Custom Apps 'apps.externalteams', 'apps.event', 'apps.cleanair', @@ -101,7 +97,7 @@ def require_env_var(env_var: str) -> str: # Middleware # --------------------------------------------------------- MIDDLEWARE = [ - 'corsheaders.middleware.CorsMiddleware', # Must be first + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', @@ -115,26 +111,15 @@ def require_env_var(env_var: str) -> str: # --------------------------------------------------------- # CORS and CSRF Configuration # --------------------------------------------------------- -if DEBUG: - # Allow all CORS origins during development - CORS_ORIGIN_ALLOW_ALL = True - CORS_ALLOWED_ORIGINS = [] - CORS_ORIGIN_REGEX_WHITELIST = [] +CORS_ALLOWED_ORIGINS = parse_env_list('CORS_ALLOWED_ORIGINS') +CORS_ALLOWED_ORIGIN_REGEXES = parse_env_list('CORS_ORIGIN_REGEX_WHITELIST') +CSRF_TRUSTED_ORIGINS = parse_env_list('CSRF_TRUSTED_ORIGINS') - # Allow all CSRF origins during development - CSRF_TRUSTED_ORIGINS = [] +# If no CORS settings provided, consider defaulting to empty lists +CORS_ALLOWED_ORIGINS = CORS_ALLOWED_ORIGINS if CORS_ALLOWED_ORIGINS else [] +CORS_ALLOWED_ORIGIN_REGEXES = CORS_ALLOWED_ORIGIN_REGEXES if CORS_ALLOWED_ORIGIN_REGEXES else [] - # Optionally, you can add more relaxed settings - # For example, allow specific subdomains or ports if needed -else: - # Restrict CORS origins in production - CORS_ORIGIN_ALLOW_ALL = False - CORS_ALLOWED_ORIGINS = parse_env_list("CORS_ALLOWED_ORIGINS") - CORS_ORIGIN_REGEX_WHITELIST = parse_env_list("CORS_ORIGIN_REGEX_WHITELIST") - CSRF_TRUSTED_ORIGINS = parse_env_list("CSRF_TRUSTED_ORIGINS") - - -# Security settings +# Security cookies CSRF_COOKIE_SECURE = not DEBUG SESSION_COOKIE_SECURE = not DEBUG @@ -167,17 +152,21 @@ def require_env_var(env_var: str) -> str: # Database Configuration # --------------------------------------------------------- DATABASE_URL = os.getenv('DATABASE_URL') - -DATABASES = { - 'default': dj_database_url.parse( - DATABASE_URL, - conn_max_age=600, - ssl_require=True - ) if DATABASE_URL else { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', +if DATABASE_URL: + DATABASES = { + 'default': dj_database_url.parse( + DATABASE_URL, + conn_max_age=600, + ssl_require=True + ) + } +else: + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } } -} # --------------------------------------------------------- # Password Validation @@ -195,7 +184,6 @@ def require_env_var(env_var: str) -> str: LANGUAGE_CODE = os.getenv('LANGUAGE_CODE', 'en-us') TIME_ZONE = os.getenv('TIME_ZONE', 'UTC') USE_I18N = True -USE_L10N = True USE_TZ = True # --------------------------------------------------------- @@ -206,6 +194,7 @@ def require_env_var(env_var: str) -> str: STATICFILES_DIRS = [BASE_DIR / 'static'] STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' +# Cloudinary Configuration CLOUDINARY_STORAGE = { 'CLOUD_NAME': require_env_var('CLOUDINARY_CLOUD_NAME'), 'API_KEY': require_env_var('CLOUDINARY_API_KEY'), @@ -213,7 +202,6 @@ def require_env_var(env_var: str) -> str: 'SECURE': True, 'TIMEOUT': 600, } - DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage' # --------------------------------------------------------- @@ -281,12 +269,3 @@ def require_env_var(env_var: str) -> str: 'scrollingContainer': '#scrolling-container', }, } - -# --------------------------------------------------------- -# Mode-Specific Logging -# --------------------------------------------------------- -if DEBUG: - print(f"Debug mode is: {DEBUG}") - print(f"Media files are stored in: {BASE_DIR / 'assets'}") -else: - print("Production mode is ON") From dcc34528cfe8f907e64e1a33cf1dba0a18bc44b4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:40:16 +0300 Subject: [PATCH 18/33] Update website staging image tag to stage-ac4d3c16-1733830718 --- k8s/website/values-stage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/website/values-stage.yaml b/k8s/website/values-stage.yaml index bdf124819b..61f8603b8d 100644 --- a/k8s/website/values-stage.yaml +++ b/k8s/website/values-stage.yaml @@ -6,7 +6,7 @@ app: replicaCount: 2 image: repository: eu.gcr.io/airqo-250220/airqo-stage-website-api - tag: stage-aff08031-1733826449 + tag: stage-ac4d3c16-1733830718 nameOverride: '' fullnameOverride: '' podAnnotations: {} From 93914f84113b2375f1304637ba399141e5acc997 Mon Sep 17 00:00:00 2001 From: baalmart Date: Tue, 10 Dec 2024 15:08:31 +0300 Subject: [PATCH 19/33] just fixing list cohorts --- src/device-registry/models/Cohort.js | 71 ++++++++++++++++------------ 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/src/device-registry/models/Cohort.js b/src/device-registry/models/Cohort.js index ac5a481261..788811e60a 100644 --- a/src/device-registry/models/Cohort.js +++ b/src/device-registry/models/Cohort.js @@ -160,8 +160,6 @@ cohortSchema.statics.register = async function(args, next) { response.errors = { message: error.message }; } - return response; - logger.error(`🐛🐛 Internal Server Error ${error.message}`); next(new HttpError(response.message, response.status, response.errors)); } @@ -196,12 +194,22 @@ cohortSchema.statics.list = async function( foreignField: "cohorts", as: "devices", }) - .unwind("$devices") - .lookup({ - from: "sites", - localField: "devices.site_id", - foreignField: "_id", - as: "devices.site", + .project({ + _id: 1, + visibility: 1, + cohort_tags: 1, + cohort_codes: 1, + name: 1, + createdAt: 1, + network: 1, + group: 1, + devices: { + $cond: { + if: { $eq: [{ $size: "$devices" }, 0] }, + then: [], + else: "$devices", + }, + }, }) .sort({ createdAt: -1 }) .project(inclusionProjection) @@ -215,11 +223,10 @@ cohortSchema.statics.list = async function( createdAt: { $first: "$createdAt" }, network: { $first: "$network" }, group: { $first: "$group" }, - numberOfDevices: { $sum: 1 }, - devices: { $push: "$devices" }, + devices: { $first: "$devices" }, }) - .skip(skip ? skip : 0) - .limit(limit ? limit : 1000) + .skip(skip ? parseInt(skip) : 0) + .limit(limit ? parseInt(limit) : 1000) .allowDiskUse(true); const cohorts = await pipeline.exec(); @@ -234,24 +241,28 @@ cohortSchema.statics.list = async function( network: cohort.network, createdAt: cohort.createdAt, group: cohort.group, - numberOfDevices: cohort.numberOfDevices, - devices: cohort.devices.map((device) => ({ - _id: device._id, - status: device.status, - name: device.name, - network: device.network, - group: device.group, - device_number: device.device_number, - description: device.description, - long_name: device.long_name, - createdAt: device.createdAt, - host_id: device.host_id, - site: device.site && - device.site[0] && { - _id: device.site[0]._id, - name: device.site[0].name, - }, - })), + numberOfDevices: cohort.devices ? cohort.devices.length : 0, + devices: cohort.devices + ? cohort.devices + .filter((device) => Object.keys(device).length > 0) + .map((device) => ({ + _id: device._id, + status: device.status, + name: device.name, + network: device.network, + group: device.group, + device_number: device.device_number, + description: device.description, + long_name: device.long_name, + createdAt: device.createdAt, + host_id: device.host_id, + site: device.site && + device.site[0] && { + _id: device.site[0]._id, + name: device.site[0].name, + }, + })) + : [], })) .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); From 5d68a3c07a9e5c2ef00eb42c690005b1c69ce6cd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:18:06 +0300 Subject: [PATCH 20/33] Update device registry staging image tag to stage-dfe6eb16-1733832983 --- k8s/device-registry/values-stage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/device-registry/values-stage.yaml b/k8s/device-registry/values-stage.yaml index 00149d1545..c3d8dccf77 100644 --- a/k8s/device-registry/values-stage.yaml +++ b/k8s/device-registry/values-stage.yaml @@ -6,7 +6,7 @@ app: replicaCount: 2 image: repository: eu.gcr.io/airqo-250220/airqo-stage-device-registry-api - tag: stage-1838aa7a-1733249623 + tag: stage-dfe6eb16-1733832983 nameOverride: '' fullnameOverride: '' podAnnotations: {} From 6a892019e2925d3280f58875a2410ea96517b3b1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:19:06 +0300 Subject: [PATCH 21/33] Update AirQo exceedance production image tag to prod-ee15b958-1733833086 --- k8s/exceedance/values-prod-airqo.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/exceedance/values-prod-airqo.yaml b/k8s/exceedance/values-prod-airqo.yaml index 85ddc8196a..8fd10c0c0c 100644 --- a/k8s/exceedance/values-prod-airqo.yaml +++ b/k8s/exceedance/values-prod-airqo.yaml @@ -4,6 +4,6 @@ app: configmap: env-exceedance-production image: repository: eu.gcr.io/airqo-250220/airqo-exceedance-job - tag: prod-aa6aa4ba-1733753092 + tag: prod-ee15b958-1733833086 nameOverride: '' fullnameOverride: '' From 2cf3d8600becf42de9af9422085a027c577f3675 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:19:13 +0300 Subject: [PATCH 22/33] Update KCCA exceedance production image tag to prod-ee15b958-1733833086 --- k8s/exceedance/values-prod-kcca.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/exceedance/values-prod-kcca.yaml b/k8s/exceedance/values-prod-kcca.yaml index 9b6a6d3b1c..72030a5345 100644 --- a/k8s/exceedance/values-prod-kcca.yaml +++ b/k8s/exceedance/values-prod-kcca.yaml @@ -4,6 +4,6 @@ app: configmap: env-exceedance-production image: repository: eu.gcr.io/airqo-250220/kcca-exceedance-job - tag: prod-aa6aa4ba-1733753092 + tag: prod-ee15b958-1733833086 nameOverride: '' fullnameOverride: '' From c43fbd6ad86535f04dffe23dc88e7ee56ac342ed Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:19:35 +0300 Subject: [PATCH 23/33] Update auth service production image tag to prod-ee15b958-1733833086 --- k8s/auth-service/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/auth-service/values-prod.yaml b/k8s/auth-service/values-prod.yaml index f7461e5376..4cb0498ecc 100644 --- a/k8s/auth-service/values-prod.yaml +++ b/k8s/auth-service/values-prod.yaml @@ -6,7 +6,7 @@ app: replicaCount: 3 image: repository: eu.gcr.io/airqo-250220/airqo-auth-api - tag: prod-aa6aa4ba-1733753092 + tag: prod-ee15b958-1733833086 nameOverride: '' fullnameOverride: '' podAnnotations: {} From 57bb1aa3819cbb67206f81dcef2e490b7a5cd541 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:19:49 +0300 Subject: [PATCH 24/33] Update device registry production image tag to prod-ee15b958-1733833086 --- k8s/device-registry/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/device-registry/values-prod.yaml b/k8s/device-registry/values-prod.yaml index 0377f748bf..b4c32c4d56 100644 --- a/k8s/device-registry/values-prod.yaml +++ b/k8s/device-registry/values-prod.yaml @@ -6,7 +6,7 @@ app: replicaCount: 3 image: repository: eu.gcr.io/airqo-250220/airqo-device-registry-api - tag: prod-03a25cf1-1733249950 + tag: prod-ee15b958-1733833086 nameOverride: '' fullnameOverride: '' podAnnotations: {} From 257b3251c0f69621bc661ad77349633d70848e88 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:20:06 +0300 Subject: [PATCH 25/33] Update website production image tag to prod-ee15b958-1733833086 --- k8s/website/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/website/values-prod.yaml b/k8s/website/values-prod.yaml index 31bec7c83a..6460bba388 100644 --- a/k8s/website/values-prod.yaml +++ b/k8s/website/values-prod.yaml @@ -6,7 +6,7 @@ app: replicaCount: 3 image: repository: eu.gcr.io/airqo-250220/airqo-website-api - tag: prod-aa6aa4ba-1733753092 + tag: prod-ee15b958-1733833086 nameOverride: '' fullnameOverride: '' podAnnotations: {} From 2d661b0e2f8fa7656964d6d41115b4fdd87824c8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:20:43 +0300 Subject: [PATCH 26/33] Update workflows prod image tag to prod-ee15b958-1733833086 --- k8s/workflows/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/workflows/values-prod.yaml b/k8s/workflows/values-prod.yaml index fec033074d..723152603a 100644 --- a/k8s/workflows/values-prod.yaml +++ b/k8s/workflows/values-prod.yaml @@ -10,7 +10,7 @@ images: initContainer: eu.gcr.io/airqo-250220/airqo-workflows-xcom redisContainer: eu.gcr.io/airqo-250220/airqo-redis containers: eu.gcr.io/airqo-250220/airqo-workflows - tag: prod-aa6aa4ba-1733753092 + tag: prod-ee15b958-1733833086 nameOverride: '' fullnameOverride: '' podAnnotations: {} From 6673262784e16220e989aa5e19b34c85319eb4de Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:21:50 +0300 Subject: [PATCH 27/33] Update predict production image tag to prod-ee15b958-1733833086 --- k8s/predict/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/predict/values-prod.yaml b/k8s/predict/values-prod.yaml index a554ba7165..70cf680436 100644 --- a/k8s/predict/values-prod.yaml +++ b/k8s/predict/values-prod.yaml @@ -7,7 +7,7 @@ images: predictJob: eu.gcr.io/airqo-250220/airqo-predict-job trainJob: eu.gcr.io/airqo-250220/airqo-train-job predictPlaces: eu.gcr.io/airqo-250220/airqo-predict-places-air-quality - tag: prod-aa6aa4ba-1733753092 + tag: prod-ee15b958-1733833086 api: name: airqo-prediction-api label: prediction-api From 6dbfb845d87283c8209c0f649848456e5d15eb9b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:26:45 +0300 Subject: [PATCH 28/33] Update spatial production image tag to prod-ee15b958-1733833086 --- k8s/spatial/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/spatial/values-prod.yaml b/k8s/spatial/values-prod.yaml index 7e876b40f0..3e448286e8 100644 --- a/k8s/spatial/values-prod.yaml +++ b/k8s/spatial/values-prod.yaml @@ -6,7 +6,7 @@ app: replicaCount: 3 image: repository: eu.gcr.io/airqo-250220/airqo-spatial-api - tag: prod-aa6aa4ba-1733753092 + tag: prod-ee15b958-1733833086 nameOverride: '' fullnameOverride: '' podAnnotations: {} From a0e0c4c621bcc372aeb334dc7fbc5107be4c4e30 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Tue, 10 Dec 2024 15:43:30 +0300 Subject: [PATCH 29/33] updating settings file --- src/website/core/settings.py | 87 ++++++++++++++++++++++++++++++++++-- src/website/entrypoint.sh | 24 ++++++++-- 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/src/website/core/settings.py b/src/website/core/settings.py index 80b50328d9..2dda70d5fd 100644 --- a/src/website/core/settings.py +++ b/src/website/core/settings.py @@ -55,7 +55,7 @@ def require_env_var(env_var: str) -> str: ALLOWED_HOSTS = parse_env_list('ALLOWED_HOSTS', default='localhost,127.0.0.1') # --------------------------------------------------------- -# Applications +# Installed Apps # --------------------------------------------------------- INSTALLED_APPS = [ # Django Defaults @@ -115,9 +115,11 @@ def require_env_var(env_var: str) -> str: CORS_ALLOWED_ORIGIN_REGEXES = parse_env_list('CORS_ORIGIN_REGEX_WHITELIST') CSRF_TRUSTED_ORIGINS = parse_env_list('CSRF_TRUSTED_ORIGINS') -# If no CORS settings provided, consider defaulting to empty lists -CORS_ALLOWED_ORIGINS = CORS_ALLOWED_ORIGINS if CORS_ALLOWED_ORIGINS else [] -CORS_ALLOWED_ORIGIN_REGEXES = CORS_ALLOWED_ORIGIN_REGEXES if CORS_ALLOWED_ORIGIN_REGEXES else [] +# Ensure no trailing slashes and correct schemes +CORS_ALLOWED_ORIGINS = [origin.rstrip('/') for origin in CORS_ALLOWED_ORIGINS] +CORS_ALLOWED_ORIGIN_REGEXES = [regex.rstrip( + '/') for regex in CORS_ALLOWED_ORIGIN_REGEXES] +CSRF_TRUSTED_ORIGINS = [origin.rstrip('/') for origin in CSRF_TRUSTED_ORIGINS] # Security cookies CSRF_COOKIE_SECURE = not DEBUG @@ -269,3 +271,80 @@ def require_env_var(env_var: str) -> str: 'scrollingContainer': '#scrolling-container', }, } + +# --------------------------------------------------------- +# File Upload Settings +# --------------------------------------------------------- +# Increase these values as needed to handle larger uploads +FILE_UPLOAD_MAX_MEMORY_SIZE = 10485760 # 10 MB +DATA_UPLOAD_MAX_MEMORY_SIZE = 10485760 # 10 MB + +# --------------------------------------------------------- +# SSL and Proxy Settings (if behind a reverse proxy) +# --------------------------------------------------------- +# Uncomment and configure if your Django app is behind a reverse proxy like Nginx +SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') +USE_X_FORWARDED_HOST = True + +# --------------------------------------------------------- +# Logging Configuration +# --------------------------------------------------------- +LOG_DIR = BASE_DIR / 'logs' +LOG_DIR.mkdir(exist_ok=True) # Ensure log directory exists + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + # Formatters + 'formatters': { + 'verbose': { + 'format': '[%(asctime)s] %(levelname)s %(name)s [%(filename)s:%(lineno)d] %(message)s', + 'datefmt': '%Y-%m-%d %H:%M:%S' + }, + 'simple': { + 'format': '%(levelname)s %(message)s' + }, + }, + # Handlers + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'formatter': 'verbose', + 'level': 'DEBUG' if DEBUG else 'INFO', + }, + 'file': { + 'class': 'logging.FileHandler', + 'filename': LOG_DIR / 'django.log', + 'formatter': 'verbose', + 'level': 'INFO', + }, + 'error_file': { + 'class': 'logging.FileHandler', + 'filename': LOG_DIR / 'django_errors.log', + 'formatter': 'verbose', + 'level': 'ERROR', + }, + }, + # Loggers + 'loggers': { + # Django Logs + 'django': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'INFO', + 'propagate': True, + }, + # Cloudinary Logs + 'cloudinary': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'INFO', + 'propagate': True, + }, + # Event App Logs + 'apps.event': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + # Add other app loggers here if needed + } +} diff --git a/src/website/entrypoint.sh b/src/website/entrypoint.sh index 915d69698b..cc23b20934 100644 --- a/src/website/entrypoint.sh +++ b/src/website/entrypoint.sh @@ -3,15 +3,33 @@ # Exit immediately if a command exits with a non-zero status set -e +# Function to handle signals and gracefully shut down Gunicorn +_term() { + echo "Caught SIGTERM signal! Shutting down Gunicorn..." + kill -TERM "$child" 2>/dev/null +} + +# Trap SIGTERM signal +trap _term SIGTERM + # Run Django migrations echo "Running migrations..." python manage.py migrate --noinput -# Collect static files (ensure the static files directory exists) +# Collect static files echo "Collecting static files..." python manage.py collectstatic --noinput +# Pre-create logs directory if not exists +echo "Ensuring log directory exists..." +mkdir -p /app/logs + # Start Gunicorn server to serve the Django application echo "Starting Gunicorn server..." -exec gunicorn core.wsgi:application --bind 0.0.0.0:8000 --timeout 600 --log-level info -# exec gunicorn core.wsgi:application --bind 0.0.0.0:8000 --timeout 600 --workers ${GUNICORN_WORKERS:-3} --log-level info +exec gunicorn core.wsgi:application \ + --bind 0.0.0.0:8000 \ + --timeout 600 \ + --log-level info \ + --workers "${GUNICORN_WORKERS:-3}" \ + --access-logfile '-' \ + --error-logfile '-' From ef2bccee9667c3901a5a319b201dc14b9dbf5efd Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Tue, 10 Dec 2024 16:02:55 +0300 Subject: [PATCH 30/33] update --- src/website/Dockerfile | 2 +- src/website/core/settings.py | 1 - src/website/entrypoint.sh | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/website/Dockerfile b/src/website/Dockerfile index 449e362eac..dfe98b351b 100644 --- a/src/website/Dockerfile +++ b/src/website/Dockerfile @@ -22,7 +22,7 @@ RUN pip install --no-cache-dir -r requirements.txt COPY . . # Expose the port the Django app will run on -EXPOSE 8000 +EXPOSE 8080 # Add execution permissions to entrypoint.sh RUN chmod +x /app/entrypoint.sh diff --git a/src/website/core/settings.py b/src/website/core/settings.py index 2dda70d5fd..437d9088dd 100644 --- a/src/website/core/settings.py +++ b/src/website/core/settings.py @@ -282,7 +282,6 @@ def require_env_var(env_var: str) -> str: # --------------------------------------------------------- # SSL and Proxy Settings (if behind a reverse proxy) # --------------------------------------------------------- -# Uncomment and configure if your Django app is behind a reverse proxy like Nginx SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') USE_X_FORWARDED_HOST = True diff --git a/src/website/entrypoint.sh b/src/website/entrypoint.sh index cc23b20934..a7ac8823cc 100644 --- a/src/website/entrypoint.sh +++ b/src/website/entrypoint.sh @@ -25,9 +25,9 @@ echo "Ensuring log directory exists..." mkdir -p /app/logs # Start Gunicorn server to serve the Django application -echo "Starting Gunicorn server..." +echo "Starting Gunicorn server on port $PORT..." exec gunicorn core.wsgi:application \ - --bind 0.0.0.0:8000 \ + --bind 0.0.0.0:$PORT \ --timeout 600 \ --log-level info \ --workers "${GUNICORN_WORKERS:-3}" \ From afc8bbef837ef284eb3bc2e2ff976b1418a138d9 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Tue, 10 Dec 2024 16:07:30 +0300 Subject: [PATCH 31/33] updates --- src/website/Dockerfile | 2 +- src/website/entrypoint.sh | 25 +++---------------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/src/website/Dockerfile b/src/website/Dockerfile index dfe98b351b..449e362eac 100644 --- a/src/website/Dockerfile +++ b/src/website/Dockerfile @@ -22,7 +22,7 @@ RUN pip install --no-cache-dir -r requirements.txt COPY . . # Expose the port the Django app will run on -EXPOSE 8080 +EXPOSE 8000 # Add execution permissions to entrypoint.sh RUN chmod +x /app/entrypoint.sh diff --git a/src/website/entrypoint.sh b/src/website/entrypoint.sh index a7ac8823cc..217438654c 100644 --- a/src/website/entrypoint.sh +++ b/src/website/entrypoint.sh @@ -3,33 +3,14 @@ # Exit immediately if a command exits with a non-zero status set -e -# Function to handle signals and gracefully shut down Gunicorn -_term() { - echo "Caught SIGTERM signal! Shutting down Gunicorn..." - kill -TERM "$child" 2>/dev/null -} - -# Trap SIGTERM signal -trap _term SIGTERM - # Run Django migrations echo "Running migrations..." python manage.py migrate --noinput -# Collect static files +# Collect static files (ensure the static files directory exists) echo "Collecting static files..." python manage.py collectstatic --noinput -# Pre-create logs directory if not exists -echo "Ensuring log directory exists..." -mkdir -p /app/logs - # Start Gunicorn server to serve the Django application -echo "Starting Gunicorn server on port $PORT..." -exec gunicorn core.wsgi:application \ - --bind 0.0.0.0:$PORT \ - --timeout 600 \ - --log-level info \ - --workers "${GUNICORN_WORKERS:-3}" \ - --access-logfile '-' \ - --error-logfile '-' +echo "Starting Gunicorn server..." +exec gunicorn core.wsgi:application --bind 0.0.0.0:8000 --timeout 600 --workers 3 --log-level info From fff06671f8be3c8f19f86afa8e17d0fe6a19f149 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Tue, 10 Dec 2024 16:15:30 +0300 Subject: [PATCH 32/33] updates --- src/website/core/settings.py | 73 +++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/src/website/core/settings.py b/src/website/core/settings.py index 437d9088dd..5bffef8789 100644 --- a/src/website/core/settings.py +++ b/src/website/core/settings.py @@ -344,6 +344,77 @@ def require_env_var(env_var: str) -> str: 'level': 'DEBUG' if DEBUG else 'INFO', 'propagate': False, }, - # Add other app loggers here if needed + # CleanAir App Logs + 'apps.cleanair': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + # AfricanCities App Logs + 'apps.africancities': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + # Publications App Logs + 'apps.publications': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + # Press App Logs + 'apps.press': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + # Impact App Logs + 'apps.impact': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + # FAQs App Logs + 'apps.faqs': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + # Highlights App Logs + 'apps.highlights': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + # Career App Logs + 'apps.career': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + # Partners App Logs + 'apps.partners': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + # Board App Logs + 'apps.board': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + # Team App Logs + 'apps.team': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + # ExternalTeams App Logs + 'apps.externalteams': { + 'handlers': ['console', 'file', 'error_file'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, } } From 9cab881e669cdc2a7f3016c499c3db7e5f5ed695 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 16:21:23 +0300 Subject: [PATCH 33/33] Update website staging image tag to stage-7a1d5dd3-1733836788 --- k8s/website/values-stage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/website/values-stage.yaml b/k8s/website/values-stage.yaml index 61f8603b8d..a85c7a78ca 100644 --- a/k8s/website/values-stage.yaml +++ b/k8s/website/values-stage.yaml @@ -6,7 +6,7 @@ app: replicaCount: 2 image: repository: eu.gcr.io/airqo-250220/airqo-stage-website-api - tag: stage-ac4d3c16-1733830718 + tag: stage-7a1d5dd3-1733836788 nameOverride: '' fullnameOverride: '' podAnnotations: {}