Merge branch 'dev' into feature-config-from-frontend

This commit is contained in:
shamoon 2023-12-27 21:38:30 -08:00
commit 8ea6bb770b
9 changed files with 41 additions and 18 deletions

View File

@ -8,20 +8,22 @@ most of the available filters and ordering fields.
The API provides the following main endpoints: The API provides the following main endpoints:
- `/api/consumption_templates/`: Full CRUD support.
- `/api/correspondents/`: Full CRUD support.
- `/api/custom_fields/`: Full CRUD support.
- `/api/documents/`: Full CRUD support, except POSTing new documents. - `/api/documents/`: Full CRUD support, except POSTing new documents.
See below. See below.
- `/api/correspondents/`: Full CRUD support.
- `/api/document_types/`: Full CRUD support. - `/api/document_types/`: Full CRUD support.
- `/api/groups/`: Full CRUD support.
- `/api/logs/`: Read-Only. - `/api/logs/`: Read-Only.
- `/api/tags/`: Full CRUD support.
- `/api/tasks/`: Read-only.
- `/api/mail_accounts/`: Full CRUD support. - `/api/mail_accounts/`: Full CRUD support.
- `/api/mail_rules/`: Full CRUD support. - `/api/mail_rules/`: Full CRUD support.
- `/api/users/`: Full CRUD support.
- `/api/groups/`: Full CRUD support.
- `/api/share_links/`: Full CRUD support.
- `/api/custom_fields/`: Full CRUD support.
- `/api/profile/`: GET, PATCH - `/api/profile/`: GET, PATCH
- `/api/share_links/`: Full CRUD support.
- `/api/storage_paths/`: Full CRUD support.
- `/api/tags/`: Full CRUD support.
- `/api/tasks/`: Read-only.
- `/api/users/`: Full CRUD support.
All of these endpoints except for the logging endpoint allow you to All of these endpoints except for the logging endpoint allow you to
fetch (and edit and delete where appropriate) individual objects by fetch (and edit and delete where appropriate) individual objects by

View File

