From baaa9c4a9f01b8e51afcca03520e1ac31594bc7d Mon Sep 17 00:00:00 2001 From: Ahmed Nassar Date: Sat, 7 Dec 2024 22:24:13 +0200 Subject: [PATCH] feat: Refactor settings for environment variables, add health check endpoint, and update logging configuration; include render.yaml for deployment --- LICENSE | 2 +- egypt_metro/settings.py | 73 ++++++++++++++++++++++------------------- egypt_metro/urls.py | 2 ++ egypt_metro/views.py | 4 +++ render.yaml | 22 +++++++++++++ requirements.txt | 1 + 6 files changed, 70 insertions(+), 34 deletions(-) create mode 100644 egypt_metro/views.py create mode 100644 render.yaml diff --git a/LICENSE b/LICENSE index d8af004..97c21d3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -MIT License +# MIT License Copyright (c) 2024 Egypt Metro diff --git a/egypt_metro/settings.py b/egypt_metro/settings.py index 4bbf3fe..233f5b5 100644 --- a/egypt_metro/settings.py +++ b/egypt_metro/settings.py @@ -19,18 +19,13 @@ # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent - # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-qyiu%=8(38%^l2@8wfmn^44bt!18fa&q(5(v0d=slb81g0_h#0' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = [] +SECRET_KEY = os.getenv("SECRET_KEY") +ALLOWED_HOSTS = [os.getenv("BASE_URL"), "127.0.0.1", "localhost"] # Application definition @@ -57,11 +52,13 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'corsheaders.middleware.CorsMiddleware', ] ROOT_URLCONF = 'egypt_metro.urls' -CORS_ALLOW_ALL_ORIGINS = True +# CORS settings +CORS_ALLOW_ALL_ORIGINS = os.getenv("CORS_ALLOW_ALL_ORIGINS", "False") == "True" TEMPLATES = [ { @@ -81,43 +78,48 @@ WSGI_APPLICATION = 'egypt_metro.wsgi.application' - # Database # https://docs.djangoproject.com/en/5.1/ref/settings/#databases -# Initialize environment variables -env = environ.Env() -environ.Env.read_env(os.path.join(BASE_DIR, '.env')) +AUTH_USER_MODEL = 'users.User' -# Load environment variables -load_dotenv() +# Load the appropriate .env file based on an environment variable +ENVIRONMENT = os.getenv("ENVIRONMENT", "dev") # Default to dev +dotenv_path = BASE_DIR / f"env/.env.{ENVIRONMENT}" +load_dotenv(dotenv_path) -# General settings -DEBUG = env.bool("DEBUG") -SECRET_KEY = env("SECRET_KEY") -BASE_URL = env("BASE_URL", default="http://127.0.0.1:8000") -JWT_SECRET = env("JWT_SECRET") +# Load secret file if in production +if ENVIRONMENT == "prod": + load_dotenv("/etc/secrets/env.prod") -AUTH_USER_MODEL = 'users.User' +# General settings +DEBUG = os.getenv("DEBUG", "False") == "True" +SECRET_KEY = os.getenv("SECRET_KEY") +BASE_URL = os.getenv("BASE_URL", "http://127.0.0.1:8000") +JWT_SECRET = os.getenv("JWT_SECRET") # Database configuration DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', - 'NAME': env('DB_NAME'), # Use environment variable for DB name - 'USER': env('DB_USER'), # Use environment variable for DB user - 'PASSWORD': env('DB_PASSWORD'),# Use environment variable for password - 'HOST': env('DB_HOST'), # Use environment variable for host - 'PORT': env('DB_PORT'), # Use environment variable for port + 'CONN_MAX_AGE': 500, + 'OPTIONS': { + 'options': '-c search_path=public', + }, + 'DISABLE_SERVER_SIDE_CURSORS': True, + "NAME": os.getenv("DB_NAME"), + "USER": os.getenv("DB_USER"), + "PASSWORD": os.getenv("DB_PASSWORD"), + "HOST": os.getenv("DB_HOST"), + "PORT": os.getenv("DB_PORT"), } } -# Print debug (remove or comment out in production) -# print(f"Database: {env('DB_NAME')}") -# print(f"User: {env('DB_USER')}") -# print(f"Password: {env('DB_PASSWORD')}") -# print(f"Host: {env('DB_HOST')}") -# print(f"Port: {env('DB_PORT')}") +REQUIRED_ENV_VARS = ["SECRET_KEY", "DATABASE_URL", "JWT_SECRET", "BASE_URL"] + +for var in REQUIRED_ENV_VARS: + if not os.getenv(var): + raise ValueError(f"{var} is not set in environment variables.") # Password validation # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators @@ -172,14 +174,14 @@ 'file': { 'level': 'DEBUG', 'class': 'logging.FileHandler', - 'filename': 'debug.log', # File where logs are saved + 'filename': BASE_DIR / 'logs/debug.log', # File where logs are saved 'formatter': 'verbose', }, }, 'loggers': { 'django': { 'handlers': ['console', 'file'], - 'level': 'DEBUG', + 'level': 'INFO', 'propagate': True, }, '__main__': { @@ -207,6 +209,11 @@ # https://docs.djangoproject.com/en/5.1/howto/static-files/ STATIC_URL = 'static/' +STATIC_ROOT = BASE_DIR / 'staticfiles' # Folder where static files will be collected + +# Media files (optional, if your project uses media uploads) +MEDIA_URL = '/media/' +MEDIA_ROOT = BASE_DIR / 'mediafiles' # Default primary key field type # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field diff --git a/egypt_metro/urls.py b/egypt_metro/urls.py index b5824aa..9ce3c0a 100644 --- a/egypt_metro/urls.py +++ b/egypt_metro/urls.py @@ -16,8 +16,10 @@ """ from django.contrib import admin from django.urls import path, include +from .views import health_check urlpatterns = [ path('admin/', admin.site.urls), # Django admin panel path('api/users/', include('apps.users.urls')), # User-related API routes + path('health/', health_check, name='health_check'), # Health check endpoint ] diff --git a/egypt_metro/views.py b/egypt_metro/views.py new file mode 100644 index 0000000..745a3df --- /dev/null +++ b/egypt_metro/views.py @@ -0,0 +1,4 @@ +from django.http import JsonResponse + +def health_check(request): + return JsonResponse({"status": "ok"}) \ No newline at end of file diff --git a/render.yaml b/render.yaml new file mode 100644 index 0000000..8fcb685 --- /dev/null +++ b/render.yaml @@ -0,0 +1,22 @@ +services: + - type: web + name: egypt-metro + env: python + region: cairo # Adjust based on your location + buildCommand: "pip install -r requirements.txt" + startCommand: "gunicorn egypt_metro.wsgi:application --bind 0.0.0.0:8000" + envVars: + - key: ENVIRONMENT # Environment for loading specific config + value: prod + - key: DATABASE_URL # Add the database URL environment variable + value: postgres://postgres:123@localhost:5432/egypt_metro + - fromGroup: egypt-metro-env-group # Link generic variables from environment group + - key: CORS_ALLOW_ALL_ORIGINS # Set CORS settings + value: "True" + disk: # Persistent storage (optional) + name: persistent-data + mountPath: /var/lib/egypt-metro + sizeGB: 1 + secretFiles: + - path: /etc/secrets/env.prod # Load sensitive secrets (e.g., SECRET_KEY, JWT_SECRET) + healthCheckPath: "/health" # Optional, ensure health endpoint exists diff --git a/requirements.txt b/requirements.txt index 208de9b..6a856e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,7 @@ drf-yasg==1.21.8 executing==2.1.0 filelock==3.16.1 git-filter-repo==2.45.0 +gunicorn==23.0.0 httplib2==0.22.0 inflection==0.5.1 ipykernel==6.29.5