|
4ce269c…
|
ragelink
|
1 |
import logging |
|
4ce269c…
|
ragelink
|
2 |
import os |
|
4ce269c…
|
ragelink
|
3 |
from pathlib import Path |
|
4ce269c…
|
ragelink
|
4 |
|
|
4ce269c…
|
ragelink
|
5 |
from django.core.exceptions import ImproperlyConfigured |
|
4ce269c…
|
ragelink
|
6 |
|
|
4ce269c…
|
ragelink
|
7 |
logger = logging.getLogger(__name__) |
|
4ce269c…
|
ragelink
|
8 |
|
|
4ce269c…
|
ragelink
|
9 |
VERSION = "0.1.0" |
|
4ce269c…
|
ragelink
|
10 |
|
|
4ce269c…
|
ragelink
|
11 |
BASE_DIR = Path(__file__).resolve().parent.parent |
|
4ce269c…
|
ragelink
|
12 |
|
|
4ce269c…
|
ragelink
|
13 |
|
|
4ce269c…
|
ragelink
|
14 |
def env_str(name: str, default: str | None = None) -> str | None: |
|
4ce269c…
|
ragelink
|
15 |
return os.getenv(name, default) |
|
4ce269c…
|
ragelink
|
16 |
|
|
4ce269c…
|
ragelink
|
17 |
|
|
4ce269c…
|
ragelink
|
18 |
def env_bool(name: str, default: bool = False) -> bool: |
|
4ce269c…
|
ragelink
|
19 |
return os.getenv(name, str(default)).lower() in ("true", "1", "yes") |
|
4ce269c…
|
ragelink
|
20 |
|
|
4ce269c…
|
ragelink
|
21 |
|
|
4ce269c…
|
ragelink
|
22 |
def env_int(name: str, default: int = 0) -> int: |
|
4ce269c…
|
ragelink
|
23 |
return int(os.getenv(name, str(default))) |
|
4ce269c…
|
ragelink
|
24 |
|
|
4ce269c…
|
ragelink
|
25 |
|
|
4ce269c…
|
ragelink
|
26 |
# --- Security --- |
|
4ce269c…
|
ragelink
|
27 |
|
|
4ce269c…
|
ragelink
|
28 |
SECRET_KEY = env_str("DJANGO_SECRET_KEY", "change-me-in-production") |
|
4ce269c…
|
ragelink
|
29 |
DEBUG = env_bool("DJANGO_DEBUG", False) |
|
4ce269c…
|
ragelink
|
30 |
ALLOWED_HOSTS = [h.strip() for h in env_str("DJANGO_ALLOWED_HOSTS", "localhost,127.0.0.1,0.0.0.0").split(",")] |
|
4ce269c…
|
ragelink
|
31 |
|
|
4ce269c…
|
ragelink
|
32 |
if not DEBUG and SECRET_KEY == "change-me-in-production": |
|
4ce269c…
|
ragelink
|
33 |
raise ImproperlyConfigured("DJANGO_SECRET_KEY must be set to a unique, unpredictable value when DEBUG is False.") |
|
4ce269c…
|
ragelink
|
34 |
|
|
4ce269c…
|
ragelink
|
35 |
# --- Application --- |
|
4ce269c…
|
ragelink
|
36 |
|
|
4ce269c…
|
ragelink
|
37 |
ROOT_URLCONF = "config.urls" |
|
4ce269c…
|
ragelink
|
38 |
WSGI_APPLICATION = "config.wsgi.application" |
|
4ce269c…
|
ragelink
|
39 |
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" |
|
4ce269c…
|
ragelink
|
40 |
LOGIN_URL = "/auth/login/" |
|
4ce269c…
|
ragelink
|
41 |
LOGIN_REDIRECT_URL = "/" |
|
4ce269c…
|
ragelink
|
42 |
LOGOUT_REDIRECT_URL = "/auth/login/" |
|
4ce269c…
|
ragelink
|
43 |
|
|
4ce269c…
|
ragelink
|
44 |
INSTALLED_APPS = [ |
|
4ce269c…
|
ragelink
|
45 |
"django.contrib.admin", |
|
4ce269c…
|
ragelink
|
46 |
"django.contrib.auth", |
|
4ce269c…
|
ragelink
|
47 |
"django.contrib.contenttypes", |
|
4ce269c…
|
ragelink
|
48 |
"django.contrib.sessions", |
|
4ce269c…
|
ragelink
|
49 |
"django.contrib.messages", |
|
4ce269c…
|
ragelink
|
50 |
"django.contrib.staticfiles", |
|
4ce269c…
|
ragelink
|
51 |
"django.contrib.humanize", |
|
4ce269c…
|
ragelink
|
52 |
# Third-party |
|
4ce269c…
|
ragelink
|
53 |
"import_export", |
|
4ce269c…
|
ragelink
|
54 |
"simple_history", |
|
4ce269c…
|
ragelink
|
55 |
"django_celery_results", |
|
4ce269c…
|
ragelink
|
56 |
"django_celery_beat", |
|
4ce269c…
|
ragelink
|
57 |
"corsheaders", |
|
4ce269c…
|
ragelink
|
58 |
"constance", |
|
4ce269c…
|
ragelink
|
59 |
"constance.backends.database", |
|
4ce269c…
|
ragelink
|
60 |
# Project apps |
|
4ce269c…
|
ragelink
|
61 |
"core", |
|
c588255…
|
ragelink
|
62 |
"accounts", |
|
4ce269c…
|
ragelink
|
63 |
"organization", |
|
4ce269c…
|
ragelink
|
64 |
"projects", |
|
4ce269c…
|
ragelink
|
65 |
"pages", |
|
4ce269c…
|
ragelink
|
66 |
"fossil", |
|
4ce269c…
|
ragelink
|
67 |
"testdata", |
|
4ce269c…
|
ragelink
|
68 |
] |
|
c588255…
|
ragelink
|
69 |
|
|
7a60a24…
|
ragelink
|
70 |
# Fossil sync pushes can be large (binary artifacts, images, etc.) |
|
6e2907c…
|
ragelink
|
71 |
DATA_UPLOAD_MAX_MEMORY_SIZE = 2 * 1024 * 1024 * 1024 # 2 GB |
|
7a60a24…
|
ragelink
|
72 |
|
|
4ce269c…
|
ragelink
|
73 |
MIDDLEWARE = [ |
|
4ce269c…
|
ragelink
|
74 |
"corsheaders.middleware.CorsMiddleware", |
|
4ce269c…
|
ragelink
|
75 |
"django.middleware.security.SecurityMiddleware", |
|
4ce269c…
|
ragelink
|
76 |
"whitenoise.middleware.WhiteNoiseMiddleware", |
|
4ce269c…
|
ragelink
|
77 |
"django.contrib.sessions.middleware.SessionMiddleware", |
|
4ce269c…
|
ragelink
|
78 |
"django.middleware.common.CommonMiddleware", |
|
4ce269c…
|
ragelink
|
79 |
"django.middleware.csrf.CsrfViewMiddleware", |
|
4ce269c…
|
ragelink
|
80 |
"django.contrib.auth.middleware.AuthenticationMiddleware", |
|
4ce269c…
|
ragelink
|
81 |
"django.contrib.messages.middleware.MessageMiddleware", |
|
4ce269c…
|
ragelink
|
82 |
"django.middleware.clickjacking.XFrameOptionsMiddleware", |
|
4ce269c…
|
ragelink
|
83 |
"simple_history.middleware.HistoryRequestMiddleware", |
|
4ce269c…
|
ragelink
|
84 |
"core.middleware.current_user.CurrentUserMiddleware", |
|
4ce269c…
|
ragelink
|
85 |
] |
|
4ce269c…
|
ragelink
|
86 |
|
|
4ce269c…
|
ragelink
|
87 |
TEMPLATES = [ |
|
4ce269c…
|
ragelink
|
88 |
{ |
|
4ce269c…
|
ragelink
|
89 |
"BACKEND": "django.template.backends.django.DjangoTemplates", |
|
4ce269c…
|
ragelink
|
90 |
"DIRS": [BASE_DIR / "templates"], |
|
4ce269c…
|
ragelink
|
91 |
"APP_DIRS": True, |
|
4ce269c…
|
ragelink
|
92 |
"OPTIONS": { |
|
4ce269c…
|
ragelink
|
93 |
"context_processors": [ |
|
4ce269c…
|
ragelink
|
94 |
"django.template.context_processors.debug", |
|
4ce269c…
|
ragelink
|
95 |
"django.template.context_processors.request", |
|
4ce269c…
|
ragelink
|
96 |
"django.contrib.auth.context_processors.auth", |
|
4ce269c…
|
ragelink
|
97 |
"django.contrib.messages.context_processors.messages", |
|
4ce269c…
|
ragelink
|
98 |
"core.context_processors.sidebar", |
|
4ce269c…
|
ragelink
|
99 |
], |
|
4ce269c…
|
ragelink
|
100 |
}, |
|
4ce269c…
|
ragelink
|
101 |
}, |
|
4ce269c…
|
ragelink
|
102 |
] |
|
4ce269c…
|
ragelink
|
103 |
|
|
4ce269c…
|
ragelink
|
104 |
# --- Database --- |
|
4ce269c…
|
ragelink
|
105 |
|
|
4ce269c…
|
ragelink
|
106 |
DATABASES = { |
|
4ce269c…
|
ragelink
|
107 |
"default": { |
|
4ce269c…
|
ragelink
|
108 |
"ENGINE": "django.db.backends.postgresql", |
|
4ce269c…
|
ragelink
|
109 |
"NAME": env_str("POSTGRES_DB", "fossilrepo"), |
|
4ce269c…
|
ragelink
|
110 |
"USER": env_str("POSTGRES_USER", "dbadmin"), |
|
4ce269c…
|
ragelink
|
111 |
"PASSWORD": env_str("POSTGRES_PASSWORD", "Password123"), |
|
4ce269c…
|
ragelink
|
112 |
"HOST": env_str("POSTGRES_HOST", "localhost"), |
|
4ce269c…
|
ragelink
|
113 |
"PORT": env_str("POSTGRES_PORT", "5432"), |
|
4ce269c…
|
ragelink
|
114 |
} |
|
4ce269c…
|
ragelink
|
115 |
} |
|
4ce269c…
|
ragelink
|
116 |
|
|
4ce269c…
|
ragelink
|
117 |
# --- Cache --- |
|
4ce269c…
|
ragelink
|
118 |
|
|
4ce269c…
|
ragelink
|
119 |
REDIS_URL = env_str("REDIS_URL", "redis://localhost:6379/1") |
|
4ce269c…
|
ragelink
|
120 |
|
|
4ce269c…
|
ragelink
|
121 |
CACHES = { |
|
4ce269c…
|
ragelink
|
122 |
"default": { |
|
4ce269c…
|
ragelink
|
123 |
"BACKEND": "django.core.cache.backends.redis.RedisCache", |
|
4ce269c…
|
ragelink
|
124 |
"LOCATION": REDIS_URL, |
|
4ce269c…
|
ragelink
|
125 |
} |
|
4ce269c…
|
ragelink
|
126 |
} |
|
4ce269c…
|
ragelink
|
127 |
|
|
4ce269c…
|
ragelink
|
128 |
# --- Auth --- |
|
4ce269c…
|
ragelink
|
129 |
|
|
4ce269c…
|
ragelink
|
130 |
AUTH_PASSWORD_VALIDATORS = [ |
|
4ce269c…
|
ragelink
|
131 |
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"}, |
|
4ce269c…
|
ragelink
|
132 |
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, |
|
4ce269c…
|
ragelink
|
133 |
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, |
|
4ce269c…
|
ragelink
|
134 |
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"}, |
|
4ce269c…
|
ragelink
|
135 |
] |
|
4ce269c…
|
ragelink
|
136 |
|
|
4ce269c…
|
ragelink
|
137 |
AUTHENTICATION_BACKENDS = ["django.contrib.auth.backends.ModelBackend"] |
|
4ce269c…
|
ragelink
|
138 |
SESSION_ENGINE = "django.contrib.sessions.backends.db" |
|
4ce269c…
|
ragelink
|
139 |
SESSION_COOKIE_HTTPONLY = True |
|
4ce269c…
|
ragelink
|
140 |
SESSION_COOKIE_AGE = 60 * 60 * 24 * 30 # 30 days |
|
4ce269c…
|
ragelink
|
141 |
CSRF_COOKIE_HTTPONLY = True |
|
4ce269c…
|
ragelink
|
142 |
|
|
4ce269c…
|
ragelink
|
143 |
if not DEBUG: |
|
7e1aaf6…
|
ragelink
|
144 |
SESSION_COOKIE_SECURE = env_bool("SESSION_COOKIE_SECURE", True) |
|
7e1aaf6…
|
ragelink
|
145 |
CSRF_COOKIE_SECURE = env_bool("CSRF_COOKIE_SECURE", True) |
|
7e1aaf6…
|
ragelink
|
146 |
SECURE_SSL_REDIRECT = env_bool("SECURE_SSL_REDIRECT", True) |
|
0da8377…
|
ragelink
|
147 |
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") |
|
0c354ac…
|
ragelink
|
148 |
SECURE_HSTS_SECONDS = 31536000 # 1 year |
|
0c354ac…
|
ragelink
|
149 |
SECURE_HSTS_INCLUDE_SUBDOMAINS = True |
|
0c354ac…
|
ragelink
|
150 |
SECURE_HSTS_PRELOAD = True |
|
4ce269c…
|
ragelink
|
151 |
|
|
4ce269c…
|
ragelink
|
152 |
# --- i18n --- |
|
4ce269c…
|
ragelink
|
153 |
|
|
4ce269c…
|
ragelink
|
154 |
LANGUAGE_CODE = "en-us" |
|
4ce269c…
|
ragelink
|
155 |
TIME_ZONE = "UTC" |
|
4ce269c…
|
ragelink
|
156 |
USE_I18N = True |
|
4ce269c…
|
ragelink
|
157 |
USE_TZ = True |
|
4ce269c…
|
ragelink
|
158 |
|
|
4ce269c…
|
ragelink
|
159 |
# --- Static --- |
|
4ce269c…
|
ragelink
|
160 |
|
|
4ce269c…
|
ragelink
|
161 |
STATIC_URL = "/static/" |
|
4ce269c…
|
ragelink
|
162 |
STATIC_ROOT = BASE_DIR / "assets" |
|
4ce269c…
|
ragelink
|
163 |
STATICFILES_DIRS = [BASE_DIR / "static"] |
|
4ce269c…
|
ragelink
|
164 |
STORAGES = { |
|
4ce269c…
|
ragelink
|
165 |
"staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"}, |
|
4ce269c…
|
ragelink
|
166 |
} |
|
4ce269c…
|
ragelink
|
167 |
|
|
4ce269c…
|
ragelink
|
168 |
# --- Media / S3 --- |
|
4ce269c…
|
ragelink
|
169 |
|
|
4ce269c…
|
ragelink
|
170 |
USE_S3 = env_bool("USE_S3", False) |
|
4ce269c…
|
ragelink
|
171 |
|
|
4ce269c…
|
ragelink
|
172 |
if USE_S3: |
|
4ce269c…
|
ragelink
|
173 |
STORAGES["default"] = {"BACKEND": "storages.backends.s3boto3.S3Boto3Storage"} |
|
4ce269c…
|
ragelink
|
174 |
AWS_ACCESS_KEY_ID = env_str("AWS_ACCESS_KEY_ID") |
|
4ce269c…
|
ragelink
|
175 |
AWS_SECRET_ACCESS_KEY = env_str("AWS_SECRET_ACCESS_KEY") |
|
4ce269c…
|
ragelink
|
176 |
AWS_STORAGE_BUCKET_NAME = env_str("AWS_STORAGE_BUCKET_NAME", "fossilrepo") |
|
4ce269c…
|
ragelink
|
177 |
AWS_S3_ENDPOINT_URL = env_str("AWS_S3_ENDPOINT_URL", "") |
|
4ce269c…
|
ragelink
|
178 |
AWS_S3_FILE_OVERWRITE = False |
|
4ce269c…
|
ragelink
|
179 |
AWS_QUERYSTRING_AUTH = True |
|
4ce269c…
|
ragelink
|
180 |
else: |
|
0cab6ed…
|
ragelink
|
181 |
STORAGES["default"] = {"BACKEND": "django.core.files.storage.FileSystemStorage"} |
|
4ce269c…
|
ragelink
|
182 |
MEDIA_URL = "/media/" |
|
4ce269c…
|
ragelink
|
183 |
MEDIA_ROOT = BASE_DIR / "media" |
|
4ce269c…
|
ragelink
|
184 |
|
|
4ce269c…
|
ragelink
|
185 |
# --- Email --- |
|
4ce269c…
|
ragelink
|
186 |
|
|
4ce269c…
|
ragelink
|
187 |
EMAIL_BACKEND = env_str("DJANGO_EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend") |
|
4ce269c…
|
ragelink
|
188 |
EMAIL_HOST = env_str("EMAIL_HOST", "localhost") |
|
4ce269c…
|
ragelink
|
189 |
EMAIL_PORT = env_int("EMAIL_PORT", 1025) |
|
4ce269c…
|
ragelink
|
190 |
DEFAULT_FROM_EMAIL = env_str("FROM_EMAIL", "[email protected]") |
|
4ce269c…
|
ragelink
|
191 |
|
|
4ce269c…
|
ragelink
|
192 |
# --- Celery --- |
|
4ce269c…
|
ragelink
|
193 |
|
|
4ce269c…
|
ragelink
|
194 |
CELERY_BROKER_URL = env_str("CELERY_BROKER", "redis://localhost:6379/0") |
|
4ce269c…
|
ragelink
|
195 |
CELERY_RESULT_BACKEND = "django-db" |
|
4ce269c…
|
ragelink
|
196 |
CELERY_TASK_TRACK_STARTED = True |
|
4ce269c…
|
ragelink
|
197 |
CELERY_TASK_TIME_LIMIT = 3600 |
|
4ce269c…
|
ragelink
|
198 |
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler" |
|
4ce269c…
|
ragelink
|
199 |
CELERY_BEAT_SCHEDULE = { |
|
4ce269c…
|
ragelink
|
200 |
"fossil-sync-metadata": { |
|
4ce269c…
|
ragelink
|
201 |
"task": "fossil.sync_metadata", |
|
4ce269c…
|
ragelink
|
202 |
"schedule": 300.0, # every 5 minutes |
|
4ce269c…
|
ragelink
|
203 |
}, |
|
4ce269c…
|
ragelink
|
204 |
"fossil-check-upstream": { |
|
4ce269c…
|
ragelink
|
205 |
"task": "fossil.check_upstream", |
|
4ce269c…
|
ragelink
|
206 |
"schedule": 900.0, # every 15 minutes |
|
0da8377…
|
ragelink
|
207 |
}, |
|
0da8377…
|
ragelink
|
208 |
"fossil-dispatch-notifications": { |
|
0da8377…
|
ragelink
|
209 |
"task": "fossil.dispatch_notifications", |
|
0da8377…
|
ragelink
|
210 |
"schedule": 300.0, # every 5 minutes |
|
c588255…
|
ragelink
|
211 |
}, |
|
c588255…
|
ragelink
|
212 |
"fossil-daily-digest": { |
|
c588255…
|
ragelink
|
213 |
"task": "fossil.send_digest", |
|
c588255…
|
ragelink
|
214 |
"schedule": 86400.0, # daily |
|
c588255…
|
ragelink
|
215 |
"kwargs": {"mode": "daily"}, |
|
c588255…
|
ragelink
|
216 |
}, |
|
c588255…
|
ragelink
|
217 |
"fossil-weekly-digest": { |
|
c588255…
|
ragelink
|
218 |
"task": "fossil.send_digest", |
|
c588255…
|
ragelink
|
219 |
"schedule": 604800.0, # weekly |
|
c588255…
|
ragelink
|
220 |
"kwargs": {"mode": "weekly"}, |
|
2eca4eb…
|
ragelink
|
221 |
}, |
|
4ce269c…
|
ragelink
|
222 |
} |
|
4ce269c…
|
ragelink
|
223 |
CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True |
|
4ce269c…
|
ragelink
|
224 |
|
|
4ce269c…
|
ragelink
|
225 |
# --- CORS --- |
|
4ce269c…
|
ragelink
|
226 |
|
|
4ce269c…
|
ragelink
|
227 |
CORS_ALLOW_CREDENTIALS = True |
|
4ce269c…
|
ragelink
|
228 |
CORS_ALLOWED_ORIGINS = [o.strip() for o in env_str("CORS_ALLOWED_ORIGINS", "http://localhost:8000").split(",")] |
|
4ce269c…
|
ragelink
|
229 |
|
|
4ce269c…
|
ragelink
|
230 |
CSRF_TRUSTED_ORIGINS = [o.strip() for o in env_str("CSRF_TRUSTED_ORIGINS", "http://localhost:8000").split(",")] |
|
4ce269c…
|
ragelink
|
231 |
|
|
4ce269c…
|
ragelink
|
232 |
# --- Rate limiting --- |
|
4ce269c…
|
ragelink
|
233 |
|
|
4ce269c…
|
ragelink
|
234 |
RATELIMIT_VIEW = "django.views.defaults.permission_denied" |
|
4ce269c…
|
ragelink
|
235 |
|
|
4ce269c…
|
ragelink
|
236 |
# --- Constance (runtime feature toggles) --- |
|
4ce269c…
|
ragelink
|
237 |
|
|
4ce269c…
|
ragelink
|
238 |
CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend" |
|
4ce269c…
|
ragelink
|
239 |
CONSTANCE_CONFIG = { |
|
4ce269c…
|
ragelink
|
240 |
"SITE_NAME": ("Fossilrepo", "Display name for the site"), |
|
4ce269c…
|
ragelink
|
241 |
"FOSSIL_DATA_DIR": ("/data/repos", "Directory where .fossil repository files are stored"), |
|
4ce269c…
|
ragelink
|
242 |
"FOSSIL_STORE_IN_DB": (False, "Store binary snapshots of .fossil files via Django file storage"), |
|
4ce269c…
|
ragelink
|
243 |
"FOSSIL_S3_TRACKING": (False, "Track S3/Litestream replication keys and versions"), |
|
4ce269c…
|
ragelink
|
244 |
"FOSSIL_S3_BUCKET": ("", "S3 bucket name for Fossil repo replication"), |
|
4ce269c…
|
ragelink
|
245 |
"FOSSIL_BINARY_PATH": ("fossil", "Path to the fossil binary"), |
|
2eca4eb…
|
ragelink
|
246 |
# Git sync settings |
|
2eca4eb…
|
ragelink
|
247 |
"GIT_SYNC_MODE": ("disabled", "Default sync mode: disabled, on_change, scheduled, both"), |
|
2eca4eb…
|
ragelink
|
248 |
"GIT_SYNC_SCHEDULE": ("*/15 * * * *", "Default cron schedule for Git sync"), |
|
2eca4eb…
|
ragelink
|
249 |
"GIT_MIRROR_DIR": ("/data/git-mirrors", "Directory for Git mirror checkouts"), |
|
2eca4eb…
|
ragelink
|
250 |
"GIT_SSH_KEY_DIR": ("/data/ssh-keys", "Directory for SSH key storage"), |
|
2eca4eb…
|
ragelink
|
251 |
"GITHUB_OAUTH_CLIENT_ID": ("", "GitHub OAuth App Client ID"), |
|
2eca4eb…
|
ragelink
|
252 |
"GITHUB_OAUTH_CLIENT_SECRET": ("", "GitHub OAuth App Client Secret"), |
|
2eca4eb…
|
ragelink
|
253 |
"GITLAB_OAUTH_CLIENT_ID": ("", "GitLab OAuth App Client ID"), |
|
2eca4eb…
|
ragelink
|
254 |
"GITLAB_OAUTH_CLIENT_SECRET": ("", "GitLab OAuth App Client Secret"), |
|
0c354ac…
|
ragelink
|
255 |
# Cloudflare Turnstile (optional bot protection on login) |
|
0c354ac…
|
ragelink
|
256 |
"TURNSTILE_ENABLED": (False, "Enable Cloudflare Turnstile on the login page"), |
|
0c354ac…
|
ragelink
|
257 |
"TURNSTILE_SITE_KEY": ("", "Cloudflare Turnstile site key (public)"), |
|
0c354ac…
|
ragelink
|
258 |
"TURNSTILE_SECRET_KEY": ("", "Cloudflare Turnstile secret key (server-side verification)"), |
|
4ce269c…
|
ragelink
|
259 |
} |
|
4ce269c…
|
ragelink
|
260 |
CONSTANCE_CONFIG_FIELDSETS = { |
|
4ce269c…
|
ragelink
|
261 |
"General": ("SITE_NAME",), |
|
4ce269c…
|
ragelink
|
262 |
"Fossil Storage": ("FOSSIL_DATA_DIR", "FOSSIL_STORE_IN_DB", "FOSSIL_S3_TRACKING", "FOSSIL_S3_BUCKET", "FOSSIL_BINARY_PATH"), |
|
2eca4eb…
|
ragelink
|
263 |
"Git Sync": ("GIT_SYNC_MODE", "GIT_SYNC_SCHEDULE", "GIT_MIRROR_DIR", "GIT_SSH_KEY_DIR"), |
|
2eca4eb…
|
ragelink
|
264 |
"GitHub OAuth": ("GITHUB_OAUTH_CLIENT_ID", "GITHUB_OAUTH_CLIENT_SECRET"), |
|
2eca4eb…
|
ragelink
|
265 |
"GitLab OAuth": ("GITLAB_OAUTH_CLIENT_ID", "GITLAB_OAUTH_CLIENT_SECRET"), |
|
0c354ac…
|
ragelink
|
266 |
"Cloudflare Turnstile": ("TURNSTILE_ENABLED", "TURNSTILE_SITE_KEY", "TURNSTILE_SECRET_KEY"), |
|
4ce269c…
|
ragelink
|
267 |
} |
|
4ce269c…
|
ragelink
|
268 |
|
|
4ce269c…
|
ragelink
|
269 |
# --- Sentry --- |
|
4ce269c…
|
ragelink
|
270 |
|
|
4ce269c…
|
ragelink
|
271 |
SENTRY_DSN = env_str("SENTRY_DSN") |
|
4ce269c…
|
ragelink
|
272 |
if SENTRY_DSN: |
|
4ce269c…
|
ragelink
|
273 |
import sentry_sdk |
|
4ce269c…
|
ragelink
|
274 |
|
|
4ce269c…
|
ragelink
|
275 |
sentry_sdk.init(dsn=SENTRY_DSN, traces_sample_rate=0.1) |
|
4ce269c…
|
ragelink
|
276 |
|
|
4ce269c…
|
ragelink
|
277 |
# --- Logging --- |
|
4ce269c…
|
ragelink
|
278 |
|
|
4ce269c…
|
ragelink
|
279 |
LOGGING = { |
|
4ce269c…
|
ragelink
|
280 |
"version": 1, |
|
4ce269c…
|
ragelink
|
281 |
"disable_existing_loggers": False, |
|
4ce269c…
|
ragelink
|
282 |
"handlers": {"console": {"class": "logging.StreamHandler"}}, |
|
4ce269c…
|
ragelink
|
283 |
"root": {"handlers": ["console"], "level": "INFO"}, |
|
4ce269c…
|
ragelink
|
284 |
"loggers": { |
|
4ce269c…
|
ragelink
|
285 |
"django": {"handlers": ["console"], "level": "INFO", "propagate": False}, |
|
4ce269c…
|
ragelink
|
286 |
}, |
|
4ce269c…
|
ragelink
|
287 |
} |
|
4ce269c…
|
ragelink
|
288 |
|
|
4ce269c…
|
ragelink
|
289 |
# --- Import/Export --- |
|
4ce269c…
|
ragelink
|
290 |
|
|
4ce269c…
|
ragelink
|
291 |
IMPORT_FORMATS = [] |
|
4ce269c…
|
ragelink
|
292 |
EXPORT_FORMATS = [] |
|
4ce269c…
|
ragelink
|
293 |
|
|
4ce269c…
|
ragelink
|
294 |
# --- Admin --- |
|
4ce269c…
|
ragelink
|
295 |
|
|
4ce269c…
|
ragelink
|
296 |
ADMIN_SITE_HEADER = "Fossilrepo" |
|
4ce269c…
|
ragelink
|
297 |
ADMIN_SITE_TITLE = f"Fossilrepo Admin {VERSION}" |