server/avocano_api/settings.py (129 lines of code) (raw):
#!/usr/bin/python
#
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import io
import os
from pathlib import Path
from urllib.parse import urlparse
import environ
from .cloudrun_helpers import MetadataError, get_service_url, get_project_id
BASE_DIR = Path(__file__).resolve().parent.parent
# Load settings from local .env, mounted .env, or envvar.
env = environ.Env()
env.read_env(BASE_DIR / ".env")
env.read_env("/settings/.env")
env.read_env(io.StringIO(os.environ.get("DJANGO_ENV", None)))
SECRET_KEY = env("SECRET_KEY")
DEBUG = env("DEBUG", default=False)
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"store",
"colorfield",
"corsheaders",
"rest_framework",
"django_filters",
"drf_spectacular",
]
MIDDLEWARE = [
"avocano_api.healthchecks.HealthCheckMiddleware",
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "avocano_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",
],
},
},
]
# Used for local dev or using Cloud Run proxy
local_hosts = ["http://localhost:8000", "http://localhost:8081"]
# Used for Cloud Shell dev with Web Preview
cloudshell_host = "https://*.cloudshell.dev"
# Used to identify the host of the deployed service
# Supports a comma separated list of full URLs
CLOUDRUN_SERVICE_URL = env("CLOUDRUN_SERVICE_URL", default=None)
# If the Cloud Run service isn't defined, try dynamically retrieving it.
if not CLOUDRUN_SERVICE_URL:
try:
CLOUDRUN_SERVICE_URL = get_service_url()
except MetadataError:
pass
if CLOUDRUN_SERVICE_URL:
# Setup as we are running in Cloud Run & Firebase
service_urls = CLOUDRUN_SERVICE_URL.split(",")
ALLOWED_HOSTS = [urlparse(url).netloc for url in service_urls] + ["127.0.0.1"]
# Firebase hosting has multiple default URLs, so add those as well.
project_id = get_project_id()
# If a deployment suffix is provided, presume this is being used as
# the Firebase Site URL, rather than just the default site (project-id).
# From variable `random_suffix` in https://github.com/GoogleCloudPlatform/terraform-dynamic-python-webapp/blob/main/infra/variables.tf#L44
DEPLOYMENT_SUFFIX = env("DEPLOYMENT_SUFFIX", default=None)
if DEPLOYMENT_SUFFIX:
firebase_site_id = f"{project_id}-{DEPLOYMENT_SUFFIX}"
else:
firebase_site_id = project_id
firebase_hosts = [
f"https://{firebase_site_id}.web.app",
f"https://{firebase_site_id}.firebaseapp.com",
]
CSRF_TRUSTED_ORIGINS = service_urls + local_hosts + firebase_hosts
SECURE_SSL_REDIRECT = True
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
else:
# Setup as we are running on localhost, or Cloud Shell
ALLOWED_HOSTS = ["*"]
CSRF_TRUSTED_ORIGINS = [cloudshell_host] + local_hosts
# django-cors-headers settings
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
WSGI_APPLICATION = "avocano_api.wsgi.application"
# Database
# https://docs.djangoproject.com/en/stable/ref/settings/#databases
# Use django-environ to parse the connection string
DATABASES = {"default": env.db()}
# If the flag as been set, configure to use proxy
if env("USE_CLOUD_SQL_AUTH_PROXY", default=None):
DATABASES["default"]["HOST"] = "127.0.0.1"
DATABASES["default"]["PORT"] = 5432
# Password validation
# https://docs.djangoproject.com/en/stable/ref/settings/#auth-password-validators
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",
},
]
# Internationalization
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
# Default primary key field type
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
# Django REST Framework
REST_FRAMEWORK = {
# Any exposed endpoints can be accessed by any client that has access to the API itself.
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.AllowAny"],
# For automatic OpenAPI schema.
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}
# Static files (CSS, JavaScript, Images)
STATIC_URL = "/static/"
MEDIA_URL = "/media/"
# Use Cloud Storage if configured, otherwise use local storage.
if GS_BUCKET_NAME := env("GS_BUCKET_NAME", default=None):
STORAGES = {
"default": {"BACKEND": "storages.backends.gcloud.GoogleCloudStorage"},
"staticfiles": {"BACKEND": "storages.backends.gcloud.GoogleCloudStorage"},
}
GS_DEFAULT_ACL = "publicRead"
else:
STORAGES = {
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
},
}
STATIC_ROOT = os.path.join(BASE_DIR, STATIC_URL.replace("/", ""))
MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_URL.replace("/", ""))