mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 19:17:13 -05:00 
			
		
		
		
	Extremely basic chat component
This commit is contained in:
		
							parent
							
								
									bb3336f7bc
								
							
						
					
					
						commit
						c809a65571
					
				@ -30,6 +30,7 @@
 | 
				
			|||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
  <ul ngbNav class="order-sm-3">
 | 
					  <ul ngbNav class="order-sm-3">
 | 
				
			||||||
 | 
					    <pngx-chat></pngx-chat>
 | 
				
			||||||
    <pngx-toasts-dropdown></pngx-toasts-dropdown>
 | 
					    <pngx-toasts-dropdown></pngx-toasts-dropdown>
 | 
				
			||||||
    <li ngbDropdown class="nav-item dropdown">
 | 
					    <li ngbDropdown class="nav-item dropdown">
 | 
				
			||||||
      <button class="btn ps-1 border-0" id="userDropdown" ngbDropdownToggle>
 | 
					      <button class="btn ps-1 border-0" id="userDropdown" ngbDropdownToggle>
 | 
				
			||||||
 | 
				
			|||||||
@ -44,6 +44,7 @@ import { SettingsService } from 'src/app/services/settings.service'
 | 
				
			|||||||
import { TasksService } from 'src/app/services/tasks.service'
 | 
					import { TasksService } from 'src/app/services/tasks.service'
 | 
				
			||||||
import { ToastService } from 'src/app/services/toast.service'
 | 
					import { ToastService } from 'src/app/services/toast.service'
 | 
				
			||||||
import { environment } from 'src/environments/environment'
 | 
					import { environment } from 'src/environments/environment'
 | 
				
			||||||
 | 
					import { ChatComponent } from '../chat/chat/chat.component'
 | 
				
			||||||
import { ProfileEditDialogComponent } from '../common/profile-edit-dialog/profile-edit-dialog.component'
 | 
					import { ProfileEditDialogComponent } from '../common/profile-edit-dialog/profile-edit-dialog.component'
 | 
				
			||||||
import { DocumentDetailComponent } from '../document-detail/document-detail.component'
 | 
					import { DocumentDetailComponent } from '../document-detail/document-detail.component'
 | 
				
			||||||
import { ComponentWithPermissions } from '../with-permissions/with-permissions.component'
 | 
					import { ComponentWithPermissions } from '../with-permissions/with-permissions.component'
 | 
				
			||||||
