settings.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. __package__ = 'archivebox.core'
  2. import os
  3. import sys
  4. import re
  5. import logging
  6. from pathlib import Path
  7. from datetime import datetime
  8. from django.utils.crypto import get_random_string
  9. from ..config import ( # noqa: F401
  10. DEBUG,
  11. IS_TTY,
  12. VERSION,
  13. IN_DOCKER,
  14. SECRET_KEY,
  15. ALLOWED_HOSTS,
  16. PACKAGE_DIR,
  17. TEMPLATES_DIR_NAME,
  18. SQL_INDEX_FILENAME,
  19. OUTPUT_DIR,
  20. LOGS_DIR,
  21. )
  22. IS_MIGRATING = 'makemigrations' in sys.argv[:3] or 'migrate' in sys.argv[:3]
  23. IS_TESTING = 'test' in sys.argv[:3] or 'PYTEST_CURRENT_TEST' in os.environ
  24. IS_SHELL = 'shell' in sys.argv[:3] or 'shell_plus' in sys.argv[:3]
  25. ################################################################################
  26. ### Django Core Settings
  27. ################################################################################
  28. WSGI_APPLICATION = 'core.wsgi.application'
  29. ROOT_URLCONF = 'core.urls'
  30. LOGIN_URL = '/accounts/login/'
  31. LOGOUT_REDIRECT_URL = '/'
  32. PASSWORD_RESET_URL = '/accounts/password_reset/'
  33. APPEND_SLASH = True
  34. DEBUG = DEBUG or ('--debug' in sys.argv)
  35. INSTALLED_APPS = [
  36. 'django.contrib.auth',
  37. 'django.contrib.contenttypes',
  38. 'django.contrib.sessions',
  39. 'django.contrib.messages',
  40. 'django.contrib.staticfiles',
  41. 'django.contrib.admin',
  42. 'core',
  43. 'django_extensions',
  44. ]
  45. MIDDLEWARE = [
  46. 'django.middleware.security.SecurityMiddleware',
  47. 'django.contrib.sessions.middleware.SessionMiddleware',
  48. 'django.middleware.common.CommonMiddleware',
  49. 'django.middleware.csrf.CsrfViewMiddleware',
  50. 'django.contrib.auth.middleware.AuthenticationMiddleware',
  51. 'django.contrib.messages.middleware.MessageMiddleware',
  52. ]
  53. AUTHENTICATION_BACKENDS = [
  54. 'django.contrib.auth.backends.ModelBackend',
  55. ]
  56. ################################################################################
  57. ### Staticfile and Template Settings
  58. ################################################################################
  59. STATIC_URL = '/static/'
  60. STATICFILES_DIRS = [
  61. str(Path(PACKAGE_DIR) / TEMPLATES_DIR_NAME / 'static'),
  62. ]
  63. TEMPLATE_DIRS = [
  64. str(Path(PACKAGE_DIR) / TEMPLATES_DIR_NAME / 'core'),
  65. str(Path(PACKAGE_DIR) / TEMPLATES_DIR_NAME / 'admin'),
  66. str(Path(PACKAGE_DIR) / TEMPLATES_DIR_NAME),
  67. ]
  68. TEMPLATES = [
  69. {
  70. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  71. 'DIRS': TEMPLATE_DIRS,
  72. 'APP_DIRS': True,
  73. 'OPTIONS': {
  74. 'context_processors': [
  75. 'django.template.context_processors.debug',
  76. 'django.template.context_processors.request',
  77. 'django.contrib.auth.context_processors.auth',
  78. 'django.contrib.messages.context_processors.messages',
  79. ],
  80. },
  81. },
  82. ]
  83. ################################################################################
  84. ### External Service Settings
  85. ################################################################################
  86. DATABASE_FILE = Path(OUTPUT_DIR) / SQL_INDEX_FILENAME
  87. DATABASE_NAME = os.environ.get("ARCHIVEBOX_DATABASE_NAME", str(DATABASE_FILE))
  88. DATABASES = {
  89. 'default': {
  90. 'ENGINE': 'django.db.backends.sqlite3',
  91. 'NAME': DATABASE_NAME,
  92. # modified to be in-memory or sqlite3+wal by setup_django() in config.py
  93. }
  94. }
  95. EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
  96. ################################################################################
  97. ### Security Settings
  98. ################################################################################
  99. SECRET_KEY = SECRET_KEY or get_random_string(50, 'abcdefghijklmnopqrstuvwxyz0123456789_')
  100. ALLOWED_HOSTS = ALLOWED_HOSTS.split(',')
  101. SECURE_BROWSER_XSS_FILTER = True
  102. SECURE_CONTENT_TYPE_NOSNIFF = True
  103. CSRF_COOKIE_SECURE = False
  104. SESSION_COOKIE_SECURE = False
  105. SESSION_COOKIE_DOMAIN = None
  106. SESSION_COOKIE_AGE = 1209600 # 2 weeks
  107. SESSION_EXPIRE_AT_BROWSER_CLOSE = False
  108. SESSION_SAVE_EVERY_REQUEST = True
  109. AUTH_PASSWORD_VALIDATORS = [
  110. {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
  111. {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
  112. {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
  113. {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
  114. ]
  115. ################################################################################
  116. ### Shell Settings
  117. ################################################################################
  118. SHELL_PLUS = 'ipython'
  119. SHELL_PLUS_PRINT_SQL = False
  120. IPYTHON_ARGUMENTS = ['--no-confirm-exit', '--no-banner']
  121. IPYTHON_KERNEL_DISPLAY_NAME = 'ArchiveBox Django Shell'
  122. if IS_SHELL:
  123. os.environ['PYTHONSTARTUP'] = str(Path(PACKAGE_DIR) / 'core' / 'welcome_message.py')
  124. ################################################################################
  125. ### Internationalization & Localization Settings
  126. ################################################################################
  127. LANGUAGE_CODE = 'en-us'
  128. TIME_ZONE = 'UTC'
  129. USE_I18N = False
  130. USE_L10N = False
  131. USE_TZ = False
  132. DATETIME_FORMAT = 'Y-m-d g:iA'
  133. SHORT_DATETIME_FORMAT = 'Y-m-d h:iA'
  134. ################################################################################
  135. ### Logging Settings
  136. ################################################################################
  137. IGNORABLE_404_URLS = [
  138. re.compile(r'apple-touch-icon.*\.png$'),
  139. re.compile(r'favicon\.ico$'),
  140. re.compile(r'robots\.txt$'),
  141. re.compile(r'.*\.(css|js)\.map$'),
  142. ]
  143. class NoisyRequestsFilter(logging.Filter):
  144. def filter(self, record):
  145. logline = record.getMessage()
  146. # ignore harmless 404s for the patterns in IGNORABLE_404_URLS
  147. for ignorable_url_pattern in IGNORABLE_404_URLS:
  148. ignorable_log_pattern = re.compile(f'^"GET /.*/?{ignorable_url_pattern.pattern[:-1]} HTTP/.*" (200|30.|404) .+$', re.I | re.M)
  149. if ignorable_log_pattern.match(logline):
  150. return 0
  151. # ignore staticfile requests that 200 or 30*
  152. ignoreable_200_log_pattern = re.compile(r'"GET /static/.* HTTP/.*" (200|30.) .+', re.I | re.M)
  153. if ignoreable_200_log_pattern.match(logline):
  154. return 0
  155. return 1
  156. ERROR_LOG = LOGS_DIR / 'errors.log'
  157. LOGGING = {
  158. 'version': 1,
  159. 'disable_existing_loggers': False,
  160. 'handlers': {
  161. 'console': {
  162. 'class': 'logging.StreamHandler',
  163. },
  164. 'logfile': {
  165. 'level': 'ERROR',
  166. 'class': 'logging.handlers.RotatingFileHandler',
  167. 'filename': ERROR_LOG,
  168. 'maxBytes': 1024 * 1024 * 25, # 25 MB
  169. 'backupCount': 10,
  170. },
  171. },
  172. 'filters': {
  173. 'noisyrequestsfilter': {
  174. '()': NoisyRequestsFilter,
  175. }
  176. },
  177. 'loggers': {
  178. 'django': {
  179. 'handlers': ['console', 'logfile'],
  180. 'level': 'DEBUG',
  181. 'filters': ['noisyrequestsfilter'],
  182. },
  183. 'django.server': {
  184. 'handlers': ['console', 'logfile'],
  185. 'level': 'DEBUG',
  186. 'filters': ['noisyrequestsfilter'],
  187. }
  188. },
  189. }
  190. # log startup message to the error log
  191. with open(ERROR_LOG, "a+") as f:
  192. command = ' '.join(sys.argv)
  193. ts = datetime.now().strftime('%Y-%m-%d__%H:%M:%S')
  194. f.write(f"\n> {command}; ts={ts} version={VERSION} docker={IN_DOCKER} is_tty={IS_TTY}\n")