added paperless ui

This commit is contained in:
Jonas Winkler
2020-10-27 01:10:18 +01:00
parent e24baf5811
commit 8693bee4ac
173 changed files with 18693 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { AuthGuardService } from './auth-guard.service';
describe('AuthGuardService', () => {
let service: AuthGuardService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AuthGuardService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,20 @@
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from './auth.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuardService {
constructor(public auth: AuthService, public router: Router) { }
canActivate(): boolean {
if (!this.auth.isAuthenticated()) {
this.router.navigate(['login']);
return false;
}
return true;
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { AuthInterceptor } from './auth.interceptor';
describe('AuthInterceptor', () => {
beforeEach(() => TestBed.configureTestingModule({
providers: [
AuthInterceptor
]
}));
it('should be created', () => {
const interceptor: AuthInterceptor = TestBed.inject(AuthInterceptor);
expect(interceptor).toBeTruthy();
});
});

View File

@@ -0,0 +1,26 @@
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
if (this.authService.isAuthenticated()) {
request = request.clone({
setHeaders: {
Authorization: 'Token ' + this.authService.getToken()
}
});
}
return next.handle(request);
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AuthService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,76 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { environment } from 'src/environments/environment';
interface TokenResponse {
token: string
}
@Injectable({
providedIn: 'root'
})
export class AuthService {
private currentUsername: string
private token: string
constructor(private http: HttpClient, private router: Router) {
this.token = localStorage.getItem('auth-service:token')
if (this.token == null) {
this.token = sessionStorage.getItem('auth-service:token')
}
this.currentUsername = localStorage.getItem('auth-service:currentUsername')
if (this.currentUsername == null) {
this.currentUsername = sessionStorage.getItem('auth-service:currentUsername')
}
}
private requestToken(username: string, password: string): Observable<TokenResponse> {
return this.http.post<TokenResponse>(`${environment.apiBaseUrl}token/`, {"username": username, "password": password})
}
isAuthenticated(): boolean {
return this.currentUsername != null
}
logout() {
this.currentUsername = null
this.token = null
localStorage.removeItem('auth-service:token')
localStorage.removeItem('auth-service:currentUsername')
sessionStorage.removeItem('auth-service:token')
sessionStorage.removeItem('auth-service:currentUsername')
this.router.navigate(['login'])
}
login(username: string, password: string, rememberMe: boolean): Observable<boolean> {
return this.requestToken(username,password).pipe(
map(tokenResponse => {
this.currentUsername = username
this.token = tokenResponse.token
if (rememberMe) {
localStorage.setItem('auth-service:token', this.token)
localStorage.setItem('auth-service:currentUsername', this.currentUsername)
} else {
sessionStorage.setItem('auth-service:token', this.token)
sessionStorage.setItem('auth-service:currentUsername', this.currentUsername)
}
return true
})
)
}
getToken(): string {
return this.token
}
getCurrentUsername(): string {
return this.currentUsername
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { DocumentListViewService } from './document-list-view.service';
describe('DocumentListViewService', () => {
let service: DocumentListViewService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DocumentListViewService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,101 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { FilterRuleSet } from '../components/filter-editor/filter-editor.component';
import { PaperlessDocument } from '../data/paperless-document';
import { DocumentService } from './rest/document.service';
@Injectable({
providedIn: 'root'
})
export class DocumentListViewService {
static DEFAULT_SORT_FIELD = 'created'
static SORT_FIELDS = [
{field: "correspondent__name", name: "Correspondent"},
{field: 'title', name: 'Title'},
{field: 'archive_serial_number', name: 'ASN'},
{field: 'created', name: 'Created'},
{field: 'added', name: 'Added'},
{field: 'modified', name: 'Modified'}
]
documents: PaperlessDocument[] = []
currentPage = 1
collectionSize: number
currentFilter = new FilterRuleSet()
currentSortDirection = 'des'
currentSortField = DocumentListViewService.DEFAULT_SORT_FIELD
reload(onFinish?) {
this.documentService.list(
this.currentPage,
null,
this.getOrderingQueryParam(),
this.currentFilter.toQueryParams()).subscribe(
result => {
this.collectionSize = result.count
this.documents = result.results
if (onFinish) {
onFinish()
}
},
error => {
if (error.error['detail'] == 'Invalid page.') {
this.currentPage = 1
this.reload()
}
})
}
getOrderingQueryParam() {
if (DocumentListViewService.SORT_FIELDS.find(f => f.field == this.currentSortField)) {
return (this.currentSortDirection == 'des' ? '-' : '') + this.currentSortField
} else {
return DocumentListViewService.DEFAULT_SORT_FIELD
}
}
setFilter(filter: FilterRuleSet) {
this.currentFilter = filter
}
getLastPage(): number {
return Math.ceil(this.collectionSize / 25)
}
hasNext(doc: number) {
if (this.documents) {
let index = this.documents.findIndex(d => d.id == doc)
return index != -1 && (this.currentPage < this.getLastPage() || (index + 1) < this.documents.length)
}
}
getNext(currentDocId: number): Observable<number> {
return new Observable(nextDocId => {
if (this.documents != null) {
let index = this.documents.findIndex(d => d.id == currentDocId)
if (index != -1 && (index + 1) < this.documents.length) {
nextDocId.next(this.documents[index+1].id)
nextDocId.complete()
} else if (index != -1 && this.currentPage < this.getLastPage()) {
this.currentPage += 1
this.reload(() => {
nextDocId.next(this.documents[0].id)
nextDocId.complete()
})
} else {
nextDocId.complete()
}
} else {
nextDocId.complete()
}
})
}
constructor(private documentService: DocumentService) { }
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { OpenDocumentsService } from './open-documents.service';
describe('OpenDocumentsService', () => {
let service: OpenDocumentsService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(OpenDocumentsService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,35 @@
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { PaperlessDocument } from '../data/paperless-document';
@Injectable({
providedIn: 'root'
})
export class OpenDocumentsService {
constructor() { }
private openDocuments: PaperlessDocument[] = []
private openDocumentsSubject: Subject<PaperlessDocument[]> = new Subject()
getOpenDocuments(): Observable<PaperlessDocument[]> {
return this.openDocumentsSubject
}
openDocument(doc: PaperlessDocument) {
if (this.openDocuments.find(d => d.id == doc.id) == null) {
this.openDocuments.push(doc)
this.openDocumentsSubject.next(this.openDocuments)
}
}
closeDocument(doc: PaperlessDocument) {
let index = this.openDocuments.findIndex(d => d.id == doc.id)
if (index > -1) {
this.openDocuments.splice(index, 1)
this.openDocumentsSubject.next(this.openDocuments)
}
}
}

View File

@@ -0,0 +1,7 @@
import { AbstractPaperlessService } from './abstract-paperless-service';
describe('AbstractPaperlessService', () => {
it('should create an instance', () => {
expect(new AbstractPaperlessService()).toBeTruthy();
});
});

View File

@@ -0,0 +1,58 @@
import { HttpClient, HttpParams } from '@angular/common/http'
import { Observable } from 'rxjs'
import { ObjectWithId } from 'src/app/data/object-with-id'
import { Results } from 'src/app/data/results'
import { environment } from 'src/environments/environment'
export abstract class AbstractPaperlessService<T extends ObjectWithId> {
protected baseUrl: string = environment.apiBaseUrl
constructor(protected http: HttpClient, private resourceName: string) { }
protected getResourceUrl(id?: number, action?: string): string {
let url = `${this.baseUrl}${this.resourceName}/`
if (id) {
url += `${id}/`
if (action) {
url += `${action}/`
}
}
return url
}
list(page?: number, pageSize?: number, ordering?: string, extraParams?): Observable<Results<T>> {
let httpParams = new HttpParams()
if (page) {
httpParams = httpParams.set('page', page.toString())
}
if (pageSize) {
httpParams = httpParams.set('page_size', pageSize.toString())
}
if (ordering) {
httpParams = httpParams.set('ordering', ordering)
}
for (let extraParamKey in extraParams) {
if (extraParams[extraParamKey]) {
httpParams = httpParams.set(extraParamKey, extraParams[extraParamKey])
}
}
return this.http.get<Results<T>>(this.getResourceUrl(), {params: httpParams})
}
get(id: number): Observable<T> {
return this.http.get<T>(this.getResourceUrl(id))
}
create(o: T): Observable<T> {
return this.http.post<T>(this.getResourceUrl(), o)
}
delete(o: T): Observable<any> {
return this.http.delete(this.getResourceUrl(o.id))
}
update(o: T): Observable<T> {
return this.http.put<T>(this.getResourceUrl(o.id), o)
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { CorrespondentService } from './correspondent.service';
describe('CorrespondentService', () => {
let service: CorrespondentService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CorrespondentService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,15 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
import { AbstractPaperlessService } from './abstract-paperless-service';
@Injectable({
providedIn: 'root'
})
export class CorrespondentService extends AbstractPaperlessService<PaperlessCorrespondent> {
constructor(http: HttpClient) {
super(http, 'correspondents')
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { DocumentTypeService } from './document-type.service';
describe('DocumentTypeService', () => {
let service: DocumentTypeService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DocumentTypeService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,14 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
import { AbstractPaperlessService } from './abstract-paperless-service';
@Injectable({
providedIn: 'root'
})
export class DocumentTypeService extends AbstractPaperlessService<PaperlessDocumentType> {
constructor(http: HttpClient) {
super(http, 'document_types')
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { DocumentService } from './document.service';
describe('DocumentService', () => {
let service: DocumentService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DocumentService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,29 @@
import { Injectable } from '@angular/core';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { AbstractPaperlessService } from './abstract-paperless-service';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { AuthService } from '../auth.service';
@Injectable({
providedIn: 'root'
})
export class DocumentService extends AbstractPaperlessService<PaperlessDocument> {
constructor(http: HttpClient, private auth: AuthService) {
super(http, 'documents')
}
getPreviewUrl(id: number): string {
return this.getResourceUrl(id, 'preview') + `?auth_token=${this.auth.getToken()}`
}
getThumbUrl(id: number): string {
return this.getResourceUrl(id, 'thumb') + `?auth_token=${this.auth.getToken()}`
}
getDownloadUrl(id: number): string {
return this.getResourceUrl(id, 'download') + `?auth_token=${this.auth.getToken()}`
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { SearchService } from './search.service';
describe('SearchService', () => {
let service: SearchService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(SearchService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,37 @@
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { environment } from 'src/environments/environment';
export class SearchResultHighlightedText {
text?: string
term?: number
toString(): string {
return this.text
}
}
export class SearchResult {
id?: number
title?: string
content?: string
score?: number
highlights?: SearchResultHighlightedText[][]
document?: PaperlessDocument
}
@Injectable({
providedIn: 'root'
})
export class SearchService {
constructor(private http: HttpClient) { }
search(query: string): Observable<SearchResult[]> {
return this.http.get<SearchResult[]>(`${environment.apiBaseUrl}search/`, {params: new HttpParams().set('query', query)})
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { TagService } from './tag.service';
describe('TagService', () => {
let service: TagService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(TagService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,14 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { PaperlessTag } from 'src/app/data/paperless-tag';
import { AbstractPaperlessService } from './abstract-paperless-service';
@Injectable({
providedIn: 'root'
})
export class TagService extends AbstractPaperlessService<PaperlessTag> {
constructor(http: HttpClient) {
super(http, 'tags')
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ToastService } from './toast.service';
describe('ToastService', () => {
let service: ToastService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ToastService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,52 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
export class Toast {
static make(title: string, content: string, delay?: number): Toast {
let t = new Toast()
t.title = title
t.content = content
if (delay) {
t.delay = delay
}
return t
}
title: string
content: string
delay: number = 5000
}
@Injectable({
providedIn: 'root'
})
export class ToastService {
constructor() { }
private toasts: Toast[] = []
private toastSubject: Subject<Toast[]> = new Subject()
showToast(toast: Toast) {
this.toasts.push(toast)
this.toastSubject.next(this.toasts)
}
closeToast(toast: Toast) {
let index = this.toasts.findIndex(t => t == toast)
if (index > -1) {
this.toasts.splice(index, 1)
this.toastSubject.next(this.toasts)
}
}
getToasts() {
return this.toastSubject
}
}