diff options
Diffstat (limited to 'client')
19 files changed, 358 insertions, 25 deletions
diff --git a/client/src/app/+admin/admin-routing.module.ts b/client/src/app/+admin/admin-routing.module.ts index ca31ba585..215da1e4f 100644 --- a/client/src/app/+admin/admin-routing.module.ts +++ b/client/src/app/+admin/admin-routing.module.ts | |||
@@ -6,9 +6,9 @@ import { MetaGuard } from '@ngx-meta/core' | |||
6 | 6 | ||
7 | import { AdminComponent } from './admin.component' | 7 | import { AdminComponent } from './admin.component' |
8 | import { FollowsRoutes } from './follows' | 8 | import { FollowsRoutes } from './follows' |
9 | import { JobsRoutes } from './jobs/job.routes' | ||
10 | import { UsersRoutes } from './users' | 9 | import { UsersRoutes } from './users' |
11 | import { ModerationRoutes } from '@app/+admin/moderation/moderation.routes' | 10 | import { ModerationRoutes } from '@app/+admin/moderation/moderation.routes' |
11 | import { SystemRoutes } from '@app/+admin/system' | ||
12 | 12 | ||
13 | const adminRoutes: Routes = [ | 13 | const adminRoutes: Routes = [ |
14 | { | 14 | { |
@@ -25,7 +25,7 @@ const adminRoutes: Routes = [ | |||
25 | ...FollowsRoutes, | 25 | ...FollowsRoutes, |
26 | ...UsersRoutes, | 26 | ...UsersRoutes, |
27 | ...ModerationRoutes, | 27 | ...ModerationRoutes, |
28 | ...JobsRoutes, | 28 | ...SystemRoutes, |
29 | ...ConfigRoutes | 29 | ...ConfigRoutes |
30 | ] | 30 | ] |
31 | } | 31 | } |
diff --git a/client/src/app/+admin/admin.component.html b/client/src/app/+admin/admin.component.html index 345fb9f5a..065d92509 100644 --- a/client/src/app/+admin/admin.component.html +++ b/client/src/app/+admin/admin.component.html | |||
@@ -12,13 +12,13 @@ | |||
12 | Moderation | 12 | Moderation |
13 | </a> | 13 | </a> |
14 | 14 | ||
15 | <a i18n *ngIf="hasJobsRight()" routerLink="/admin/jobs" routerLinkActive="active" class="title-page"> | ||
16 | Jobs | ||
17 | </a> | ||
18 | |||
19 | <a i18n *ngIf="hasConfigRight()" routerLink="/admin/config" routerLinkActive="active" class="title-page"> | 15 | <a i18n *ngIf="hasConfigRight()" routerLink="/admin/config" routerLinkActive="active" class="title-page"> |
20 | Configuration | 16 | Configuration |
21 | </a> | 17 | </a> |
18 | |||
19 | <a i18n *ngIf="hasJobsRight() || hasLogsRight()" routerLink="/admin/system" routerLinkActive="active" class="title-page"> | ||
20 | System | ||
21 | </a> | ||
22 | </div> | 22 | </div> |
23 | 23 | ||
24 | <div class="margin-content"> | 24 | <div class="margin-content"> |
diff --git a/client/src/app/+admin/admin.component.ts b/client/src/app/+admin/admin.component.ts index b4b807c67..fc775a5a4 100644 --- a/client/src/app/+admin/admin.component.ts +++ b/client/src/app/+admin/admin.component.ts | |||
@@ -28,6 +28,10 @@ export class AdminComponent { | |||
28 | return this.auth.getUser().hasRight(UserRight.MANAGE_JOBS) | 28 | return this.auth.getUser().hasRight(UserRight.MANAGE_JOBS) |
29 | } | 29 | } |
30 | 30 | ||
31 | hasLogsRight () { | ||
32 | return this.auth.getUser().hasRight(UserRight.MANAGE_LOGS) | ||
33 | } | ||
34 | |||
31 | hasConfigRight () { | 35 | hasConfigRight () { |
32 | return this.auth.getUser().hasRight(UserRight.MANAGE_CONFIGURATION) | 36 | return this.auth.getUser().hasRight(UserRight.MANAGE_CONFIGURATION) |
33 | } | 37 | } |
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts index 282d59634..ae0af686b 100644 --- a/client/src/app/+admin/admin.module.ts +++ b/client/src/app/+admin/admin.module.ts | |||
@@ -7,20 +7,19 @@ import { AdminRoutingModule } from './admin-routing.module' | |||
7 | import { AdminComponent } from './admin.component' | 7 | import { AdminComponent } from './admin.component' |
8 | import { FollowersListComponent, FollowingAddComponent, FollowsComponent, FollowService } from './follows' | 8 | import { FollowersListComponent, FollowingAddComponent, FollowsComponent, FollowService } from './follows' |
9 | import { FollowingListComponent } from './follows/following-list/following-list.component' | 9 | import { FollowingListComponent } from './follows/following-list/following-list.component' |
10 | import { JobsComponent } from './jobs/job.component' | 10 | import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersComponent, UserUpdateComponent } from './users' |
11 | import { JobsListComponent } from './jobs/jobs-list/jobs-list.component' | ||
12 | import { JobService } from './jobs/shared/job.service' | ||
13 | import { UserCreateComponent, UserListComponent, UsersComponent, UserUpdateComponent, UserPasswordComponent } from './users' | ||
14 | import { | 11 | import { |
15 | ModerationCommentModalComponent, | 12 | ModerationCommentModalComponent, |
16 | VideoAbuseListComponent, | 13 | VideoAbuseListComponent, |
17 | VideoBlacklistListComponent, | 14 | VideoAutoBlacklistListComponent, |
18 | VideoAutoBlacklistListComponent | 15 | VideoBlacklistListComponent |
19 | } from './moderation' | 16 | } from './moderation' |
20 | import { ModerationComponent } from '@app/+admin/moderation/moderation.component' | 17 | import { ModerationComponent } from '@app/+admin/moderation/moderation.component' |
21 | import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component' | 18 | import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component' |
22 | import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service' | 19 | import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service' |
23 | import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist' | 20 | import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist' |
21 | import { JobsComponent } from '@app/+admin/system/jobs/jobs.component' | ||
22 | import { JobService, LogsComponent, LogsService, SystemComponent } from '@app/+admin/system' | ||
24 | 23 | ||
25 | @NgModule({ | 24 | @NgModule({ |
26 | imports: [ | 25 | imports: [ |
@@ -52,8 +51,9 @@ import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } f | |||
52 | InstanceServerBlocklistComponent, | 51 | InstanceServerBlocklistComponent, |
53 | InstanceAccountBlocklistComponent, | 52 | InstanceAccountBlocklistComponent, |
54 | 53 | ||
54 | SystemComponent, | ||
55 | JobsComponent, | 55 | JobsComponent, |
56 | JobsListComponent, | 56 | LogsComponent, |
57 | 57 | ||
58 | ConfigComponent, | 58 | ConfigComponent, |
59 | EditCustomConfigComponent | 59 | EditCustomConfigComponent |
@@ -67,6 +67,7 @@ import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } f | |||
67 | FollowService, | 67 | FollowService, |
68 | RedundancyService, | 68 | RedundancyService, |
69 | JobService, | 69 | JobService, |
70 | LogsService, | ||
70 | ConfigService | 71 | ConfigService |
71 | ] | 72 | ] |
72 | }) | 73 | }) |
diff --git a/client/src/app/+admin/system/index.ts b/client/src/app/+admin/system/index.ts new file mode 100644 index 000000000..226d999d2 --- /dev/null +++ b/client/src/app/+admin/system/index.ts | |||
@@ -0,0 +1,4 @@ | |||
1 | export * from './jobs' | ||
2 | export * from './logs' | ||
3 | export * from './system.component' | ||
4 | export * from './system.routes' | ||
diff --git a/client/src/app/+admin/system/jobs/index.ts b/client/src/app/+admin/system/jobs/index.ts index c0e0cc95d..486a745e4 100644 --- a/client/src/app/+admin/system/jobs/index.ts +++ b/client/src/app/+admin/system/jobs/index.ts | |||
@@ -1,4 +1,2 @@ | |||
1 | export * from './shared' | 1 | export * from './job.service' |
2 | export * from './jobs-list' | 2 | export * from './jobs.component' |
3 | export * from './job.routes' | ||
4 | export * from './job.component' | ||
diff --git a/client/src/app/+admin/system/jobs/jobs.component.ts b/client/src/app/+admin/system/jobs/jobs.component.ts index b265e1dd6..ebfb52779 100644 --- a/client/src/app/+admin/system/jobs/jobs.component.ts +++ b/client/src/app/+admin/system/jobs/jobs.component.ts | |||
@@ -5,15 +5,15 @@ import { SortMeta } from 'primeng/primeng' | |||
5 | import { Job } from '../../../../../../shared/index' | 5 | import { Job } from '../../../../../../shared/index' |
6 | import { JobState } from '../../../../../../shared/models' | 6 | import { JobState } from '../../../../../../shared/models' |
7 | import { RestPagination, RestTable } from '../../../shared' | 7 | import { RestPagination, RestTable } from '../../../shared' |
8 | import { JobService } from '../shared' | 8 | import { JobService } from './job.service' |
9 | import { I18n } from '@ngx-translate/i18n-polyfill' | 9 | import { I18n } from '@ngx-translate/i18n-polyfill' |
10 | 10 | ||
11 | @Component({ | 11 | @Component({ |
12 | selector: 'my-jobs-list', | 12 | selector: 'my-jobs', |
13 | templateUrl: './jobs-list.component.html', | 13 | templateUrl: './jobs.component.html', |
14 | styleUrls: [ './jobs-list.component.scss' ] | 14 | styleUrls: [ './jobs.component.scss' ] |
15 | }) | 15 | }) |
16 | export class JobsListComponent extends RestTable implements OnInit { | 16 | export class JobsComponent extends RestTable implements OnInit { |
17 | private static JOB_STATE_LOCAL_STORAGE_STATE = 'jobs-list-state' | 17 | private static JOB_STATE_LOCAL_STORAGE_STATE = 'jobs-list-state' |
18 | 18 | ||
19 | jobState: JobState = 'waiting' | 19 | jobState: JobState = 'waiting' |
@@ -58,12 +58,12 @@ export class JobsListComponent extends RestTable implements OnInit { | |||
58 | } | 58 | } |
59 | 59 | ||
60 | private loadJobState () { | 60 | private loadJobState () { |
61 | const result = peertubeLocalStorage.getItem(JobsListComponent.JOB_STATE_LOCAL_STORAGE_STATE) | 61 | const result = peertubeLocalStorage.getItem(JobsComponent.JOB_STATE_LOCAL_STORAGE_STATE) |
62 | 62 | ||
63 | if (result) this.jobState = result as JobState | 63 | if (result) this.jobState = result as JobState |
64 | } | 64 | } |
65 | 65 | ||
66 | private saveJobState () { | 66 | private saveJobState () { |
67 | peertubeLocalStorage.setItem(JobsListComponent.JOB_STATE_LOCAL_STORAGE_STATE, this.jobState) | 67 | peertubeLocalStorage.setItem(JobsComponent.JOB_STATE_LOCAL_STORAGE_STATE, this.jobState) |
68 | } | 68 | } |
69 | } | 69 | } |
diff --git a/client/src/app/+admin/system/logs/index.ts b/client/src/app/+admin/system/logs/index.ts new file mode 100644 index 000000000..7b56d4237 --- /dev/null +++ b/client/src/app/+admin/system/logs/index.ts | |||
@@ -0,0 +1,2 @@ | |||
1 | export * from './logs.component' | ||
2 | export * from './logs.service' | ||
diff --git a/client/src/app/+admin/system/logs/log-row.model.ts b/client/src/app/+admin/system/logs/log-row.model.ts new file mode 100644 index 000000000..9bc7dafdd --- /dev/null +++ b/client/src/app/+admin/system/logs/log-row.model.ts | |||
@@ -0,0 +1,21 @@ | |||
1 | import { LogLevel } from '@shared/models/server/log-level.type' | ||
2 | import omit from 'lodash-es/omit' | ||
3 | |||
4 | export class LogRow { | ||
5 | date: Date | ||
6 | localeDate: string | ||
7 | level: LogLevel | ||
8 | message: string | ||
9 | meta: string | ||
10 | |||
11 | constructor (row: any) { | ||
12 | this.date = new Date(row.timestamp) | ||
13 | this.localeDate = this.date.toLocaleString() | ||
14 | this.level = row.level | ||
15 | this.message = row.message | ||
16 | |||
17 | const metaObj = omit(row, 'timestamp', 'level', 'message', 'label') | ||
18 | |||
19 | if (Object.keys(metaObj).length !== 0) this.meta = JSON.stringify(metaObj, undefined, 2) | ||
20 | } | ||
21 | } | ||
diff --git a/client/src/app/+admin/system/logs/logs.component.html b/client/src/app/+admin/system/logs/logs.component.html new file mode 100644 index 000000000..45723a655 --- /dev/null +++ b/client/src/app/+admin/system/logs/logs.component.html | |||
@@ -0,0 +1,31 @@ | |||
1 | <div class="header"> | ||
2 | <div class="peertube-select-container"> | ||
3 | <select [(ngModel)]="startDate" (ngModelChange)="refresh()"> | ||
4 | <option *ngFor="let timeChoice of timeChoices" [value]="timeChoice.id">{{ timeChoice.label }}</option> | ||
5 | </select> | ||
6 | </div> | ||
7 | |||
8 | <div class="peertube-select-container"> | ||
9 | <select [(ngModel)]="level" (ngModelChange)="refresh()"> | ||
10 | <option *ngFor="let levelChoice of levelChoices" [value]="levelChoice.id">{{ levelChoice.label }}</option> | ||
11 | </select> | ||
12 | </div> | ||
13 | |||
14 | <my-button i18n-label label="Refresh" icon="refresh" (click)="refresh()"></my-button> | ||
15 | </div> | ||
16 | |||
17 | <div class="logs"> | ||
18 | <div *ngIf="loading">Loading...</div> | ||
19 | |||
20 | <div #logsElement> | ||
21 | <div *ngFor="let log of logs" class="log-row" [ngClass]="{ error: log.level === 'error', warn: log.level === 'warn' }"> | ||
22 | <span class="log-level">{{ log.level }}</span> | ||
23 | |||
24 | <span class="log-date">[{{ log.localeDate }}]</span> | ||
25 | |||
26 | {{ log.message }} | ||
27 | |||
28 | {{ log.meta }} | ||
29 | </div> | ||
30 | </div> | ||
31 | </div> | ||
diff --git a/client/src/app/+admin/system/logs/logs.component.scss b/client/src/app/+admin/system/logs/logs.component.scss new file mode 100644 index 000000000..ab00fb5ae --- /dev/null +++ b/client/src/app/+admin/system/logs/logs.component.scss | |||
@@ -0,0 +1,48 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .logs { | ||
5 | font-family: monospace; | ||
6 | font-size: 13px; | ||
7 | max-height: 500px; | ||
8 | overflow-y: auto; | ||
9 | background: rgba(0, 0, 0, 0.03); | ||
10 | padding: 20px; | ||
11 | |||
12 | .log-row { | ||
13 | margin-top: 1px; | ||
14 | |||
15 | &:hover { | ||
16 | background: rgba(0, 0, 0, 0.07); | ||
17 | } | ||
18 | } | ||
19 | |||
20 | .log-level { | ||
21 | font-weight: $font-semibold; | ||
22 | margin-right: 5px; | ||
23 | } | ||
24 | |||
25 | .warn { | ||
26 | color: $orange-color; | ||
27 | } | ||
28 | |||
29 | .error { | ||
30 | color: $red; | ||
31 | } | ||
32 | } | ||
33 | |||
34 | .header { | ||
35 | display: flex; | ||
36 | justify-content: flex-end; | ||
37 | margin-bottom: 10px; | ||
38 | |||
39 | .peertube-select-container { | ||
40 | @include peertube-select-container(150px); | ||
41 | } | ||
42 | |||
43 | my-button, | ||
44 | .peertube-select-container { | ||
45 | margin-left: 10px; | ||
46 | } | ||
47 | } | ||
48 | |||
diff --git a/client/src/app/+admin/system/logs/logs.component.ts b/client/src/app/+admin/system/logs/logs.component.ts new file mode 100644 index 000000000..17abb8409 --- /dev/null +++ b/client/src/app/+admin/system/logs/logs.component.ts | |||
@@ -0,0 +1,111 @@ | |||
1 | import { Component, ElementRef, OnInit, ViewChild } from '@angular/core' | ||
2 | import { LogsService } from '@app/+admin/system/logs/logs.service' | ||
3 | import { Notifier } from '@app/core' | ||
4 | import { LogRow } from '@app/+admin/system/logs/log-row.model' | ||
5 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
6 | import { LogLevel } from '@shared/models/server/log-level.type' | ||
7 | |||
8 | @Component({ | ||
9 | templateUrl: './logs.component.html', | ||
10 | styleUrls: [ './logs.component.scss' ] | ||
11 | }) | ||
12 | export class LogsComponent implements OnInit { | ||
13 | @ViewChild('logsElement') logsElement: ElementRef<HTMLElement> | ||
14 | |||
15 | loading = false | ||
16 | |||
17 | logs: LogRow[] = [] | ||
18 | timeChoices: { id: string, label: string }[] = [] | ||
19 | levelChoices: { id: LogLevel, label: string }[] = [] | ||
20 | |||
21 | startDate: string | ||
22 | level: LogLevel | ||
23 | |||
24 | constructor ( | ||
25 | private logsService: LogsService, | ||
26 | private notifier: Notifier, | ||
27 | private i18n: I18n | ||
28 | ) { } | ||
29 | |||
30 | ngOnInit (): void { | ||
31 | this.buildTimeChoices() | ||
32 | this.buildLevelChoices() | ||
33 | |||
34 | this.load() | ||
35 | } | ||
36 | |||
37 | refresh () { | ||
38 | this.logs = [] | ||
39 | this.load() | ||
40 | } | ||
41 | |||
42 | load () { | ||
43 | this.loading = true | ||
44 | |||
45 | this.logsService.getLogs(this.level, this.startDate) | ||
46 | .subscribe( | ||
47 | logs => { | ||
48 | this.logs = logs | ||
49 | |||
50 | setTimeout(() => { | ||
51 | this.logsElement.nativeElement.scrollIntoView({ block: 'end', inline: 'nearest' }) | ||
52 | }) | ||
53 | }, | ||
54 | |||
55 | err => this.notifier.error(err.message), | ||
56 | |||
57 | () => this.loading = false | ||
58 | ) | ||
59 | } | ||
60 | |||
61 | buildTimeChoices () { | ||
62 | const lastHour = new Date() | ||
63 | lastHour.setHours(lastHour.getHours() - 1) | ||
64 | |||
65 | const lastDay = new Date() | ||
66 | lastDay.setDate(lastDay.getDate() - 1) | ||
67 | |||
68 | const lastWeek = new Date() | ||
69 | lastWeek.setDate(lastWeek.getDate() - 7) | ||
70 | |||
71 | this.timeChoices = [ | ||
72 | { | ||
73 | id: lastWeek.toISOString(), | ||
74 | label: this.i18n('Last week') | ||
75 | }, | ||
76 | { | ||
77 | id: lastDay.toISOString(), | ||
78 | label: this.i18n('Last day') | ||
79 | }, | ||
80 | { | ||
81 | id: lastHour.toISOString(), | ||
82 | label: this.i18n('Last hour') | ||
83 | } | ||
84 | ] | ||
85 | |||
86 | this.startDate = lastHour.toISOString() | ||
87 | } | ||
88 | |||
89 | buildLevelChoices () { | ||
90 | this.levelChoices = [ | ||
91 | { | ||
92 | id: 'debug', | ||
93 | label: this.i18n('Debug') | ||
94 | }, | ||
95 | { | ||
96 | id: 'info', | ||
97 | label: this.i18n('Info') | ||
98 | }, | ||
99 | { | ||
100 | id: 'warn', | ||
101 | label: this.i18n('Warning') | ||
102 | }, | ||
103 | { | ||
104 | id: 'error', | ||
105 | label: this.i18n('Error') | ||
106 | } | ||
107 | ] | ||
108 | |||
109 | this.level = 'info' | ||
110 | } | ||
111 | } | ||
diff --git a/client/src/app/+admin/system/logs/logs.service.ts b/client/src/app/+admin/system/logs/logs.service.ts new file mode 100644 index 000000000..4db79a1fa --- /dev/null +++ b/client/src/app/+admin/system/logs/logs.service.ts | |||
@@ -0,0 +1,33 @@ | |||
1 | import { catchError, map } from 'rxjs/operators' | ||
2 | import { HttpClient, HttpParams } from '@angular/common/http' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { Observable } from 'rxjs' | ||
5 | import { environment } from '../../../../environments/environment' | ||
6 | import { RestExtractor, RestService } from '../../../shared' | ||
7 | import { LogRow } from '@app/+admin/system/logs/log-row.model' | ||
8 | import { LogLevel } from '@shared/models/server/log-level.type' | ||
9 | |||
10 | @Injectable() | ||
11 | export class LogsService { | ||
12 | private static BASE_JOB_URL = environment.apiUrl + '/api/v1/server/logs' | ||
13 | |||
14 | constructor ( | ||
15 | private authHttp: HttpClient, | ||
16 | private restService: RestService, | ||
17 | private restExtractor: RestExtractor | ||
18 | ) {} | ||
19 | |||
20 | getLogs (level: LogLevel, startDate: string, endDate?: string): Observable<any> { | ||
21 | let params = new HttpParams() | ||
22 | params = params.append('startDate', startDate) | ||
23 | params = params.append('level', level) | ||
24 | |||
25 | if (endDate) params.append('endDate', endDate) | ||
26 | |||
27 | return this.authHttp.get<any[]>(LogsService.BASE_JOB_URL, { params }) | ||
28 | .pipe( | ||
29 | map(rows => rows.map(r => new LogRow(r))), | ||
30 | catchError(err => this.restExtractor.handleError(err)) | ||
31 | ) | ||
32 | } | ||
33 | } | ||
diff --git a/client/src/app/+admin/system/system.component.html b/client/src/app/+admin/system/system.component.html new file mode 100644 index 000000000..345a101e6 --- /dev/null +++ b/client/src/app/+admin/system/system.component.html | |||
@@ -0,0 +1,11 @@ | |||
1 | <div class="admin-sub-header"> | ||
2 | <div i18n class="form-sub-title">System</div> | ||
3 | |||
4 | <div class="admin-sub-nav"> | ||
5 | <a i18n routerLink="jobs" routerLinkActive="active">Jobs</a> | ||
6 | |||
7 | <a i18n routerLink="logs" routerLinkActive="active">Logs</a> | ||
8 | </div> | ||
9 | </div> | ||
10 | |||
11 | <router-outlet></router-outlet> | ||
diff --git a/client/src/app/+admin/system/system.component.scss b/client/src/app/+admin/system/system.component.scss new file mode 100644 index 000000000..766d7853b --- /dev/null +++ b/client/src/app/+admin/system/system.component.scss | |||
@@ -0,0 +1,4 @@ | |||
1 | .form-sub-title { | ||
2 | flex-grow: 0; | ||
3 | margin-right: 30px; | ||
4 | } | ||
diff --git a/client/src/app/+admin/system/system.component.ts b/client/src/app/+admin/system/system.component.ts new file mode 100644 index 000000000..992d9c8af --- /dev/null +++ b/client/src/app/+admin/system/system.component.ts | |||
@@ -0,0 +1,8 @@ | |||
1 | import { Component } from '@angular/core' | ||
2 | |||
3 | @Component({ | ||
4 | templateUrl: './system.component.html', | ||
5 | styleUrls: [ './system.component.scss' ] | ||
6 | }) | ||
7 | export class SystemComponent { | ||
8 | } | ||
diff --git a/client/src/app/+admin/system/system.routes.ts b/client/src/app/+admin/system/system.routes.ts new file mode 100644 index 000000000..e6d45b760 --- /dev/null +++ b/client/src/app/+admin/system/system.routes.ts | |||
@@ -0,0 +1,44 @@ | |||
1 | import { Routes } from '@angular/router' | ||
2 | import { UserRightGuard } from '../../core' | ||
3 | import { UserRight } from '../../../../../shared' | ||
4 | import { JobsComponent } from '@app/+admin/system/jobs/jobs.component' | ||
5 | import { LogsComponent } from '@app/+admin/system/logs' | ||
6 | import { SystemComponent } from '@app/+admin/system/system.component' | ||
7 | |||
8 | export const SystemRoutes: Routes = [ | ||
9 | { | ||
10 | path: 'system', | ||
11 | component: SystemComponent, | ||
12 | data: { | ||
13 | }, | ||
14 | children: [ | ||
15 | { | ||
16 | path: '', | ||
17 | redirectTo: 'jobs', | ||
18 | pathMatch: 'full' | ||
19 | }, | ||
20 | { | ||
21 | path: 'jobs', | ||
22 | canActivate: [ UserRightGuard ], | ||
23 | component: JobsComponent, | ||
24 | data: { | ||
25 | meta: { | ||
26 | userRight: UserRight.MANAGE_JOBS, | ||
27 | title: 'Jobs' | ||
28 | } | ||
29 | } | ||
30 | }, | ||
31 | { | ||
32 | path: 'logs', | ||
33 | canActivate: [ UserRightGuard ], | ||
34 | component: LogsComponent, | ||
35 | data: { | ||
36 | meta: { | ||
37 | userRight: UserRight.MANAGE_LOGS, | ||
38 | title: 'Logs' | ||
39 | } | ||
40 | } | ||
41 | } | ||
42 | ] | ||
43 | } | ||
44 | ] | ||
diff --git a/client/src/app/shared/images/global-icon.component.ts b/client/src/app/shared/images/global-icon.component.ts index bd5b11bb0..03cf3d7ae 100644 --- a/client/src/app/shared/images/global-icon.component.ts +++ b/client/src/app/shared/images/global-icon.component.ts | |||
@@ -44,7 +44,8 @@ const icons = { | |||
44 | 'folder': require('../../../assets/images/global/folder.html'), | 44 | 'folder': require('../../../assets/images/global/folder.html'), |
45 | 'administration': require('../../../assets/images/menu/administration.html'), | 45 | 'administration': require('../../../assets/images/menu/administration.html'), |
46 | 'subscriptions': require('../../../assets/images/menu/subscriptions.html'), | 46 | 'subscriptions': require('../../../assets/images/menu/subscriptions.html'), |
47 | 'users': require('../../../assets/images/global/users.html') | 47 | 'users': require('../../../assets/images/global/users.html'), |
48 | 'refresh': require('../../../assets/images/global/refresh.html') | ||
48 | } | 49 | } |
49 | 50 | ||
50 | export type GlobalIconName = keyof typeof icons | 51 | export type GlobalIconName = keyof typeof icons |
diff --git a/client/src/assets/images/global/refresh.html b/client/src/assets/images/global/refresh.html new file mode 100644 index 000000000..421ab343d --- /dev/null +++ b/client/src/assets/images/global/refresh.html | |||
@@ -0,0 +1,12 @@ | |||
1 | <svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | ||
2 | <defs/> | ||
3 | <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> | ||
4 | <g id="Artboard-4" transform="translate(-224.000000, -1046.000000)" fill="#000000"> | ||
5 | <g id="Extras" transform="translate(48.000000, 1046.000000)"> | ||
6 | <g id="refresh" transform="translate(176.000000, 0.000000)"> | ||
7 | <path d="M20.9995201,13.0312796 L20.9999519,13.0312796 C20.9830843,17.9874565 16.960132,22 12,22 C7.02943725,22 3,17.9705627 3,13 C3,8.0398348 7.01259713,4.01686187 11.9688198,4.00005287 L11.9688198,6.00006796 C8.11716976,6.01686496 5,9.14440548 5,13 C5,16.8659932 8.13400675,20 12,20 C15.8555614,20 18.9830812,16.8828839 18.9999316,13.0312796 L19.0004799,13.0312796 C19.0001607,13.0208922 19,13.0104649 19,13 C19,12.4477153 19.4477153,12 20,12 C20.5522847,12 21,12.4477153 21,13 C21,13.0104649 20.9998393,13.0208922 20.9995201,13.0312796 Z M12,9 L12,1 L16,5 L12,9 Z" id="Combined-Shape"/> | ||
8 | </g> | ||
9 | </g> | ||
10 | </g> | ||
11 | </g> | ||
12 | </svg> | ||