@ -54,6 +54,7 @@ export class LogsComponent implements OnInit, AfterViewChecked, OnDestroy {
ngOnDestroy(): void { ngOnDestroy(): void {
this.unsubscribeNotifier.next(true) this.unsubscribeNotifier.next(true)
this.unsubscribeNotifier.complete() this.unsubscribeNotifier.complete()
clearInterval(this.autoRefreshInterval)
} }
reloadLogs() { reloadLogs() {

View File

@ -46,6 +46,7 @@ export class TasksComponent
ngOnDestroy() { ngOnDestroy() {
this.tasksService.cancelPending() this.tasksService.cancelPending()
clearInterval(this.autoRefreshInterval)
} }
dismissTask(task: PaperlessTask) { dismissTask(task: PaperlessTask) {

View File

@ -34,6 +34,9 @@
(focus)="clearLastSearchTerm()" (focus)="clearLastSearchTerm()"
(clear)="clearLastSearchTerm()" (clear)="clearLastSearchTerm()"
(blur)="onBlur()"> (blur)="onBlur()">
<ng-template ng-option-tmp let-item="item">
<span [title]="item.name">{{item.name}}</span>
</ng-template>
</ng-select> </ng-select>
@if (allowCreateNew) { @if (allowCreateNew) {
<button class="btn btn-outline-secondary" type="button" (click)="addItem()" [disabled]="disabled"> <button class="btn btn-outline-secondary" type="button" (click)="addItem()" [disabled]="disabled">

View File

@ -105,7 +105,7 @@ class BarcodeReader:
asn_text = asn_text[len(settings.CONSUMER_ASN_BARCODE_PREFIX) :].strip() asn_text = asn_text[len(settings.CONSUMER_ASN_BARCODE_PREFIX) :].strip()
# remove non-numeric parts of the remaining string # remove non-numeric parts of the remaining string
asn_text = re.sub("[^0-9]", "", asn_text) asn_text = re.sub(r"\D", "", asn_text)
# now, try parsing the ASN number # now, try parsing the ASN number
try: try:

View File

@ -2,12 +2,16 @@ from django.conf import settings
from django.contrib import auth from django.contrib import auth
from django.contrib.auth.middleware import PersistentRemoteUserMiddleware from django.contrib.auth.middleware import PersistentRemoteUserMiddleware
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.http import HttpRequest
from django.utils.deprecation import MiddlewareMixin from django.utils.deprecation import MiddlewareMixin
from rest_framework import authentication from rest_framework import authentication
class AutoLoginMiddleware(MiddlewareMixin): class AutoLoginMiddleware(MiddlewareMixin):
def process_request(self, request): def process_request(self, request: HttpRequest):
# Dont use auto-login with token request
if request.path.startswith("/api/token/") and request.method == "POST":
return None
try: try:
request.user = User.objects.get(username=settings.AUTO_LOGIN_USERNAME) request.user = User.objects.get(username=settings.AUTO_LOGIN_USERNAME)
auth.login( auth.login(

View File

@ -315,8 +315,8 @@ if DEBUG:
REST_FRAMEWORK = { REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [ "DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.BasicAuthentication", "rest_framework.authentication.BasicAuthentication",
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.TokenAuthentication", "rest_framework.authentication.TokenAuthentication",
"rest_framework.authentication.SessionAuthentication",
], ],
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.AcceptHeaderVersioning", "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.AcceptHeaderVersioning",
"DEFAULT_VERSION": "1", "DEFAULT_VERSION": "1",

View File

@ -6,6 +6,7 @@ from django.urls import path
from django.urls import re_path from django.urls import re_path
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.generic import RedirectView from django.views.generic import RedirectView
from rest_framework.authtoken import views from rest_framework.authtoken import views
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
@ -180,7 +181,11 @@ urlpatterns = [
# login, logout # login, logout
path("accounts/", include("django.contrib.auth.urls")), path("accounts/", include("django.contrib.auth.urls")),
# Root of the Frontend # Root of the Frontend
re_path(r".*", login_required(IndexView.as_view()), name="base"), re_path(
r".*",
login_required(ensure_csrf_cookie(IndexView.as_view())),
name="base",
),
] ]

View File

@ -703,6 +703,12 @@ class MailAccountHandler(LoggingMixin):
mime_type = magic.from_buffer(att.payload, mime=True) mime_type = magic.from_buffer(att.payload, mime=True)
if is_mime_type_supported(mime_type): if is_mime_type_supported(mime_type):
self.log.info(
f"Rule {rule}: "
f"Consuming attachment {att.filename} from mail "
f"{message.subject} from {message.from_}",
)
os.makedirs(settings.SCRATCH_DIR, exist_ok=True) os.makedirs(settings.SCRATCH_DIR, exist_ok=True)
temp_dir = Path( temp_dir = Path(
@ -711,14 +717,15 @@ class MailAccountHandler(LoggingMixin):
dir=settings.SCRATCH_DIR, dir=settings.SCRATCH_DIR,
), ),
) )
temp_filename = temp_dir / pathvalidate.sanitize_filename(att.filename)
temp_filename.write_bytes(att.payload)
self.log.info( attachment_name = pathvalidate.sanitize_filename(att.filename)
f"Rule {rule}: " if attachment_name:
f"Consuming attachment {att.filename} from mail " temp_filename = temp_dir / attachment_name
f"{message.subject} from {message.from_}", else: # pragma: no cover
) # Some cases may have no name (generally inline)
temp_filename = temp_dir / "no-name-attachment"
temp_filename.write_bytes(att.payload)
input_doc = ConsumableDocument( input_doc = ConsumableDocument(
source=DocumentSource.MailFetch, source=DocumentSource.MailFetch,