dc68ecfe4c
Added initial migrations for accounts, salons, bookings, payments. 0001_initial.py 0001_initial.py 0001_initial.py 0001_initial.py Added demo seeding command. seed_demo.py OTP provider abstraction now includes Twilio + Unifonic adapter scaffolds (not implemented yet, they raise). otp.py Moyasar gateway scaffold added (not implemented yet, raises). gateway.py Added .env loading in settings and expanded .env.example for OTP and Moyasar. settings.py .env.example Captured current gaps/risks in a tracked doc as requested. risks.md Updated README with seeding guidance and risk doc pointer. README.md
133 lines
3.7 KiB
Python
133 lines
3.7 KiB
Python
import os
|
|
from pathlib import Path
|
|
from datetime import timedelta
|
|
from urllib.parse import urlparse
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
|
|
load_dotenv(BASE_DIR / ".env")
|
|
|
|
SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", "unsafe-dev-key")
|
|
DEBUG = os.getenv("DJANGO_DEBUG", "0") == "1"
|
|
ALLOWED_HOSTS = [h.strip() for h in os.getenv("DJANGO_ALLOWED_HOSTS", "").split(",") if h.strip()]
|
|
|
|
INSTALLED_APPS = [
|
|
"django.contrib.admin",
|
|
"django.contrib.auth",
|
|
"django.contrib.contenttypes",
|
|
"django.contrib.sessions",
|
|
"django.contrib.messages",
|
|
"django.contrib.staticfiles",
|
|
"rest_framework",
|
|
"corsheaders",
|
|
"apps.accounts",
|
|
"apps.salons",
|
|
"apps.bookings",
|
|
"apps.payments",
|
|
]
|
|
|
|
MIDDLEWARE = [
|
|
"django.middleware.security.SecurityMiddleware",
|
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
|
"corsheaders.middleware.CorsMiddleware",
|
|
"django.middleware.common.CommonMiddleware",
|
|
"django.middleware.csrf.CsrfViewMiddleware",
|
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
|
"django.contrib.messages.middleware.MessageMiddleware",
|
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
]
|
|
|
|
ROOT_URLCONF = "salon_api.urls"
|
|
|
|
TEMPLATES = [
|
|
{
|
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
|
"DIRS": [],
|
|
"APP_DIRS": True,
|
|
"OPTIONS": {
|
|
"context_processors": [
|
|
"django.template.context_processors.debug",
|
|
"django.template.context_processors.request",
|
|
"django.contrib.auth.context_processors.auth",
|
|
"django.contrib.messages.context_processors.messages",
|
|
],
|
|
},
|
|
},
|
|
]
|
|
|
|
WSGI_APPLICATION = "salon_api.wsgi.application"
|
|
ASGI_APPLICATION = "salon_api.asgi.application"
|
|
|
|
|
|
def parse_database_url(database_url: str):
|
|
parsed = urlparse(database_url)
|
|
if parsed.scheme not in {"postgres", "postgresql"}:
|
|
return None
|
|
return {
|
|
"ENGINE": "django.db.backends.postgresql",
|
|
"NAME": parsed.path.lstrip("/"),
|
|
"USER": parsed.username,
|
|
"PASSWORD": parsed.password,
|
|
"HOST": parsed.hostname,
|
|
"PORT": parsed.port or "5432",
|
|
}
|
|
|
|
|
|
DATABASE_URL = os.getenv("DATABASE_URL")
|
|
if DATABASE_URL:
|
|
parsed_db = parse_database_url(DATABASE_URL)
|
|
else:
|
|
parsed_db = None
|
|
|
|
DATABASES = {
|
|
"default": parsed_db
|
|
or {
|
|
"ENGINE": "django.db.backends.sqlite3",
|
|
"NAME": BASE_DIR / "db.sqlite3",
|
|
}
|
|
}
|
|
|
|
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"},
|
|
]
|
|
|
|
LANGUAGE_CODE = "en-us"
|
|
TIME_ZONE = "Asia/Riyadh"
|
|
USE_I18N = True
|
|
USE_TZ = True
|
|
|
|
STATIC_URL = "static/"
|
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|
|
|
AUTH_USER_MODEL = "accounts.User"
|
|
|
|
REST_FRAMEWORK = {
|
|
"DEFAULT_AUTHENTICATION_CLASSES": (
|
|
"rest_framework_simplejwt.authentication.JWTAuthentication",
|
|
),
|
|
"DEFAULT_PERMISSION_CLASSES": (
|
|
"rest_framework.permissions.IsAuthenticatedOrReadOnly",
|
|
),
|
|
}
|
|
|
|
SIMPLE_JWT = {
|
|
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=30),
|
|
"REFRESH_TOKEN_LIFETIME": timedelta(days=7),
|
|
"AUTH_HEADER_TYPES": ("Bearer",),
|
|
}
|
|
|
|
CORS_ALLOWED_ORIGINS = [
|
|
origin.strip()
|
|
for origin in os.getenv("CORS_ALLOWED_ORIGINS", "").split(",")
|
|
if origin.strip()
|
|
]
|
|
|
|
OTP_PROVIDER = os.getenv("OTP_PROVIDER", "console")
|
|
OTP_EXPIRY_MINUTES = int(os.getenv("OTP_EXPIRY_MINUTES", "5"))
|
|
DEFAULT_CURRENCY = os.getenv("DEFAULT_CURRENCY", "SAR")
|