""" Django settings for hiresync_backend project. Generated by 'django-admin startproject' using Django 5.2.14. """ import os import sys from pathlib import Path from urllib.parse import urlparse from dotenv import load_dotenv from datetime import timedelta # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # Load .env file load_dotenv(os.path.join(BASE_DIR, '.env')) # Add apps to Python path sys.path.insert(0, os.path.join(BASE_DIR, 'apps')) # Quick-start development settings - unsuitable for production SECRET_KEY = os.environ.get('SECRET_KEY', 'django-insecure-fallback-key') DEBUG = os.environ.get('DEBUG', 'True').lower() in ('true', '1', 't') ALLOWED_HOSTS = [host.strip() for host in os.environ.get('ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',') if host.strip()] # Application definition INSTALLED_APPS = [ # Daphne must be at the top of INSTALLED_APPS for Channels integration 'daphne', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # Third-party apps 'rest_framework', 'rest_framework_simplejwt', 'corsheaders', 'channels', # Local apps 'authentication', 'companies', 'jobs', 'candidates', 'assessments', 'interviews', ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', # Put CORS first 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'companies.middleware.TenantMiddleware', # Custom multi-tenant workspace context resolver 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'hiresync_backend.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'hiresync_backend.wsgi.application' ASGI_APPLICATION = 'hiresync_backend.asgi.application' # Database Connection (Mandatory production-ready Supabase PostgreSQL) db_url = os.environ.get('DATABASE_URL', '') if not db_url or not (db_url.startswith('postgres://') or db_url.startswith('postgresql://')): from django.core.exceptions import ImproperlyConfigured raise ImproperlyConfigured( "\n" + "="*80 + "\n" " ❌ CONFIGURATION ERROR: DATABASE_URL environment variable is missing or invalid!\n" " Production-ready Supabase PostgreSQL is MANDATORY for Tarvax.\n\n" " 👉 TO CONFIGURE SUPABASE:\n" " 1. Copy 'backend/.env.example' to 'backend/.env'\n" " 2. Set the following environment variables in '.env':\n" " DATABASE_URL=postgresql://postgres:Yash9897422911@db.kntarabupposhjhlqwob.supabase.co:5432/postgres\n" " SUPABASE_URL=https://kntarabupposhjhlqwob.supabase.co\n" " SUPABASE_JWT_SECRET=kcwYMVIO/tp2KTwydJqHc7XnsnvIoYBGVe3LGvl7BAEVIZi25FmsFo70uaRRjmxwBIRYJl5qGX1nJYSJwD5Thw==\n" "="*80 + "\n" ) url = urlparse(db_url) DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': url.path[1:], 'USER': url.username, 'PASSWORD': url.password, 'HOST': url.hostname, 'PORT': url.port or 5432, 'OPTIONS': { 'sslmode': 'prefer', } } } # Password validation AUTH_PASSWORD_VALIDATORS = [ {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'}, {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'}, {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'}, {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}, ] # DRF configuration REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'authentication.authentication.SupabaseJWTAuthentication', # Custom Supabase authentication 'authentication.authentication.RecruiterJWTAuthentication', # Custom Recruiter SimpleJWT authentication ), 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ), } # Simple JWT Settings SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(hours=8), # 8-hour expiry for Recruiters 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), 'ROTATE_REFRESH_TOKENS': True, 'BLACKLIST_AFTER_ROTATION': True, 'ALGORITHM': 'HS256', 'SIGNING_KEY': SECRET_KEY, } # Supabase Auth Settings SUPABASE_URL = os.environ.get('SUPABASE_URL', '').strip().strip('\'"') if not SUPABASE_URL or not SUPABASE_URL.startswith('http') or SUPABASE_URL == 'SUPABASE_URL' or 'your-project' in SUPABASE_URL: SUPABASE_URL = 'https://kntarabupposhjhlqwob.supabase.co' SUPABASE_JWT_SECRET = os.environ.get('SUPABASE_JWT_SECRET', '').strip().strip('\'"') if not SUPABASE_JWT_SECRET or SUPABASE_JWT_SECRET == 'SUPABASE_JWT_SECRET' or 'your-supabase-jwt-secret' in SUPABASE_JWT_SECRET: SUPABASE_JWT_SECRET = 'kcwYMVIO/tp2KTwydJqHc7XnsnvIoYBGVe3LGvl7BAEVIZi25FmsFo70uaRRjmxwBIRYJl5qGX1nJYSJwD5Thw==' # CORS Settings CORS_ALLOW_ALL_ORIGINS = True # For dev purposes CORS_ALLOW_CREDENTIALS = True # CSRF Settings CSRF_TRUSTED_ORIGINS = [ 'http://localhost:5173', 'http://127.0.0.1:5173', ] + [origin.strip() for origin in os.environ.get('CSRF_TRUSTED_ORIGINS', '').split(',') if origin.strip()] # If not in debug mode, append wildcard to ALLOWED_HOSTS to prevent host header blocking in Hugging Face Spaces proxy if not DEBUG: ALLOWED_HOSTS.append('*') # Internationalization LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_TZ = True # Static files STATIC_URL = 'static/' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' # WebSockets Channel Layers # Use in-memory layer as dev fallback if redis config is not complete CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels.layers.InMemoryChannelLayer', }, } # Media files (for resume uploads, profile photos) MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # Setup logging LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'class': 'logging.StreamHandler', }, }, 'root': { 'handlers': ['console'], 'level': 'INFO', }, }