mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-30 18:27:45 -05:00
Format Python code with black
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
# Fetch Django ASGI application early to ensure AppRegistry is populated
|
||||
# before importing consumers and AuthMiddlewareStack that may import ORM
|
||||
# models.
|
||||
@@ -13,11 +14,9 @@ from channels.routing import ProtocolTypeRouter, URLRouter # NOQA: E402
|
||||
|
||||
from paperless.urls import websocket_urlpatterns # NOQA: E402
|
||||
|
||||
application = ProtocolTypeRouter({
|
||||
"http": get_asgi_application(),
|
||||
"websocket": AuthMiddlewareStack(
|
||||
URLRouter(
|
||||
websocket_urlpatterns
|
||||
)
|
||||
),
|
||||
})
|
||||
application = ProtocolTypeRouter(
|
||||
{
|
||||
"http": get_asgi_application(),
|
||||
"websocket": AuthMiddlewareStack(URLRouter(websocket_urlpatterns)),
|
||||
}
|
||||
)
|
||||
|
@@ -7,23 +7,25 @@ from django.contrib.auth.middleware import RemoteUserMiddleware
|
||||
|
||||
|
||||
class AutoLoginMiddleware(MiddlewareMixin):
|
||||
|
||||
def process_request(self, request):
|
||||
try:
|
||||
request.user = User.objects.get(
|
||||
username=settings.AUTO_LOGIN_USERNAME)
|
||||
request.user = User.objects.get(username=settings.AUTO_LOGIN_USERNAME)
|
||||
auth.login(request, request.user)
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
|
||||
|
||||
class AngularApiAuthenticationOverride(authentication.BaseAuthentication):
|
||||
""" This class is here to provide authentication to the angular dev server
|
||||
during development. This is disabled in production.
|
||||
"""This class is here to provide authentication to the angular dev server
|
||||
during development. This is disabled in production.
|
||||
"""
|
||||
|
||||
def authenticate(self, request):
|
||||
if settings.DEBUG and 'Referer' in request.headers and request.headers['Referer'].startswith('http://localhost:4200/'): # NOQA: E501
|
||||
if (
|
||||
settings.DEBUG
|
||||
and "Referer" in request.headers
|
||||
and request.headers["Referer"].startswith("http://localhost:4200/")
|
||||
): # NOQA: E501
|
||||
user = User.objects.filter(is_staff=True).first()
|
||||
print("Auto-Login with user {}".format(user))
|
||||
return (user, None)
|
||||
@@ -32,7 +34,8 @@ class AngularApiAuthenticationOverride(authentication.BaseAuthentication):
|
||||
|
||||
|
||||
class HttpRemoteUserMiddleware(RemoteUserMiddleware):
|
||||
""" This class allows authentication via HTTP_REMOTE_USER which is set for
|
||||
example by certain SSO applications.
|
||||
"""This class allows authentication via HTTP_REMOTE_USER which is set for
|
||||
example by certain SSO applications.
|
||||
"""
|
||||
|
||||
header = settings.HTTP_REMOTE_USER_HEADER_NAME
|
||||
|
@@ -18,24 +18,26 @@ def path_check(var, directory):
|
||||
messages = []
|
||||
if directory:
|
||||
if not os.path.isdir(directory):
|
||||
messages.append(Error(
|
||||
exists_message.format(var),
|
||||
exists_hint.format(directory)
|
||||
))
|
||||
messages.append(
|
||||
Error(exists_message.format(var), exists_hint.format(directory))
|
||||
)
|
||||
else:
|
||||
test_file = os.path.join(
|
||||
directory, f'__paperless_write_test_{os.getpid()}__'
|
||||
directory, f"__paperless_write_test_{os.getpid()}__"
|
||||
)
|
||||
try:
|
||||
with open(test_file, 'w'):
|
||||
with open(test_file, "w"):
|
||||
pass
|
||||
except PermissionError:
|
||||
messages.append(Error(
|
||||
writeable_message.format(var),
|
||||
writeable_hint.format(
|
||||
f'\n{stat.filemode(os.stat(directory).st_mode)} '
|
||||
f'{directory}\n')
|
||||
))
|
||||
messages.append(
|
||||
Error(
|
||||
writeable_message.format(var),
|
||||
writeable_hint.format(
|
||||
f"\n{stat.filemode(os.stat(directory).st_mode)} "
|
||||
f"{directory}\n"
|
||||
),
|
||||
)
|
||||
)
|
||||
finally:
|
||||
if os.path.isfile(test_file):
|
||||
os.remove(test_file)
|
||||
@@ -49,10 +51,12 @@ def paths_check(app_configs, **kwargs):
|
||||
Check the various paths for existence, readability and writeability
|
||||
"""
|
||||
|
||||
return path_check("PAPERLESS_DATA_DIR", settings.DATA_DIR) + \
|
||||
path_check("PAPERLESS_TRASH_DIR", settings.TRASH_DIR) + \
|
||||
path_check("PAPERLESS_MEDIA_ROOT", settings.MEDIA_ROOT) + \
|
||||
path_check("PAPERLESS_CONSUMPTION_DIR", settings.CONSUMPTION_DIR)
|
||||
return (
|
||||
path_check("PAPERLESS_DATA_DIR", settings.DATA_DIR)
|
||||
+ path_check("PAPERLESS_TRASH_DIR", settings.TRASH_DIR)
|
||||
+ path_check("PAPERLESS_MEDIA_ROOT", settings.MEDIA_ROOT)
|
||||
+ path_check("PAPERLESS_CONSUMPTION_DIR", settings.CONSUMPTION_DIR)
|
||||
)
|
||||
|
||||
|
||||
@register()
|
||||
@@ -65,11 +69,7 @@ def binaries_check(app_configs, **kwargs):
|
||||
error = "Paperless can't find {}. Without it, consumption is impossible."
|
||||
hint = "Either it's not in your ${PATH} or it's not installed."
|
||||
|
||||
binaries = (
|
||||
settings.CONVERT_BINARY,
|
||||
settings.OPTIPNG_BINARY,
|
||||
"tesseract"
|
||||
)
|
||||
binaries = (settings.CONVERT_BINARY, settings.OPTIPNG_BINARY, "tesseract")
|
||||
|
||||
check_messages = []
|
||||
for binary in binaries:
|
||||
@@ -82,11 +82,14 @@ def binaries_check(app_configs, **kwargs):
|
||||
@register()
|
||||
def debug_mode_check(app_configs, **kwargs):
|
||||
if settings.DEBUG:
|
||||
return [Warning(
|
||||
"DEBUG mode is enabled. Disable Debug mode. This is a serious "
|
||||
"security issue, since it puts security overides in place which "
|
||||
"are meant to be only used during development. This "
|
||||
"also means that paperless will tell anyone various "
|
||||
"debugging information when something goes wrong.")]
|
||||
return [
|
||||
Warning(
|
||||
"DEBUG mode is enabled. Disable Debug mode. This is a serious "
|
||||
"security issue, since it puts security overides in place which "
|
||||
"are meant to be only used during development. This "
|
||||
"also means that paperless will tell anyone various "
|
||||
"debugging information when something goes wrong."
|
||||
)
|
||||
]
|
||||
else:
|
||||
return []
|
||||
|
@@ -6,24 +6,25 @@ from channels.generic.websocket import WebsocketConsumer
|
||||
|
||||
|
||||
class StatusConsumer(WebsocketConsumer):
|
||||
|
||||
def _authenticated(self):
|
||||
return 'user' in self.scope and self.scope['user'].is_authenticated
|
||||
return "user" in self.scope and self.scope["user"].is_authenticated
|
||||
|
||||
def connect(self):
|
||||
if not self._authenticated():
|
||||
raise DenyConnection()
|
||||
else:
|
||||
async_to_sync(self.channel_layer.group_add)(
|
||||
'status_updates', self.channel_name)
|
||||
"status_updates", self.channel_name
|
||||
)
|
||||
raise AcceptConnection()
|
||||
|
||||
def disconnect(self, close_code):
|
||||
async_to_sync(self.channel_layer.group_discard)(
|
||||
'status_updates', self.channel_name)
|
||||
"status_updates", self.channel_name
|
||||
)
|
||||
|
||||
def status_update(self, event):
|
||||
if not self._authenticated():
|
||||
self.close()
|
||||
else:
|
||||
self.send(json.dumps(event['data']))
|
||||
self.send(json.dumps(event["data"]))
|
||||
|
@@ -4,17 +4,14 @@ from paperless import version
|
||||
|
||||
|
||||
class ApiVersionMiddleware:
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
if request.user.is_authenticated:
|
||||
versions = settings.REST_FRAMEWORK['ALLOWED_VERSIONS']
|
||||
response['X-Api-Version'] = versions[len(versions)-1]
|
||||
response['X-Version'] = ".".join(
|
||||
[str(_) for _ in version.__version__]
|
||||
)
|
||||
versions = settings.REST_FRAMEWORK["ALLOWED_VERSIONS"]
|
||||
response["X-Api-Version"] = versions[len(versions) - 1]
|
||||
response["X-Version"] = ".".join([str(_) for _ in version.__version__])
|
||||
|
||||
return response
|
||||
|
@@ -27,7 +27,7 @@ elif os.path.exists("/usr/local/etc/paperless.conf"):
|
||||
# OCR threads may exceed the number of available cpu cores, which will
|
||||
# dramatically slow down the consumption process. This settings limits each
|
||||
# Tesseract process to one thread.
|
||||
os.environ['OMP_THREAD_LIMIT'] = "1"
|
||||
os.environ["OMP_THREAD_LIMIT"] = "1"
|
||||
|
||||
|
||||
def __get_boolean(key, default="NO"):
|
||||
@@ -50,14 +50,14 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
STATIC_ROOT = os.getenv("PAPERLESS_STATICDIR", os.path.join(BASE_DIR, "..", "static"))
|
||||
|
||||
MEDIA_ROOT = os.getenv('PAPERLESS_MEDIA_ROOT', os.path.join(BASE_DIR, "..", "media"))
|
||||
MEDIA_ROOT = os.getenv("PAPERLESS_MEDIA_ROOT", os.path.join(BASE_DIR, "..", "media"))
|
||||
ORIGINALS_DIR = os.path.join(MEDIA_ROOT, "documents", "originals")
|
||||
ARCHIVE_DIR = os.path.join(MEDIA_ROOT, "documents", "archive")
|
||||
THUMBNAIL_DIR = os.path.join(MEDIA_ROOT, "documents", "thumbnails")
|
||||
|
||||
DATA_DIR = os.getenv('PAPERLESS_DATA_DIR', os.path.join(BASE_DIR, "..", "data"))
|
||||
DATA_DIR = os.getenv("PAPERLESS_DATA_DIR", os.path.join(BASE_DIR, "..", "data"))
|
||||
|
||||
TRASH_DIR = os.getenv('PAPERLESS_TRASH_DIR')
|
||||
TRASH_DIR = os.getenv("PAPERLESS_TRASH_DIR")
|
||||
|
||||
# Lock file for synchronizing changes to the MEDIA directory across multiple
|
||||
# threads.
|
||||
@@ -65,9 +65,11 @@ MEDIA_LOCK = os.path.join(MEDIA_ROOT, "media.lock")
|
||||
INDEX_DIR = os.path.join(DATA_DIR, "index")
|
||||
MODEL_FILE = os.path.join(DATA_DIR, "classification_model.pickle")
|
||||
|
||||
LOGGING_DIR = os.getenv('PAPERLESS_LOGGING_DIR', os.path.join(DATA_DIR, "log"))
|
||||
LOGGING_DIR = os.getenv("PAPERLESS_LOGGING_DIR", os.path.join(DATA_DIR, "log"))
|
||||
|
||||
CONSUMPTION_DIR = os.getenv("PAPERLESS_CONSUMPTION_DIR", os.path.join(BASE_DIR, "..", "consume"))
|
||||
CONSUMPTION_DIR = os.getenv(
|
||||
"PAPERLESS_CONSUMPTION_DIR", os.path.join(BASE_DIR, "..", "consume")
|
||||
)
|
||||
|
||||
# This will be created if it doesn't exist
|
||||
SCRATCH_DIR = os.getenv("PAPERLESS_SCRATCH_DIR", "/tmp/paperless")
|
||||
@@ -80,75 +82,68 @@ env_apps = os.getenv("PAPERLESS_APPS").split(",") if os.getenv("PAPERLESS_APPS")
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"whitenoise.runserver_nostatic",
|
||||
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
|
||||
"corsheaders",
|
||||
"django_extensions",
|
||||
|
||||
"paperless",
|
||||
"documents.apps.DocumentsConfig",
|
||||
"paperless_tesseract.apps.PaperlessTesseractConfig",
|
||||
"paperless_text.apps.PaperlessTextConfig",
|
||||
"paperless_mail.apps.PaperlessMailConfig",
|
||||
|
||||
"django.contrib.admin",
|
||||
|
||||
"rest_framework",
|
||||
"rest_framework.authtoken",
|
||||
"django_filters",
|
||||
|
||||
"django_q",
|
||||
|
||||
] + env_apps
|
||||
|
||||
if DEBUG:
|
||||
INSTALLED_APPS.append("channels")
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||
'rest_framework.authentication.BasicAuthentication',
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
'rest_framework.authentication.TokenAuthentication'
|
||||
"DEFAULT_AUTHENTICATION_CLASSES": [
|
||||
"rest_framework.authentication.BasicAuthentication",
|
||||
"rest_framework.authentication.SessionAuthentication",
|
||||
"rest_framework.authentication.TokenAuthentication",
|
||||
],
|
||||
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.AcceptHeaderVersioning',
|
||||
'DEFAULT_VERSION': '1',
|
||||
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.AcceptHeaderVersioning",
|
||||
"DEFAULT_VERSION": "1",
|
||||
# Make sure these are ordered and that the most recent version appears
|
||||
# last
|
||||
'ALLOWED_VERSIONS': ['1', '2']
|
||||
"ALLOWED_VERSIONS": ["1", "2"],
|
||||
}
|
||||
|
||||
if DEBUG:
|
||||
REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'].append(
|
||||
'paperless.auth.AngularApiAuthenticationOverride'
|
||||
REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"].append(
|
||||
"paperless.auth.AngularApiAuthenticationOverride"
|
||||
)
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'paperless.middleware.ApiVersionMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"whitenoise.middleware.WhiteNoiseMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"corsheaders.middleware.CorsMiddleware",
|
||||
"django.middleware.locale.LocaleMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"paperless.middleware.ApiVersionMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'paperless.urls'
|
||||
ROOT_URLCONF = "paperless.urls"
|
||||
|
||||
FORCE_SCRIPT_NAME = os.getenv("PAPERLESS_FORCE_SCRIPT_NAME")
|
||||
BASE_URL = (FORCE_SCRIPT_NAME or "") + "/"
|
||||
LOGIN_URL = BASE_URL + "accounts/login/"
|
||||
LOGOUT_REDIRECT_URL = os.getenv("PAPERLESS_LOGOUT_REDIRECT_URL")
|
||||
|
||||
WSGI_APPLICATION = 'paperless.wsgi.application'
|
||||
WSGI_APPLICATION = "paperless.wsgi.application"
|
||||
ASGI_APPLICATION = "paperless.asgi.application"
|
||||
|
||||
STATIC_URL = os.getenv("PAPERLESS_STATIC_URL", BASE_URL + "static/")
|
||||
@@ -157,15 +152,15 @@ WHITENOISE_STATIC_PREFIX = "/static/"
|
||||
# TODO: what is this used for?
|
||||
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',
|
||||
"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",
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -189,45 +184,46 @@ CHANNEL_LAYERS = {
|
||||
AUTO_LOGIN_USERNAME = os.getenv("PAPERLESS_AUTO_LOGIN_USERNAME")
|
||||
|
||||
if AUTO_LOGIN_USERNAME:
|
||||
_index = MIDDLEWARE.index('django.contrib.auth.middleware.AuthenticationMiddleware')
|
||||
_index = MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware")
|
||||
# This overrides everything the auth middleware is doing but still allows
|
||||
# regular login in case the provided user does not exist.
|
||||
MIDDLEWARE.insert(_index+1, 'paperless.auth.AutoLoginMiddleware')
|
||||
MIDDLEWARE.insert(_index + 1, "paperless.auth.AutoLoginMiddleware")
|
||||
|
||||
ENABLE_HTTP_REMOTE_USER = __get_boolean("PAPERLESS_ENABLE_HTTP_REMOTE_USER")
|
||||
HTTP_REMOTE_USER_HEADER_NAME = os.getenv("PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME", "HTTP_REMOTE_USER")
|
||||
HTTP_REMOTE_USER_HEADER_NAME = os.getenv(
|
||||
"PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME", "HTTP_REMOTE_USER"
|
||||
)
|
||||
|
||||
if ENABLE_HTTP_REMOTE_USER:
|
||||
MIDDLEWARE.append(
|
||||
'paperless.auth.HttpRemoteUserMiddleware'
|
||||
)
|
||||
MIDDLEWARE.append("paperless.auth.HttpRemoteUserMiddleware")
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
'django.contrib.auth.backends.RemoteUserBackend',
|
||||
'django.contrib.auth.backends.ModelBackend'
|
||||
"django.contrib.auth.backends.RemoteUserBackend",
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
]
|
||||
REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'].append(
|
||||
'rest_framework.authentication.RemoteUserAuthentication'
|
||||
REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"].append(
|
||||
"rest_framework.authentication.RemoteUserAuthentication"
|
||||
)
|
||||
|
||||
# X-Frame options for embedded PDF display:
|
||||
if DEBUG:
|
||||
X_FRAME_OPTIONS = 'ANY'
|
||||
X_FRAME_OPTIONS = "ANY"
|
||||
else:
|
||||
X_FRAME_OPTIONS = 'SAMEORIGIN'
|
||||
X_FRAME_OPTIONS = "SAMEORIGIN"
|
||||
|
||||
# We allow CORS from localhost:8080
|
||||
CORS_ALLOWED_ORIGINS = tuple(os.getenv("PAPERLESS_CORS_ALLOWED_HOSTS", "http://localhost:8000").split(","))
|
||||
CORS_ALLOWED_ORIGINS = tuple(
|
||||
os.getenv("PAPERLESS_CORS_ALLOWED_HOSTS", "http://localhost:8000").split(",")
|
||||
)
|
||||
|
||||
if DEBUG:
|
||||
# Allow access from the angular development server during debugging
|
||||
CORS_ALLOWED_ORIGINS += ('http://localhost:4200',)
|
||||
CORS_ALLOWED_ORIGINS += ("http://localhost:4200",)
|
||||
|
||||
# The secret key has a default that should be fine so long as you're hosting
|
||||
# Paperless on a closed network. However, if you're putting this anywhere
|
||||
# public, you should change the key to something unique and verbose.
|
||||
SECRET_KEY = os.getenv(
|
||||
"PAPERLESS_SECRET_KEY",
|
||||
"e11fl1oa-*ytql8p)(06fbj4ukrlo+n7k&q5+$1md7i+mge=ee"
|
||||
"PAPERLESS_SECRET_KEY", "e11fl1oa-*ytql8p)(06fbj4ukrlo+n7k&q5+$1md7i+mge=ee"
|
||||
)
|
||||
|
||||
_allowed_hosts = os.getenv("PAPERLESS_ALLOWED_HOSTS")
|
||||
@@ -238,16 +234,16 @@ else:
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -271,17 +267,14 @@ LANGUAGE_COOKIE_NAME = f"{COOKIE_PREFIX}django_language"
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": os.path.join(
|
||||
DATA_DIR,
|
||||
"db.sqlite3"
|
||||
)
|
||||
"NAME": os.path.join(DATA_DIR, "db.sqlite3"),
|
||||
}
|
||||
}
|
||||
|
||||
if os.getenv("PAPERLESS_DBHOST"):
|
||||
# Have sqlite available as a second option for management commands
|
||||
# This is important when migrating to/from sqlite
|
||||
DATABASES['sqlite'] = DATABASES['default'].copy()
|
||||
DATABASES["sqlite"] = DATABASES["default"].copy()
|
||||
|
||||
DATABASES["default"] = {
|
||||
"ENGINE": "django.db.backends.postgresql_psycopg2",
|
||||
@@ -289,21 +282,21 @@ if os.getenv("PAPERLESS_DBHOST"):
|
||||
"NAME": os.getenv("PAPERLESS_DBNAME", "paperless"),
|
||||
"USER": os.getenv("PAPERLESS_DBUSER", "paperless"),
|
||||
"PASSWORD": os.getenv("PAPERLESS_DBPASS", "paperless"),
|
||||
'OPTIONS': {'sslmode': os.getenv("PAPERLESS_DBSSLMODE", "prefer")},
|
||||
"OPTIONS": {"sslmode": os.getenv("PAPERLESS_DBSSLMODE", "prefer")},
|
||||
}
|
||||
if os.getenv("PAPERLESS_DBPORT"):
|
||||
DATABASES["default"]["PORT"] = os.getenv("PAPERLESS_DBPORT")
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
|
||||
###############################################################################
|
||||
# Internationalization #
|
||||
###############################################################################
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
LANGUAGES = [
|
||||
("en-us", _("English (US)")), # needs to be first to act as fallback language
|
||||
("en-us", _("English (US)")), # needs to be first to act as fallback language
|
||||
("cs-cz", _("Czech")),
|
||||
("da-dk", _("Danish")),
|
||||
("de-de", _("German")),
|
||||
@@ -321,9 +314,7 @@ LANGUAGES = [
|
||||
("sv-se", _("Swedish")),
|
||||
]
|
||||
|
||||
LOCALE_PATHS = [
|
||||
os.path.join(BASE_DIR, "locale")
|
||||
]
|
||||
LOCALE_PATHS = [os.path.join(BASE_DIR, "locale")]
|
||||
|
||||
TIME_ZONE = os.getenv("PAPERLESS_TIME_ZONE", "UTC")
|
||||
|
||||
@@ -341,20 +332,20 @@ setup_logging_queues()
|
||||
|
||||
os.makedirs(LOGGING_DIR, exist_ok=True)
|
||||
|
||||
LOGROTATE_MAX_SIZE = os.getenv("PAPERLESS_LOGROTATE_MAX_SIZE", 1024*1024)
|
||||
LOGROTATE_MAX_SIZE = os.getenv("PAPERLESS_LOGROTATE_MAX_SIZE", 1024 * 1024)
|
||||
LOGROTATE_MAX_BACKUPS = os.getenv("PAPERLESS_LOGROTATE_MAX_BACKUPS", 20)
|
||||
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '[{asctime}] [{levelname}] [{name}] {message}',
|
||||
'style': '{',
|
||||
"formatters": {
|
||||
"verbose": {
|
||||
"format": "[{asctime}] [{levelname}] [{name}] {message}",
|
||||
"style": "{",
|
||||
},
|
||||
'simple': {
|
||||
'format': '{levelname} {message}',
|
||||
'style': '{',
|
||||
"simple": {
|
||||
"format": "{levelname} {message}",
|
||||
"style": "{",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
@@ -368,29 +359,21 @@ LOGGING = {
|
||||
"formatter": "verbose",
|
||||
"filename": os.path.join(LOGGING_DIR, "paperless.log"),
|
||||
"maxBytes": LOGROTATE_MAX_SIZE,
|
||||
"backupCount": LOGROTATE_MAX_BACKUPS
|
||||
"backupCount": LOGROTATE_MAX_BACKUPS,
|
||||
},
|
||||
"file_mail": {
|
||||
"class": "concurrent_log_handler.ConcurrentRotatingFileHandler",
|
||||
"formatter": "verbose",
|
||||
"filename": os.path.join(LOGGING_DIR, "mail.log"),
|
||||
"maxBytes": LOGROTATE_MAX_SIZE,
|
||||
"backupCount": LOGROTATE_MAX_BACKUPS
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"handlers": ["console"]
|
||||
},
|
||||
"loggers": {
|
||||
"paperless": {
|
||||
"handlers": ["file_paperless"],
|
||||
"level": "DEBUG"
|
||||
"backupCount": LOGROTATE_MAX_BACKUPS,
|
||||
},
|
||||
"paperless_mail": {
|
||||
"handlers": ["file_mail"],
|
||||
"level": "DEBUG"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": {"handlers": ["console"]},
|
||||
"loggers": {
|
||||
"paperless": {"handlers": ["file_paperless"], "level": "DEBUG"},
|
||||
"paperless_mail": {"handlers": ["file_mail"], "level": "DEBUG"},
|
||||
},
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
@@ -412,10 +395,7 @@ def default_task_workers():
|
||||
try:
|
||||
if available_cores < 4:
|
||||
return available_cores
|
||||
return max(
|
||||
math.floor(math.sqrt(available_cores)),
|
||||
1
|
||||
)
|
||||
return max(math.floor(math.sqrt(available_cores)), 1)
|
||||
except NotImplementedError:
|
||||
return 1
|
||||
|
||||
@@ -423,13 +403,13 @@ def default_task_workers():
|
||||
TASK_WORKERS = int(os.getenv("PAPERLESS_TASK_WORKERS", default_task_workers()))
|
||||
|
||||
Q_CLUSTER = {
|
||||
'name': 'paperless',
|
||||
'catch_up': False,
|
||||
'recycle': 1,
|
||||
'retry': 1800,
|
||||
'timeout': int(os.getenv("PAPERLESS_WORKER_TIMEOUT", 1800)),
|
||||
'workers': TASK_WORKERS,
|
||||
'redis': os.getenv("PAPERLESS_REDIS", "redis://localhost:6379")
|
||||
"name": "paperless",
|
||||
"catch_up": False,
|
||||
"recycle": 1,
|
||||
"retry": 1800,
|
||||
"timeout": int(os.getenv("PAPERLESS_WORKER_TIMEOUT", 1800)),
|
||||
"workers": TASK_WORKERS,
|
||||
"redis": os.getenv("PAPERLESS_REDIS", "redis://localhost:6379"),
|
||||
}
|
||||
|
||||
|
||||
@@ -437,15 +417,14 @@ def default_threads_per_worker(task_workers):
|
||||
# always leave one core open
|
||||
available_cores = max(multiprocessing.cpu_count(), 1)
|
||||
try:
|
||||
return max(
|
||||
math.floor(available_cores / task_workers),
|
||||
1
|
||||
)
|
||||
return max(math.floor(available_cores / task_workers), 1)
|
||||
except NotImplementedError:
|
||||
return 1
|
||||
|
||||
|
||||
THREADS_PER_WORKER = os.getenv("PAPERLESS_THREADS_PER_WORKER", default_threads_per_worker(TASK_WORKERS))
|
||||
THREADS_PER_WORKER = os.getenv(
|
||||
"PAPERLESS_THREADS_PER_WORKER", default_threads_per_worker(TASK_WORKERS)
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
# Paperless Specific Settings #
|
||||
@@ -466,14 +445,18 @@ CONSUMER_RECURSIVE = __get_boolean("PAPERLESS_CONSUMER_RECURSIVE")
|
||||
# Ignore glob patterns, relative to PAPERLESS_CONSUMPTION_DIR
|
||||
CONSUMER_IGNORE_PATTERNS = list(
|
||||
json.loads(
|
||||
os.getenv("PAPERLESS_CONSUMER_IGNORE_PATTERNS",
|
||||
'[".DS_STORE/*", "._*", ".stfolder/*"]')))
|
||||
os.getenv(
|
||||
"PAPERLESS_CONSUMER_IGNORE_PATTERNS",
|
||||
'[".DS_STORE/*", "._*", ".stfolder/*"]',
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
CONSUMER_SUBDIRS_AS_TAGS = __get_boolean("PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS")
|
||||
|
||||
OPTIMIZE_THUMBNAILS = __get_boolean("PAPERLESS_OPTIMIZE_THUMBNAILS", "true")
|
||||
|
||||
OCR_PAGES = int(os.getenv('PAPERLESS_OCR_PAGES', 0))
|
||||
OCR_PAGES = int(os.getenv("PAPERLESS_OCR_PAGES", 0))
|
||||
|
||||
# The default language that tesseract will attempt to use when parsing
|
||||
# documents. It should be a 3-letter language code consistent with ISO 639.
|
||||
@@ -495,7 +478,9 @@ OCR_DESKEW = __get_boolean("PAPERLESS_OCR_DESKEW", "true")
|
||||
|
||||
OCR_ROTATE_PAGES = __get_boolean("PAPERLESS_OCR_ROTATE_PAGES", "true")
|
||||
|
||||
OCR_ROTATE_PAGES_THRESHOLD = float(os.getenv("PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD", 12.0))
|
||||
OCR_ROTATE_PAGES_THRESHOLD = float(
|
||||
os.getenv("PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD", 12.0)
|
||||
)
|
||||
|
||||
OCR_USER_ARGS = os.getenv("PAPERLESS_OCR_USER_ARGS", "{}")
|
||||
|
||||
@@ -542,7 +527,10 @@ for t in json.loads(os.getenv("PAPERLESS_FILENAME_PARSE_TRANSFORMS", "[]")):
|
||||
# Specify the filename format for out files
|
||||
PAPERLESS_FILENAME_FORMAT = os.getenv("PAPERLESS_FILENAME_FORMAT")
|
||||
|
||||
THUMBNAIL_FONT_NAME = os.getenv("PAPERLESS_THUMBNAIL_FONT_NAME", "/usr/share/fonts/liberation/LiberationSerif-Regular.ttf")
|
||||
THUMBNAIL_FONT_NAME = os.getenv(
|
||||
"PAPERLESS_THUMBNAIL_FONT_NAME",
|
||||
"/usr/share/fonts/liberation/LiberationSerif-Regular.ttf",
|
||||
)
|
||||
|
||||
# Tika settings
|
||||
PAPERLESS_TIKA_ENABLED = __get_boolean("PAPERLESS_TIKA_ENABLED", "NO")
|
||||
|
@@ -9,7 +9,6 @@ from paperless.checks import debug_mode_check
|
||||
|
||||
|
||||
class TestChecks(DirectoriesMixin, TestCase):
|
||||
|
||||
def test_binaries(self):
|
||||
self.assertEqual(binaries_check(None), [])
|
||||
|
||||
@@ -20,9 +19,9 @@ class TestChecks(DirectoriesMixin, TestCase):
|
||||
def test_paths_check(self):
|
||||
self.assertEqual(paths_check(None), [])
|
||||
|
||||
@override_settings(MEDIA_ROOT="uuh",
|
||||
DATA_DIR="whatever",
|
||||
CONSUMPTION_DIR="idontcare")
|
||||
@override_settings(
|
||||
MEDIA_ROOT="uuh", DATA_DIR="whatever", CONSUMPTION_DIR="idontcare"
|
||||
)
|
||||
def test_paths_check_dont_exist(self):
|
||||
msgs = paths_check(None)
|
||||
self.assertEqual(len(msgs), 3, str(msgs))
|
||||
|
@@ -8,14 +8,13 @@ from paperless.asgi import application
|
||||
|
||||
|
||||
TEST_CHANNEL_LAYERS = {
|
||||
'default': {
|
||||
'BACKEND': 'channels.layers.InMemoryChannelLayer',
|
||||
"default": {
|
||||
"BACKEND": "channels.layers.InMemoryChannelLayer",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class TestWebSockets(TestCase):
|
||||
|
||||
@override_settings(CHANNEL_LAYERS=TEST_CHANNEL_LAYERS)
|
||||
async def test_no_auth(self):
|
||||
communicator = WebsocketCommunicator(application, "/ws/status/")
|
||||
@@ -43,15 +42,12 @@ class TestWebSockets(TestCase):
|
||||
connected, subprotocol = await communicator.connect()
|
||||
self.assertTrue(connected)
|
||||
|
||||
message = {
|
||||
"task_id": "test"
|
||||
}
|
||||
message = {"task_id": "test"}
|
||||
|
||||
channel_layer = get_channel_layer()
|
||||
await channel_layer.group_send("status_updates", {
|
||||
"type": "status_update",
|
||||
"data": message
|
||||
})
|
||||
await channel_layer.group_send(
|
||||
"status_updates", {"type": "status_update", "data": message}
|
||||
)
|
||||
|
||||
response = await communicator.receive_json_from()
|
||||
|
||||
|
@@ -25,7 +25,7 @@ from documents.views import (
|
||||
SavedViewViewSet,
|
||||
BulkEditView,
|
||||
SelectionDataView,
|
||||
BulkDownloadView
|
||||
BulkDownloadView,
|
||||
)
|
||||
from paperless.views import FaviconView
|
||||
|
||||
@@ -39,82 +39,101 @@ api_router.register(r"saved_views", SavedViewViewSet)
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r"^api/", include([
|
||||
re_path(r"^auth/",
|
||||
include(('rest_framework.urls', 'rest_framework'),
|
||||
namespace="rest_framework")),
|
||||
|
||||
re_path(r"^search/autocomplete/",
|
||||
SearchAutoCompleteView.as_view(),
|
||||
name="autocomplete"),
|
||||
|
||||
re_path(r"^statistics/",
|
||||
StatisticsView.as_view(),
|
||||
name="statistics"),
|
||||
|
||||
re_path(r"^documents/post_document/", PostDocumentView.as_view(),
|
||||
name="post_document"),
|
||||
|
||||
re_path(r"^documents/bulk_edit/", BulkEditView.as_view(),
|
||||
name="bulk_edit"),
|
||||
|
||||
re_path(r"^documents/selection_data/", SelectionDataView.as_view(),
|
||||
name="selection_data"),
|
||||
|
||||
re_path(r"^documents/bulk_download/", BulkDownloadView.as_view(),
|
||||
name="bulk_download"),
|
||||
|
||||
path('token/', views.obtain_auth_token)
|
||||
|
||||
] + api_router.urls)),
|
||||
|
||||
re_path(
|
||||
r"^api/",
|
||||
include(
|
||||
[
|
||||
re_path(
|
||||
r"^auth/",
|
||||
include(
|
||||
("rest_framework.urls", "rest_framework"),
|
||||
namespace="rest_framework",
|
||||
),
|
||||
),
|
||||
re_path(
|
||||
r"^search/autocomplete/",
|
||||
SearchAutoCompleteView.as_view(),
|
||||
name="autocomplete",
|
||||
),
|
||||
re_path(r"^statistics/", StatisticsView.as_view(), name="statistics"),
|
||||
re_path(
|
||||
r"^documents/post_document/",
|
||||
PostDocumentView.as_view(),
|
||||
name="post_document",
|
||||
),
|
||||
re_path(
|
||||
r"^documents/bulk_edit/", BulkEditView.as_view(), name="bulk_edit"
|
||||
),
|
||||
re_path(
|
||||
r"^documents/selection_data/",
|
||||
SelectionDataView.as_view(),
|
||||
name="selection_data",
|
||||
),
|
||||
re_path(
|
||||
r"^documents/bulk_download/",
|
||||
BulkDownloadView.as_view(),
|
||||
name="bulk_download",
|
||||
),
|
||||
path("token/", views.obtain_auth_token),
|
||||
]
|
||||
+ api_router.urls
|
||||
),
|
||||
),
|
||||
re_path(r"^favicon.ico$", FaviconView.as_view(), name="favicon"),
|
||||
|
||||
re_path(r"admin/", admin.site.urls),
|
||||
|
||||
re_path(r"^fetch/", include([
|
||||
re_path(
|
||||
r"^doc/(?P<pk>\d+)$",
|
||||
RedirectView.as_view(url=settings.BASE_URL +
|
||||
'api/documents/%(pk)s/download/'),
|
||||
re_path(
|
||||
r"^fetch/",
|
||||
include(
|
||||
[
|
||||
re_path(
|
||||
r"^doc/(?P<pk>\d+)$",
|
||||
RedirectView.as_view(
|
||||
url=settings.BASE_URL + "api/documents/%(pk)s/download/"
|
||||
),
|
||||
),
|
||||
re_path(
|
||||
r"^thumb/(?P<pk>\d+)$",
|
||||
RedirectView.as_view(
|
||||
url=settings.BASE_URL + "api/documents/%(pk)s/thumb/"
|
||||
),
|
||||
),
|
||||
re_path(
|
||||
r"^preview/(?P<pk>\d+)$",
|
||||
RedirectView.as_view(
|
||||
url=settings.BASE_URL + "api/documents/%(pk)s/preview/"
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
re_path(
|
||||
r"^thumb/(?P<pk>\d+)$",
|
||||
RedirectView.as_view(url=settings.BASE_URL +
|
||||
'api/documents/%(pk)s/thumb/'),
|
||||
),
|
||||
re_path(
|
||||
r"^push$",
|
||||
csrf_exempt(
|
||||
RedirectView.as_view(url=settings.BASE_URL + "api/documents/post_document/")
|
||||
),
|
||||
re_path(
|
||||
r"^preview/(?P<pk>\d+)$",
|
||||
RedirectView.as_view(url=settings.BASE_URL +
|
||||
'api/documents/%(pk)s/preview/'),
|
||||
),
|
||||
])),
|
||||
|
||||
re_path(r"^push$", csrf_exempt(
|
||||
RedirectView.as_view(url=settings.BASE_URL +
|
||||
'api/documents/post_document/'))),
|
||||
|
||||
),
|
||||
# Frontend assets TODO: this is pretty bad, but it works.
|
||||
path('assets/<path:path>',
|
||||
RedirectView.as_view(url=settings.STATIC_URL +
|
||||
'frontend/en-US/assets/%(path)s')),
|
||||
path(
|
||||
"assets/<path:path>",
|
||||
RedirectView.as_view(
|
||||
url=settings.STATIC_URL + "frontend/en-US/assets/%(path)s"
|
||||
),
|
||||
),
|
||||
# TODO: with localization, this is even worse! :/
|
||||
|
||||
# login, logout
|
||||
path('accounts/', include('django.contrib.auth.urls')),
|
||||
|
||||
path("accounts/", include("django.contrib.auth.urls")),
|
||||
# Root of the Frontent
|
||||
re_path(r".*", login_required(IndexView.as_view()), name='base'),
|
||||
re_path(r".*", login_required(IndexView.as_view()), name="base"),
|
||||
]
|
||||
|
||||
|
||||
websocket_urlpatterns = [
|
||||
re_path(r'ws/status/$', StatusConsumer.as_asgi()),
|
||||
re_path(r"ws/status/$", StatusConsumer.as_asgi()),
|
||||
]
|
||||
|
||||
# Text in each page's <h1> (and above login form).
|
||||
admin.site.site_header = 'Paperless-ng'
|
||||
admin.site.site_header = "Paperless-ng"
|
||||
# Text at the end of each page's <title>.
|
||||
admin.site.site_title = 'Paperless-ng'
|
||||
admin.site.site_title = "Paperless-ng"
|
||||
# Text at the top of the admin index page.
|
||||
admin.site.index_title = _('Paperless-ng administration')
|
||||
admin.site.index_title = _("Paperless-ng administration")
|
||||
|
@@ -12,14 +12,9 @@ class StandardPagination(PageNumberPagination):
|
||||
|
||||
|
||||
class FaviconView(View):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
favicon = os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
"static",
|
||||
"paperless",
|
||||
"img",
|
||||
"favicon.ico"
|
||||
os.path.dirname(__file__), "static", "paperless", "img", "favicon.ico"
|
||||
)
|
||||
with open(favicon, "rb") as f:
|
||||
return HttpResponse(f, content_type="image/x-icon")
|
||||
|
Reference in New Issue
Block a user