@ -59,6 +60,7 @@ import { ToastsDropdownComponent } from './toasts-dropdown/toasts-dropdown.compo
 | 
				
			|||||||
    DocumentTitlePipe,
 | 
					    DocumentTitlePipe,
 | 
				
			||||||
    IfPermissionsDirective,
 | 
					    IfPermissionsDirective,
 | 
				
			||||||
    ToastsDropdownComponent,
 | 
					    ToastsDropdownComponent,
 | 
				
			||||||
 | 
					    ChatComponent,
 | 
				
			||||||
    RouterModule,
 | 
					    RouterModule,
 | 
				
			||||||
    NgClass,
 | 
					    NgClass,
 | 
				
			||||||
    NgbDropdownModule,
 | 
					    NgbDropdownModule,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<li ngbDropdown class="nav-item" (openChange)="onOpenChange($event)">
 | 
					<li ngbDropdown class="nav-item mx-1" (openChange)="onOpenChange($event)">
 | 
				
			||||||
  @if (toasts.length) {
 | 
					  @if (toasts.length) {
 | 
				
			||||||
    <span class="badge rounded-pill z-3 pe-none bg-secondary me-2 position-absolute top-0 left-0">{{ toasts.length }}</span>
 | 
					    <span class="badge rounded-pill z-3 pe-none bg-secondary me-2 position-absolute top-0 left-0">{{ toasts.length }}</span>
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										31
									
								
								src-ui/src/app/components/chat/chat/chat.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src-ui/src/app/components/chat/chat/chat.component.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					<li ngbDropdown class="nav-item me-n2" (openChange)="onOpenChange($event)">
 | 
				
			||||||
 | 
					  <button class="btn border-0" id="chatDropdown" ngbDropdownToggle>
 | 
				
			||||||
 | 
					    <i-bs width="1.3em" height="1.3em" name="chatSquareDots"></i-bs>
 | 
				
			||||||
 | 
					  </button>
 | 
				
			||||||
 | 
					  <div ngbDropdownMenu class="dropdown-menu-end shadow p-3" aria-labelledby="chatDropdown">
 | 
				
			||||||
 | 
					    <div class="chat-container bg-light p-2">
 | 
				
			||||||
 | 
					      <div class="chat-messages font-monospace small">
 | 
				
			||||||
 | 
					        @for (message of messages; track message) {
 | 
				
			||||||
 | 
					          <div class="message d-flex flex-row small" [class.justify-content-end]="message.role === 'user'">
 | 
				
			||||||
 | 
					            <span class="p-2 m-2" [class.bg-dark]="message.role === 'user'">{{ message.content }}</span>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        <div #scrollAnchor></div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <form class="chat-input">
 | 
				
			||||||
 | 
					        <div class="input-group">
 | 
				
			||||||
 | 
					          <input
 | 
				
			||||||
 | 
					            #inputField
 | 
				
			||||||
 | 
					            class="form-control form-control-sm" name="chatInput" type="text" placeholder="Ask about this document..."
 | 
				
			||||||
 | 
					            [disabled]="loading"
 | 
				
			||||||
 | 
					            [(ngModel)]="input"
 | 
				
			||||||
 | 
					            (keydown)="searchInputKeyDown($event)"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          <button class="btn btn-sm btn-secondary" type="button" (click)="sendMessage()" [disabled]="loading">Send</button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </form>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</li>
 | 
				
			||||||
							
								
								
									
										22
									
								
								src-ui/src/app/components/chat/chat/chat.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src-ui/src/app/components/chat/chat/chat.component.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					.dropdown-menu {
 | 
				
			||||||
 | 
					  width: var(--pngx-toast-max-width);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.chat-messages {
 | 
				
			||||||
 | 
					  max-height: 350px;
 | 
				
			||||||
 | 
					  overflow-y: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.dropdown-toggle::after {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.dropdown-item {
 | 
				
			||||||
 | 
					  white-space: initial;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media screen and (max-width: 400px) {
 | 
				
			||||||
 | 
					  :host ::ng-deep .dropdown-menu-end {
 | 
				
			||||||
 | 
					    right: -3rem;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										91
									
								
								src-ui/src/app/components/chat/chat/chat.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src-ui/src/app/components/chat/chat/chat.component.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					import { NgClass } from '@angular/common'
 | 
				
			||||||
 | 
					import { Component, ElementRef, ViewChild } from '@angular/core'
 | 
				
			||||||
 | 
					import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
				
			||||||
 | 
					import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
 | 
				
			||||||
 | 
					import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
 | 
				
			||||||
 | 
					import { ChatMessage, ChatService } from 'src/app/services/chat.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					  selector: 'pngx-chat',
 | 
				
			||||||
 | 
					  imports: [
 | 
				
			||||||
 | 
					    FormsModule,
 | 
				
			||||||
 | 
					    ReactiveFormsModule,
 | 
				
			||||||
 | 
					    NgxBootstrapIconsModule,
 | 
				
			||||||
 | 
					    NgbDropdownModule,
 | 
				
			||||||
 | 
					    NgClass,
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  templateUrl: './chat.component.html',
 | 
				
			||||||
 | 
					  styleUrl: './chat.component.scss',
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class ChatComponent {
 | 
				
			||||||
 | 
					  messages: ChatMessage[] = []
 | 
				
			||||||
 | 
					  loading = false
 | 
				
			||||||
 | 
					  documentId = 295 // Replace this with actual doc ID logic
 | 
				
			||||||
 | 
					  input: string = ''
 | 
				
			||||||
 | 
					  @ViewChild('scrollAnchor') scrollAnchor!: ElementRef<HTMLDivElement>
 | 
				
			||||||
 | 
					  @ViewChild('inputField') inputField!: ElementRef<HTMLInputElement>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(private chatService: ChatService) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sendMessage(): void {
 | 
				
			||||||
 | 
					    if (!this.input.trim()) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const userMessage: ChatMessage = { role: 'user', content: this.input }
 | 
				
			||||||
 | 
					    this.messages.push(userMessage)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const assistantMessage: ChatMessage = {
 | 
				
			||||||
 | 
					      role: 'assistant',
 | 
				
			||||||
 | 
					      content: '',
 | 
				
			||||||
 | 
					      isStreaming: true,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.messages.push(assistantMessage)
 | 
				
			||||||
 | 
					    this.loading = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.chatService.streamChat(this.documentId, this.input).subscribe({
 | 
				
			||||||
 | 
					      next: (chunk) => {
 | 
				
			||||||
 | 
					        assistantMessage.content += chunk
 | 
				
			||||||
 | 
					        this.scrollToBottom()
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      error: () => {
 | 
				
			||||||
 | 
					        assistantMessage.content += '\n\n⚠️ Error receiving response.'
 | 
				
			||||||
 | 
					        assistantMessage.isStreaming = false
 | 
				
			||||||
 | 
					        this.loading = false
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      complete: () => {
 | 
				
			||||||
 | 
					        assistantMessage.isStreaming = false
 | 
				
			||||||
 | 
					        this.loading = false
 | 
				
			||||||
 | 
					        this.scrollToBottom()
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.input = ''
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  scrollToBottom(): void {
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					      this.scrollAnchor?.nativeElement?.scrollIntoView({ behavior: 'smooth' })
 | 
				
			||||||
 | 
					    }, 50)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onOpenChange(open: boolean): void {
 | 
				
			||||||
 | 
					    if (open) {
 | 
				
			||||||
 | 
					      setTimeout(() => {
 | 
				
			||||||
 | 
					        this.inputField.nativeElement.focus()
 | 
				
			||||||
 | 
					      }, 10)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public searchInputKeyDown(event: KeyboardEvent) {
 | 
				
			||||||
 | 
					    if (event.key === 'Enter') {
 | 
				
			||||||
 | 
					      event.preventDefault()
 | 
				
			||||||
 | 
					      this.sendMessage()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // } else if (event.key === 'Escape' && !this.resultsDropdown.isOpen()) {
 | 
				
			||||||
 | 
					    //   if (this.query?.length) {
 | 
				
			||||||
 | 
					    //     this.reset(true)
 | 
				
			||||||
 | 
					    //   } else {
 | 
				
			||||||
 | 
					    //     this.searchInput.nativeElement.blur()
 | 
				
			||||||
 | 
					    //   }
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -4,25 +4,19 @@ import {
 | 
				
			|||||||
  HttpInterceptor,
 | 
					  HttpInterceptor,
 | 
				
			||||||
  HttpRequest,
 | 
					  HttpRequest,
 | 
				
			||||||
} from '@angular/common/http'
 | 
					} from '@angular/common/http'
 | 
				
			||||||
import { Injectable, inject } from '@angular/core'
 | 
					import { inject, Injectable } from '@angular/core'
 | 
				
			||||||
import { Meta } from '@angular/platform-browser'
 | 
					 | 
				
			||||||
import { CookieService } from 'ngx-cookie-service'
 | 
					 | 
				
			||||||
import { Observable } from 'rxjs'
 | 
					import { Observable } from 'rxjs'
 | 
				
			||||||
 | 
					import { CsrfService } from '../services/csrf.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class CsrfInterceptor implements HttpInterceptor {
 | 
					export class CsrfInterceptor implements HttpInterceptor {
 | 
				
			||||||
  private cookieService = inject(CookieService)
 | 
					  private csrfService = inject(CsrfService)
 | 
				
			||||||
  private meta = inject(Meta)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  intercept(
 | 
					  intercept(
 | 
				
			||||||
    request: HttpRequest<unknown>,
 | 
					    request: HttpRequest<unknown>,
 | 
				
			||||||
    next: HttpHandler
 | 
					    next: HttpHandler
 | 
				
			||||||
  ): Observable<HttpEvent<unknown>> {
 | 
					  ): Observable<HttpEvent<unknown>> {
 | 
				
			||||||
    let prefix = ''
 | 
					    const csrfToken = this.csrfService.getToken()
 | 
				
			||||||
    if (this.meta.getTag('name=cookie_prefix')) {
 | 
					 | 
				
			||||||
      prefix = this.meta.getTag('name=cookie_prefix').content
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    let csrfToken = this.cookieService.get(`${prefix}csrftoken`)
 | 
					 | 
				
			||||||
    if (csrfToken) {
 | 
					    if (csrfToken) {
 | 
				
			||||||
      request = request.clone({
 | 
					      request = request.clone({
 | 
				
			||||||
        setHeaders: {
 | 
					        setHeaders: {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										60
									
								
								src-ui/src/app/services/chat.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src-ui/src/app/services/chat.service.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					import { Injectable } from '@angular/core'
 | 
				
			||||||
 | 
					import { Observable } from 'rxjs'
 | 
				
			||||||
 | 
					import { environment } from 'src/environments/environment'
 | 
				
			||||||
 | 
					import { CsrfService } from './csrf.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ChatMessage {
 | 
				
			||||||
 | 
					  role: 'user' | 'assistant'
 | 
				
			||||||
 | 
					  content: string
 | 
				
			||||||
 | 
					  isStreaming?: boolean
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Injectable({
 | 
				
			||||||
 | 
					  providedIn: 'root',
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class ChatService {
 | 
				
			||||||
 | 
					  constructor(private csrfService: CsrfService) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  streamChat(documentId: number, prompt: string): Observable<string> {
 | 
				
			||||||
 | 
					    return new Observable<string>((observer) => {
 | 
				
			||||||
 | 
					      const url = `${environment.apiBaseUrl}documents/chat/`
 | 
				
			||||||
 | 
					      const xhr = new XMLHttpRequest()
 | 
				
			||||||
 | 
					      let lastLength = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      xhr.open('POST', url)
 | 
				
			||||||
 | 
					      xhr.setRequestHeader('Content-Type', 'application/json')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      xhr.withCredentials = true
 | 
				
			||||||
 | 
					      let csrfToken = this.csrfService.getToken()
 | 
				
			||||||
 | 
					      if (csrfToken) {
 | 
				
			||||||
 | 
					        xhr.setRequestHeader('X-CSRFToken', csrfToken)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      xhr.onreadystatechange = () => {
 | 
				
			||||||
 | 
					        if (xhr.readyState === 3 || xhr.readyState === 4) {
 | 
				
			||||||
 | 
					          const partial = xhr.responseText.slice(lastLength)
 | 
				
			||||||
 | 
					          lastLength = xhr.responseText.length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (partial) {
 | 
				
			||||||
 | 
					            observer.next(partial)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (xhr.readyState === 4) {
 | 
				
			||||||
 | 
					          observer.complete()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      xhr.onerror = () => {
 | 
				
			||||||
 | 
					        observer.error(new Error('Streaming request failed.'))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const body = JSON.stringify({
 | 
				
			||||||
 | 
					        document_id: documentId,
 | 
				
			||||||
 | 
					        q: prompt,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      xhr.send(body)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								src-ui/src/app/services/csrf.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src-ui/src/app/services/csrf.service.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					import { Injectable } from '@angular/core'
 | 
				
			||||||
 | 
					import { Meta } from '@angular/platform-browser'
 | 
				
			||||||
 | 
					import { CookieService } from 'ngx-cookie-service' // Assuming you're using this
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Injectable({ providedIn: 'root' })
 | 
				
			||||||
 | 
					export class CsrfService {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private cookieService: CookieService,
 | 
				
			||||||
 | 
					    private meta: Meta
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public getCookiePrefix(): string {
 | 
				
			||||||
 | 
					    let prefix = ''
 | 
				
			||||||
 | 
					    if (this.meta.getTag('name=cookie_prefix')) {
 | 
				
			||||||
 | 
					      prefix = this.meta.getTag('name=cookie_prefix').content
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return prefix
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public getToken(): string {
 | 
				
			||||||
 | 
					    return this.cookieService.get(`${this.getCookiePrefix()}csrftoken`)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -48,6 +48,7 @@ import {
 | 
				
			|||||||
  caretDown,
 | 
					  caretDown,
 | 
				
			||||||
  caretUp,
 | 
					  caretUp,
 | 
				
			||||||
  chatLeftText,
 | 
					  chatLeftText,
 | 
				
			||||||
 | 
					  chatSquareDots,
 | 
				
			||||||
  check,
 | 
					  check,
 | 
				
			||||||
  check2All,
 | 
					  check2All,
 | 
				
			||||||
  checkAll,
 | 
					  checkAll,
 | 
				
			||||||
@ -256,6 +257,7 @@ const icons = {
 | 
				
			|||||||
  caretDown,
 | 
					  caretDown,
 | 
				
			||||||
  caretUp,
 | 
					  caretUp,
 | 
				
			||||||
  chatLeftText,
 | 
					  chatLeftText,
 | 
				
			||||||
 | 
					  chatSquareDots,
 | 
				
			||||||
  check,
 | 
					  check,
 | 
				
			||||||
  check2All,
 | 
					  check2All,
 | 
				
			||||||
  checkAll,
 | 
					  checkAll,
 | 
				
			||||||
 | 
				
			|||||||
@ -593,6 +593,10 @@ X_FRAME_OPTIONS = "SAMEORIGIN"
 | 
				
			|||||||
# The next 3 settings can also be set using just PAPERLESS_URL
 | 
					# The next 3 settings can also be set using just PAPERLESS_URL
 | 
				
			||||||
CSRF_TRUSTED_ORIGINS = __get_list("PAPERLESS_CSRF_TRUSTED_ORIGINS")
 | 
					CSRF_TRUSTED_ORIGINS = __get_list("PAPERLESS_CSRF_TRUSTED_ORIGINS")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if DEBUG:
 | 
				
			||||||
 | 
					    # Allow access from the angular development server during debugging
 | 
				
			||||||
 | 
					    CSRF_TRUSTED_ORIGINS.append("http://localhost:4200")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# We allow CORS from localhost:8000
 | 
					# We allow CORS from localhost:8000
 | 
				
			||||||
CORS_ALLOWED_ORIGINS = __get_list(
 | 
					CORS_ALLOWED_ORIGINS = __get_list(
 | 
				
			||||||
    "PAPERLESS_CORS_ALLOWED_HOSTS",
 | 
					    "PAPERLESS_CORS_ALLOWED_HOSTS",
 | 
				
			||||||
@ -603,6 +607,8 @@ if DEBUG:
 | 
				
			|||||||
    # Allow access from the angular development server during debugging
 | 
					    # Allow access from the angular development server during debugging
 | 
				
			||||||
    CORS_ALLOWED_ORIGINS.append("http://localhost:4200")
 | 
					    CORS_ALLOWED_ORIGINS.append("http://localhost:4200")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CORS_ALLOW_CREDENTIALS = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CORS_EXPOSE_HEADERS = [
 | 
					CORS_EXPOSE_HEADERS = [
 | 
				
			||||||
    "Content-Disposition",
 | 
					    "Content-Disposition",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user