Coverage for cookie / settings.py: 89%

44 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-14 19:13 +0000

1""" 

2Django settings for cookie project. 

3Single settings file for simplicity. 

4""" 

5 

6import os 

7from pathlib import Path 

8 

9import dj_database_url 

10 

11BASE_DIR = Path(__file__).resolve().parent.parent 

12 

13# =========================================== 

14# Environment-based Configuration 

15# =========================================== 

16 

17DEBUG = os.environ.get("DEBUG", "True").lower() == "true" 

18 

19 

20def get_secret_key(): 

21 """Get secret key from environment or generate one.""" 

22 env_key = os.environ.get("SECRET_KEY") 

23 if env_key: 

24 return env_key 

25 if DEBUG: 

26 return "django-insecure-dev-key-change-in-production" 

27 from django.core.management.utils import get_random_secret_key 

28 

29 return get_random_secret_key() 

30 

31 

32SECRET_KEY = get_secret_key() 

33 

34ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "*").split(",") 

35 

36# Use X-Forwarded-Host header (preserves port when behind nginx proxy) 

37USE_X_FORWARDED_HOST = True 

38 

39# CSRF trusted origins (for reverse proxies) 

40csrf_origins = os.environ.get("CSRF_TRUSTED_ORIGINS", "") 

41CSRF_TRUSTED_ORIGINS = [o.strip() for o in csrf_origins.split(",") if o.strip()] 

42 

43INSTALLED_APPS = [ 

44 "django.contrib.contenttypes", 

45 "django.contrib.sessions", 

46 "django.contrib.staticfiles", 

47 "apps.core", 

48 "apps.profiles", 

49 "apps.recipes", 

50 "apps.ai", 

51 "apps.legacy", 

52] 

53 

54MIDDLEWARE = [ 

55 "django.middleware.security.SecurityMiddleware", 

56 "whitenoise.middleware.WhiteNoiseMiddleware", 

57 "django.contrib.sessions.middleware.SessionMiddleware", 

58 "django.middleware.common.CommonMiddleware", 

59 "apps.core.middleware.DeviceDetectionMiddleware", 

60] 

61 

62ROOT_URLCONF = "cookie.urls" 

63 

64TEMPLATES = [ 

65 { 

66 "BACKEND": "django.template.backends.django.DjangoTemplates", 

67 "DIRS": [], 

68 "APP_DIRS": True, 

69 "OPTIONS": { 

70 "context_processors": [ 

71 "django.template.context_processors.debug", 

72 "django.template.context_processors.request", 

73 ], 

74 }, 

75 }, 

76] 

77 

78WSGI_APPLICATION = "cookie.wsgi.application" 

79 

80# Database configuration 

81# Priority: DATABASE_URL > DATABASE_PATH > default SQLite 

82DATABASE_URL = os.environ.get("DATABASE_URL") 

83 

84if DATABASE_URL: 

85 DATABASES = { 

86 "default": dj_database_url.parse( 

87 DATABASE_URL, 

88 conn_max_age=60, 

89 conn_health_checks=True, 

90 ) 

91 } 

92else: 

93 # SQLite fallback (existing behavior) 

94 DATABASE_PATH = os.environ.get("DATABASE_PATH", str(BASE_DIR / "db.sqlite3")) 

95 DATABASES = { 

96 "default": { 

97 "ENGINE": "django.db.backends.sqlite3", 

98 "NAME": DATABASE_PATH, 

99 "OPTIONS": { 

100 # Increase lock wait timeout from default 5s to 20s 

101 "timeout": 20, 

102 # Acquire write lock at transaction START (not mid-transaction) 

103 # This prevents "database is locked" errors during concurrent writes 

104 # by allowing failed lock acquisitions to be retried 

105 "transaction_mode": "IMMEDIATE", 

106 # PRAGMA settings applied on each new connection: 

107 # - journal_mode=WAL: Allow concurrent reads during writes 

108 # - synchronous=NORMAL: Safe for WAL mode, better performance 

109 # - busy_timeout=5000: Wait up to 5s for locks at SQLite level 

110 "init_command": ("PRAGMA journal_mode=WAL;PRAGMA synchronous=NORMAL;PRAGMA busy_timeout=5000;"), 

111 }, 

112 } 

113 } 

114 

115LANGUAGE_CODE = "en-us" 

116TIME_ZONE = "UTC" 

117USE_I18N = True 

118USE_TZ = True 

119 

120STATIC_URL = "static/" 

121STATIC_ROOT = BASE_DIR / "staticfiles" 

122 

123# Include built frontend assets in static files (only if directory exists) 

124_frontend_dist = BASE_DIR / "frontend" / "dist" 

125STATICFILES_DIRS = [_frontend_dist] if _frontend_dist.exists() else [] 

126 

127# WhiteNoise configuration for efficient static file serving 

128STORAGES = { 

129 "default": { 

130 "BACKEND": "django.core.files.storage.FileSystemStorage", 

131 }, 

132 "staticfiles": { 

133 "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", 

134 }, 

135} 

136 

137MEDIA_URL = "/media/" 

138data_dir = os.environ.get("DATA_DIR", str(BASE_DIR)) 

139MEDIA_ROOT = Path(data_dir) / "data" / "media" if not DEBUG else BASE_DIR / "media" 

140 

141DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 

142 

143# Session settings 

144SESSION_ENGINE = "django.contrib.sessions.backends.db" 

145SESSION_COOKIE_AGE = 43200 # 12 hours 

146 

147# Logging configuration 

148LOGGING = { 

149 "version": 1, 

150 "disable_existing_loggers": False, 

151 "formatters": { 

152 "verbose": { 

153 "format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}", 

154 "style": "{", 

155 }, 

156 "simple": { 

157 "format": "{levelname} {message}", 

158 "style": "{", 

159 }, 

160 }, 

161 "handlers": { 

162 "console": { 

163 "class": "logging.StreamHandler", 

164 "formatter": "verbose", 

165 }, 

166 }, 

167 "loggers": { 

168 "apps.recipes": { 

169 "handlers": ["console"], 

170 "level": "INFO", 

171 "propagate": False, 

172 }, 

173 "apps.recipes.services": { 

174 "handlers": ["console"], 

175 "level": "INFO", 

176 "propagate": False, 

177 }, 

178 "apps.ai": { 

179 "handlers": ["console"], 

180 "level": "INFO", 

181 "propagate": False, 

182 }, 

183 }, 

184 "root": { 

185 "handlers": ["console"], 

186 "level": "WARNING", 

187 }, 

188} 

← Back to Dashboard