mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-30 18:27:45 -05:00
Enhancement: system status report sanity check, simpler classifier check, styling updates (#9106)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modal-basic-title" i18n>System Status</h5>
|
||||
<h6 class="modal-title" id="modal-basic-title" i18n>System Status</h6>
|
||||
<button type="button" class="btn-close" aria-label="Close" (click)="close()"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@@ -11,11 +11,11 @@
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="row row-cols-1 row-cols-md-3 g-3">
|
||||
<div class="row row-cols-1 row-cols-md-4 g-3">
|
||||
<div class="col">
|
||||
<div class="card bg-light h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0" i18n>Environment</h5>
|
||||
<h6 class="card-title mb-0" i18n>Environment</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="card-text">
|
||||
@@ -38,37 +38,96 @@
|
||||
<div class="col">
|
||||
<div class="card bg-light h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0" i18n>Database</h5>
|
||||
<h6 class="card-title mb-0" i18n>Database</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="card-text">
|
||||
<dt i18n>Type</dt>
|
||||
<dd>{{status.database.type}}</dd>
|
||||
<dt i18n>Status</dt>
|
||||
<dd class="d-flex align-items-center">
|
||||
{{status.database.status}}
|
||||
@if (status.database.status === 'OK') {
|
||||
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" ngbPopover="{{status.database.url}}" triggers="mouseenter:mouseleave"></i-bs>
|
||||
} @else {
|
||||
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1" ngbPopover="{{status.database.url}}: {{status.database.error}}" triggers="mouseenter:mouseleave"></i-bs>
|
||||
}
|
||||
<dd>
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark text-uppercase small" [ngbPopover]="databaseStatus" triggers="click mouseenter:mouseleave">
|
||||
{{status.database.status}}
|
||||
@if (status.database.status === 'OK') {
|
||||
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1"></i-bs>
|
||||
} @else {
|
||||
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1"></i-bs>
|
||||
}
|
||||
</button>
|
||||
<ng-template #databaseStatus>
|
||||
@if (status.database.status === 'OK') {
|
||||
{{status.database.url}}
|
||||
} @else {
|
||||
{{status.database.url}}: {{status.database.error}}
|
||||
}
|
||||
</ng-template>
|
||||
</dd>
|
||||
<dt i18n>Migration Status</dt>
|
||||
<dd class="d-flex align-items-center">
|
||||
@if (status.database.migration_status.unapplied_migrations.length === 0) {
|
||||
<ng-container i18n>Up to date</ng-container><i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" [ngbPopover]="migrationStatus" triggers="mouseenter:mouseleave"></i-bs>
|
||||
} @else {
|
||||
<ng-container>{{status.database.migration_status.unapplied_migrations.length}} Pending</ng-container><i-bs name="exclamation-triangle-fill" class="text-warning ms-2 lh-1" [ngbPopover]="migrationStatus" triggers="mouseenter:mouseleave"></i-bs>
|
||||
}
|
||||
<ng-template #migrationStatus>
|
||||
<h6><ng-container i18n>Latest Migration</ng-container>:</h6> <span class="font-monospace small">{{status.database.migration_status.latest_migration}}</span>
|
||||
@if (status.database.migration_status.unapplied_migrations.length > 0) {
|
||||
<h6 class="mt-3"><ng-container i18n>Pending Migrations</ng-container>:</h6>
|
||||
<ul>
|
||||
@for (migration of status.database.migration_status.unapplied_migrations; track migration) {
|
||||
<li class="font-monospace small">{{migration}}</li>
|
||||
}
|
||||
</ul>
|
||||
<dd>
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark text-uppercase small" [ngbPopover]="migrationStatus" triggers="click mouseenter:mouseleave">
|
||||
@if (status.database.migration_status.unapplied_migrations.length === 0) {
|
||||
<ng-container i18n>Up to date</ng-container><i-bs name="check-circle-fill" class="text-primary ms-2 lh-1"></i-bs>
|
||||
} @else {
|
||||
<ng-container>{{status.database.migration_status.unapplied_migrations.length}} Pending</ng-container><i-bs name="exclamation-triangle-fill" class="text-warning ms-2 lh-1"></i-bs>
|
||||
}
|
||||
<ng-template #migrationStatus>
|
||||
<h6><ng-container i18n>Latest Migration</ng-container>:</h6> <span class="font-monospace small">{{status.database.migration_status.latest_migration}}</span>
|
||||
@if (status.database.migration_status.unapplied_migrations.length > 0) {
|
||||
<h6 class="mt-3"><ng-container i18n>Pending Migrations</ng-container>:</h6>
|
||||
<ul>
|
||||
@for (migration of status.database.migration_status.unapplied_migrations; track migration) {
|
||||
<li class="font-monospace small">{{migration}}</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</ng-template>
|
||||
</button>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="card bg-light h-100">
|
||||
<div class="card-header">
|
||||
<h6 class="card-title mb-0" i18n>Tasks Queue</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="card-text">
|
||||
<dt i18n>Redis Status</dt>
|
||||
<dd>
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark text-uppercase small" [ngbPopover]="redisStatus" triggers="click mouseenter:mouseleave">
|
||||
{{status.tasks.redis_status}}
|
||||
@if (status.tasks.redis_status === 'OK') {
|
||||
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1"></i-bs>
|
||||
} @else {
|
||||
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1"></i-bs>
|
||||
}
|
||||
</button>
|
||||
<ng-template #redisStatus>
|
||||
@if (status.tasks.redis_status === 'OK') {
|
||||
{{status.tasks.redis_url}}
|
||||
} @else {
|
||||
{{status.tasks.redis_url}}: {{status.tasks.redis_error}}
|
||||
}
|
||||
</ng-template>
|
||||
</dd>
|
||||
<dt i18n>Celery Status</dt>
|
||||
<dd>
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark text-uppercase small" [ngbPopover]="celeryStatus" triggers="click mouseenter:mouseleave">
|
||||
{{status.tasks.celery_status}}
|
||||
@if (status.tasks.celery_status === 'OK') {
|
||||
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1"></i-bs>
|
||||
} @else {
|
||||
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1"></i-bs>
|
||||
}
|
||||
</button>
|
||||
<ng-template #celeryStatus>
|
||||
@if (status.tasks.celery_status === 'OK') {
|
||||
{{status.tasks.celery_url}}
|
||||
} @else {
|
||||
{{status.tasks.celery_error}}
|
||||
}
|
||||
</ng-template>
|
||||
</dd>
|
||||
@@ -80,63 +139,109 @@
|
||||
<div class="col">
|
||||
<div class="card bg-light h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0" i18n>Tasks</h5>
|
||||
<h6 class="card-title mb-0" i18n>Health</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="card-text">
|
||||
<dt i18n>Redis Status</dt>
|
||||
<dd class="d-flex align-items-center">
|
||||
{{status.tasks.redis_status}}
|
||||
@if (status.tasks.redis_status === 'OK') {
|
||||
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" ngbPopover="{{status.tasks.redis_url}}" triggers="mouseenter:mouseleave"></i-bs>
|
||||
} @else {
|
||||
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1" ngbPopover="{{status.tasks.redis_url}}: {{status.tasks.redis_error}}" triggers="mouseenter:mouseleave"></i-bs>
|
||||
}
|
||||
</dd>
|
||||
<dt i18n>Celery Status</dt>
|
||||
<dd class="d-flex align-items-center">
|
||||
{{status.tasks.celery_status}}
|
||||
@if (status.tasks.celery_status === 'OK') {
|
||||
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1"></i-bs>
|
||||
} @else {
|
||||
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1"></i-bs>
|
||||
}
|
||||
</dd>
|
||||
<dt i18n>Search Index</dt>
|
||||
<dd class="d-flex align-items-center">
|
||||
{{status.tasks.index_status}}
|
||||
@if (status.tasks.index_status === 'OK') {
|
||||
@if (isStale(status.tasks.index_last_modified)) {
|
||||
<i-bs name="exclamation-triangle-fill" class="text-warning ms-2 lh-1" [ngbPopover]="indexStatus" triggers="mouseenter:mouseleave"></i-bs>
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark text-uppercase small" [ngbPopover]="indexStatus" triggers="click mouseenter:mouseleave">
|
||||
{{status.tasks.index_status}}
|
||||
@if (status.tasks.index_status === 'OK') {
|
||||
@if (isStale(status.tasks.index_last_modified)) {
|
||||
<i-bs name="exclamation-triangle-fill" class="text-warning ms-2 lh-1"></i-bs>
|
||||
} @else {
|
||||
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1"></i-bs>
|
||||
}
|
||||
} @else {
|
||||
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" [ngbPopover]="indexStatus" triggers="mouseenter:mouseleave"></i-bs>
|
||||
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1"></i-bs>
|
||||
}
|
||||
</button>
|
||||
@if (currentUserIsSuperUser) {
|
||||
@if (isRunning(PaperlessTaskName.IndexOptimize)) {
|
||||
<div class="spinner-border spinner-border-sm ms-2" role="status"></div>
|
||||
} @else {
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark small ms-2" (click)="runTask(PaperlessTaskName.IndexOptimize)">
|
||||
<i-bs name="play-fill"></i-bs>
|
||||
<ng-container i18n>Run Task</ng-container>
|
||||
</button>
|
||||
}
|
||||
} @else {
|
||||
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1" ngbPopover="{{status.tasks.index_error}}" triggers="mouseenter:mouseleave"></i-bs>
|
||||
}
|
||||
</dd>
|
||||
<ng-template #indexStatus>
|
||||
<h6><ng-container i18n>Last Updated</ng-container>:</h6> <span class="font-monospace small">{{status.tasks.index_last_modified | customDate:'medium'}}</span>
|
||||
@if (status.tasks.index_status === 'OK') {
|
||||
<h6><ng-container i18n>Last Updated</ng-container>:</h6> <span class="font-monospace small">{{status.tasks.index_last_modified | customDate:'medium'}}</span>
|
||||
} @else {
|
||||
<h6><ng-container i18n>Error</ng-container>:</h6> <span class="font-monospace small">{{status.tasks.index_error}}</span>
|
||||
}
|
||||
</ng-template>
|
||||
<dt i18n>Classifier</dt>
|
||||
<dd class="d-flex align-items-center">
|
||||
{{status.tasks.classifier_status}}
|
||||
@if (status.tasks.classifier_status === 'OK') {
|
||||
@if (isStale(status.tasks.classifier_last_trained)) {
|
||||
<i-bs name="exclamation-triangle-fill" class="text-warning ms-2 lh-1" [ngbPopover]="classifierStatus" triggers="mouseenter:mouseleave"></i-bs>
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark text-uppercase small" [ngbPopover]="classifierStatus" triggers="click mouseenter:mouseleave">
|
||||
{{status.tasks.classifier_status}}
|
||||
@if (status.tasks.classifier_status === 'OK') {
|
||||
@if (isStale(status.tasks.classifier_last_trained)) {
|
||||
<i-bs name="exclamation-triangle-fill" class="text-warning ms-2 lh-1"></i-bs>
|
||||
} @else {
|
||||
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1"></i-bs>
|
||||
}
|
||||
} @else {
|
||||
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" [ngbPopover]="classifierStatus" triggers="mouseenter:mouseleave"></i-bs>
|
||||
}
|
||||
} @else {
|
||||
<i-bs name="exclamation-triangle-fill" class="ms-2 lh-1"
|
||||
[class.text-danger]="status.tasks.classifier_status === SystemStatusItemStatus.ERROR"
|
||||
[class.text-warning]="status.tasks.classifier_status === SystemStatusItemStatus.WARNING"
|
||||
ngbPopover="{{status.tasks.classifier_error}}"
|
||||
triggers="mouseenter:mouseleave"></i-bs>
|
||||
[class.text-warning]="status.tasks.classifier_status === SystemStatusItemStatus.WARNING"></i-bs>
|
||||
}
|
||||
</button>
|
||||
@if (currentUserIsSuperUser) {
|
||||
@if (isRunning(PaperlessTaskName.TrainClassifier)) {
|
||||
<div class="spinner-border spinner-border-sm ms-2" role="status"></div>
|
||||
} @else {
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark small ms-2" (click)="runTask(PaperlessTaskName.TrainClassifier)">
|
||||
<i-bs name="play-fill"></i-bs>
|
||||
<ng-container i18n>Run Task</ng-container>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</dd>
|
||||
<ng-template #classifierStatus>
|
||||
<h6><ng-container i18n>Last Trained</ng-container>:</h6> <span class="font-monospace small">{{status.tasks.classifier_last_trained | customDate:'medium'}}</span>
|
||||
@if (status.tasks.classifier_status === 'OK') {
|
||||
<h6><ng-container i18n>Last Trained</ng-container>:</h6> <span class="font-monospace small">{{status.tasks.classifier_last_trained | customDate:'medium'}}</span>
|
||||
} @else {
|
||||
<h6><ng-container i18n>Error</ng-container>:</h6> <span class="font-monospace small">{{status.tasks.classifier_error}}</span>
|
||||
}
|
||||
</ng-template>
|
||||
<dt i18n>Sanity Checker</dt>
|
||||
<dd class="d-flex align-items-center">
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark text-uppercase small" [ngbPopover]="sanityCheckerStatus" triggers="click mouseenter:mouseleave">
|
||||
{{status.tasks.sanity_check_status}}
|
||||
@if (status.tasks.sanity_check_status === 'OK') {
|
||||
@if (isStale(status.tasks.sanity_check_last_run)) {
|
||||
<i-bs name="exclamation-triangle-fill" class="text-warning ms-2 lh-1"></i-bs>
|
||||
} @else {
|
||||
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1"></i-bs>
|
||||
}
|
||||
} @else {
|
||||
<i-bs name="exclamation-triangle-fill" class="ms-2 lh-1"
|
||||
[class.text-danger]="status.tasks.sanity_check_status === SystemStatusItemStatus.ERROR"
|
||||
[class.text-warning]="status.tasks.sanity_check_status === SystemStatusItemStatus.WARNING"></i-bs>
|
||||
}
|
||||
</button>
|
||||
@if (currentUserIsSuperUser) {
|
||||
@if (isRunning(PaperlessTaskName.SanityCheck)) {
|
||||
<div class="spinner-border spinner-border-sm ms-2" role="status"></div>
|
||||
} @else {
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark small ms-2" (click)="runTask(PaperlessTaskName.SanityCheck)">
|
||||
<i-bs name="play-fill"></i-bs>
|
||||
<ng-container i18n>Run Task</ng-container>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</dd>
|
||||
<ng-template #sanityCheckerStatus>
|
||||
@if (status.tasks.sanity_check_status === 'OK') {
|
||||
<h6><ng-container i18n>Last Run</ng-container>:</h6> <span class="font-monospace small">{{status.tasks.sanity_check_last_run | customDate:'medium'}}</span>
|
||||
} @else {
|
||||
<h6><ng-container i18n>Error</ng-container>:</h6> <span class="font-monospace small">{{status.tasks.sanity_check_error}}</span>
|
||||
}
|
||||
</ng-template>
|
||||
</dl>
|
||||
</div>
|
||||
@@ -146,7 +251,7 @@
|
||||
}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-sm btn-outline-secondary" (click)="copy()">
|
||||
<button class="btn btn-sm d-flex align-items-center btn-dark btn btn-sm d-flex align-items-center btn-dark btn-outline-secondary" (click)="copy()">
|
||||
@if (!copied) {
|
||||
<i-bs name="clipboard-fill"></i-bs>
|
||||
}
|
||||
|
@@ -0,0 +1,3 @@
|
||||
.btn.small {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
@@ -9,11 +9,16 @@ import {
|
||||
} from '@angular/core/testing'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { of, throwError } from 'rxjs'
|
||||
import { PaperlessTaskName } from 'src/app/data/paperless-task'
|
||||
import {
|
||||
InstallType,
|
||||
SystemStatus,
|
||||
SystemStatusItemStatus,
|
||||
} from 'src/app/data/system-status'
|
||||
import { SystemStatusService } from 'src/app/services/system-status.service'
|
||||
import { TasksService } from 'src/app/services/tasks.service'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { SystemStatusDialogComponent } from './system-status-dialog.component'
|
||||
|
||||
const status: SystemStatus = {
|
||||
@@ -36,12 +41,17 @@ const status: SystemStatus = {
|
||||
redis_status: SystemStatusItemStatus.ERROR,
|
||||
redis_error: 'Error 61 connecting to localhost:6379. Connection refused.',
|
||||
celery_status: SystemStatusItemStatus.ERROR,
|
||||
celery_url: 'celery@localhost',
|
||||
celery_error: 'Error connecting to celery@localhost',
|
||||
index_status: SystemStatusItemStatus.OK,
|
||||
index_last_modified: new Date().toISOString(),
|
||||
index_error: null,
|
||||
classifier_status: SystemStatusItemStatus.OK,
|
||||
classifier_last_trained: new Date().toISOString(),
|
||||
classifier_error: null,
|
||||
sanity_check_status: SystemStatusItemStatus.OK,
|
||||
sanity_check_last_run: new Date().toISOString(),
|
||||
sanity_check_error: null,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -49,6 +59,9 @@ describe('SystemStatusDialogComponent', () => {
|
||||
let component: SystemStatusDialogComponent
|
||||
let fixture: ComponentFixture<SystemStatusDialogComponent>
|
||||
let clipboard: Clipboard
|
||||
let tasksService: TasksService
|
||||
let systemStatusService: SystemStatusService
|
||||
let toastService: ToastService
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
@@ -67,6 +80,9 @@ describe('SystemStatusDialogComponent', () => {
|
||||
component = fixture.componentInstance
|
||||
component.status = status
|
||||
clipboard = TestBed.inject(Clipboard)
|
||||
tasksService = TestBed.inject(TasksService)
|
||||
systemStatusService = TestBed.inject(SystemStatusService)
|
||||
toastService = TestBed.inject(ToastService)
|
||||
fixture.detectChanges()
|
||||
})
|
||||
|
||||
@@ -93,4 +109,37 @@ describe('SystemStatusDialogComponent', () => {
|
||||
expect(component.isStale(date.toISOString())).toBeTruthy()
|
||||
expect(component.isStale(date.toISOString(), 26)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should check if task is running', () => {
|
||||
component.runTask(PaperlessTaskName.IndexOptimize)
|
||||
expect(component.isRunning(PaperlessTaskName.IndexOptimize)).toBeTruthy()
|
||||
expect(component.isRunning(PaperlessTaskName.SanityCheck)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should support running tasks, refresh status and show toasts', () => {
|
||||
const toastSpy = jest.spyOn(toastService, 'showInfo')
|
||||
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
||||
const getStatusSpy = jest.spyOn(systemStatusService, 'get')
|
||||
const runSpy = jest.spyOn(tasksService, 'run')
|
||||
|
||||
// fail first
|
||||
runSpy.mockReturnValue(throwError(() => new Error('error')))
|
||||
component.runTask(PaperlessTaskName.IndexOptimize)
|
||||
expect(runSpy).toHaveBeenCalledWith(PaperlessTaskName.IndexOptimize)
|
||||
expect(toastErrorSpy).toHaveBeenCalledWith(
|
||||
`Failed to start task ${PaperlessTaskName.IndexOptimize}, see the logs for more details`,
|
||||
expect.any(Error)
|
||||
)
|
||||
|
||||
// succeed
|
||||
runSpy.mockReturnValue(of({}))
|
||||
getStatusSpy.mockReturnValue(of(status))
|
||||
component.runTask(PaperlessTaskName.IndexOptimize)
|
||||
expect(runSpy).toHaveBeenCalledWith(PaperlessTaskName.IndexOptimize)
|
||||
|
||||
expect(getStatusSpy).toHaveBeenCalled()
|
||||
expect(toastSpy).toHaveBeenCalledWith(
|
||||
`Task ${PaperlessTaskName.IndexOptimize} started`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@@ -7,12 +7,17 @@ import {
|
||||
NgbProgressbarModule,
|
||||
} from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { PaperlessTaskName } from 'src/app/data/paperless-task'
|
||||
import {
|
||||
SystemStatus,
|
||||
SystemStatusItemStatus,
|
||||
} from 'src/app/data/system-status'
|
||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||
import { FileSizePipe } from 'src/app/pipes/file-size.pipe'
|
||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||
import { SystemStatusService } from 'src/app/services/system-status.service'
|
||||
import { TasksService } from 'src/app/services/tasks.service'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
|
||||
@Component({
|
||||
selector: 'pngx-system-status-dialog',
|
||||
@@ -30,13 +35,24 @@ import { FileSizePipe } from 'src/app/pipes/file-size.pipe'
|
||||
})
|
||||
export class SystemStatusDialogComponent {
|
||||
public SystemStatusItemStatus = SystemStatusItemStatus
|
||||
public PaperlessTaskName = PaperlessTaskName
|
||||
public status: SystemStatus
|
||||
|
||||
public copied: boolean = false
|
||||
|
||||
private runningTasks: Set<PaperlessTaskName> = new Set()
|
||||
|
||||
get currentUserIsSuperUser(): boolean {
|
||||
return this.permissionsService.isSuperUser()
|
||||
}
|
||||
|
||||
constructor(
|
||||
public activeModal: NgbActiveModal,
|
||||
private clipboard: Clipboard
|
||||
private clipboard: Clipboard,
|
||||
private systemStatusService: SystemStatusService,
|
||||
private tasksService: TasksService,
|
||||
private toastService: ToastService,
|
||||
private permissionsService: PermissionsService
|
||||
) {}
|
||||
|
||||
public close() {
|
||||
@@ -56,4 +72,30 @@ export class SystemStatusDialogComponent {
|
||||
const now = new Date()
|
||||
return now.getTime() - date.getTime() > hours * 60 * 60 * 1000
|
||||
}
|
||||
|
||||
public isRunning(taskName: PaperlessTaskName): boolean {
|
||||
return this.runningTasks.has(taskName)
|
||||
}
|
||||
|
||||
public runTask(taskName: PaperlessTaskName) {
|
||||
this.runningTasks.add(taskName)
|
||||
this.toastService.showInfo(`Task ${taskName} started`)
|
||||
this.tasksService.run(taskName).subscribe({
|
||||
next: () => {
|
||||
this.runningTasks.delete(taskName)
|
||||
this.systemStatusService.get().subscribe({
|
||||
next: (status) => {
|
||||
this.status = status
|
||||
},
|
||||
})
|
||||
},
|
||||
error: (err) => {
|
||||
this.runningTasks.delete(taskName)
|
||||
this.toastService.showError(
|
||||
`Failed to start task ${taskName}, see the logs for more details`,
|
||||
err
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user