diff options
author | Chocobozzz <me@florianbigard.com> | 2019-12-11 14:14:01 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2019-12-11 14:14:01 +0100 |
commit | 566c125d6eee3bd907404523d94e1e0b5e403a46 (patch) | |
tree | c477cdd2ba745015d80052968c37927b1bca1254 /client/src/app | |
parent | 92e0f42e8ce5f1ab5e4023900b8194627231a11b (diff) | |
download | PeerTube-566c125d6eee3bd907404523d94e1e0b5e403a46.tar.gz PeerTube-566c125d6eee3bd907404523d94e1e0b5e403a46.tar.zst PeerTube-566c125d6eee3bd907404523d94e1e0b5e403a46.zip |
Serve audit logs to client
Diffstat (limited to 'client/src/app')
9 files changed, 83 insertions, 11 deletions
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html index 627437053..30eb2dbde 100644 --- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html +++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html | |||
@@ -15,7 +15,7 @@ | |||
15 | 15 | ||
16 | <ng-template pTemplate="body" let-expanded="expanded" let-videoAbuse> | 16 | <ng-template pTemplate="body" let-expanded="expanded" let-videoAbuse> |
17 | <tr> | 17 | <tr> |
18 | <td> | 18 | <td class="expand-cell"> |
19 | <span class="expander" [pRowToggler]="videoAbuse"> | 19 | <span class="expander" [pRowToggler]="videoAbuse"> |
20 | <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i> | 20 | <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i> |
21 | </span> | 21 | </span> |
diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html index 608dff2d8..a0b89acc6 100644 --- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html +++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html | |||
@@ -15,7 +15,7 @@ | |||
15 | 15 | ||
16 | <ng-template pTemplate="body" let-videoBlacklist let-expanded="expanded"> | 16 | <ng-template pTemplate="body" let-videoBlacklist let-expanded="expanded"> |
17 | <tr> | 17 | <tr> |
18 | <td> | 18 | <td class="expand-cell"> |
19 | <span *ngIf="videoBlacklist.reason" class="expander" [pRowToggler]="videoBlacklist"> | 19 | <span *ngIf="videoBlacklist.reason" class="expander" [pRowToggler]="videoBlacklist"> |
20 | <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i> | 20 | <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i> |
21 | </span> | 21 | </span> |
diff --git a/client/src/app/+admin/system/jobs/jobs.component.html b/client/src/app/+admin/system/jobs/jobs.component.html index cd26257dd..de43b6448 100644 --- a/client/src/app/+admin/system/jobs/jobs.component.html +++ b/client/src/app/+admin/system/jobs/jobs.component.html | |||
@@ -38,7 +38,7 @@ | |||
38 | 38 | ||
39 | <ng-template pTemplate="body" let-expanded="expanded" let-job> | 39 | <ng-template pTemplate="body" let-expanded="expanded" let-job> |
40 | <tr> | 40 | <tr> |
41 | <td> | 41 | <td class="expand-cell"> |
42 | <span class="expander" [pRowToggler]="job"> | 42 | <span class="expander" [pRowToggler]="job"> |
43 | <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i> | 43 | <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i> |
44 | </span> | 44 | </span> |
diff --git a/client/src/app/+admin/system/logs/log-row.model.ts b/client/src/app/+admin/system/logs/log-row.model.ts index 9bc7dafdd..b22581b5a 100644 --- a/client/src/app/+admin/system/logs/log-row.model.ts +++ b/client/src/app/+admin/system/logs/log-row.model.ts | |||
@@ -8,6 +8,10 @@ export class LogRow { | |||
8 | message: string | 8 | message: string |
9 | meta: string | 9 | meta: string |
10 | 10 | ||
11 | by: string | ||
12 | domain: string | ||
13 | action: string | ||
14 | |||
11 | constructor (row: any) { | 15 | constructor (row: any) { |
12 | this.date = new Date(row.timestamp) | 16 | this.date = new Date(row.timestamp) |
13 | this.localeDate = this.date.toLocaleString() | 17 | this.localeDate = this.date.toLocaleString() |
@@ -17,5 +21,20 @@ export class LogRow { | |||
17 | const metaObj = omit(row, 'timestamp', 'level', 'message', 'label') | 21 | const metaObj = omit(row, 'timestamp', 'level', 'message', 'label') |
18 | 22 | ||
19 | if (Object.keys(metaObj).length !== 0) this.meta = JSON.stringify(metaObj, undefined, 2) | 23 | if (Object.keys(metaObj).length !== 0) this.meta = JSON.stringify(metaObj, undefined, 2) |
24 | |||
25 | if (row.level === 'audit') { | ||
26 | try { | ||
27 | const message = JSON.parse(row.message) | ||
28 | |||
29 | this.by = message.user | ||
30 | this.domain = message.domain | ||
31 | this.action = message.action | ||
32 | |||
33 | this.meta = JSON.stringify(message, null, 2) | ||
34 | this.message = '' | ||
35 | } catch (err) { | ||
36 | console.error('Cannot parse audit message.', err) | ||
37 | } | ||
38 | } | ||
20 | } | 39 | } |
21 | } | 40 | } |
diff --git a/client/src/app/+admin/system/logs/logs.component.html b/client/src/app/+admin/system/logs/logs.component.html index 45723a655..ddad1314f 100644 --- a/client/src/app/+admin/system/logs/logs.component.html +++ b/client/src/app/+admin/system/logs/logs.component.html | |||
@@ -1,11 +1,17 @@ | |||
1 | <div class="header"> | 1 | <div class="header"> |
2 | <div class="peertube-select-container"> | 2 | <div class="peertube-select-container"> |
3 | <select [(ngModel)]="logType" (ngModelChange)="refresh()"> | ||
4 | <option *ngFor="let logTypeChoice of logTypeChoices" [value]="logTypeChoice.id">{{ logTypeChoice.label }}</option> | ||
5 | </select> | ||
6 | </div> | ||
7 | |||
8 | <div class="peertube-select-container"> | ||
3 | <select [(ngModel)]="startDate" (ngModelChange)="refresh()"> | 9 | <select [(ngModel)]="startDate" (ngModelChange)="refresh()"> |
4 | <option *ngFor="let timeChoice of timeChoices" [value]="timeChoice.id">{{ timeChoice.label }}</option> | 10 | <option *ngFor="let timeChoice of timeChoices" [value]="timeChoice.id">{{ timeChoice.label }}</option> |
5 | </select> | 11 | </select> |
6 | </div> | 12 | </div> |
7 | 13 | ||
8 | <div class="peertube-select-container"> | 14 | <div class="peertube-select-container" *ngIf="!isAuditLog()"> |
9 | <select [(ngModel)]="level" (ngModelChange)="refresh()"> | 15 | <select [(ngModel)]="level" (ngModelChange)="refresh()"> |
10 | <option *ngFor="let levelChoice of levelChoices" [value]="levelChoice.id">{{ levelChoice.label }}</option> | 16 | <option *ngFor="let levelChoice of levelChoices" [value]="levelChoice.id">{{ levelChoice.label }}</option> |
11 | </select> | 17 | </select> |
@@ -23,9 +29,12 @@ | |||
23 | 29 | ||
24 | <span class="log-date">[{{ log.localeDate }}]</span> | 30 | <span class="log-date">[{{ log.localeDate }}]</span> |
25 | 31 | ||
32 | <strong class="log-by" *ngIf="log.by" i18n>By {{ log.by }} -></strong> | ||
33 | <strong class="log-domain-action" *ngIf="log.domain">{{ log.domain }} -> {{ log.action }}</strong> | ||
34 | |||
26 | {{ log.message }} | 35 | {{ log.message }} |
27 | 36 | ||
28 | {{ log.meta }} | 37 | <pre>{{ log.meta }}</pre> |
29 | </div> | 38 | </div> |
30 | </div> | 39 | </div> |
31 | </div> | 40 | </div> |
diff --git a/client/src/app/+admin/system/logs/logs.component.scss b/client/src/app/+admin/system/logs/logs.component.scss index 7ad2e853c..dae8b21c7 100644 --- a/client/src/app/+admin/system/logs/logs.component.scss +++ b/client/src/app/+admin/system/logs/logs.component.scss | |||
@@ -23,6 +23,10 @@ | |||
23 | margin-right: 5px; | 23 | margin-right: 5px; |
24 | } | 24 | } |
25 | 25 | ||
26 | .log-by { | ||
27 | margin: 0 5px; | ||
28 | } | ||
29 | |||
26 | .warn { | 30 | .warn { |
27 | color: $orange-color; | 31 | color: $orange-color; |
28 | } | 32 | } |
@@ -30,6 +34,12 @@ | |||
30 | .error { | 34 | .error { |
31 | color: $red; | 35 | color: $red; |
32 | } | 36 | } |
37 | |||
38 | pre { | ||
39 | margin-bottom: 5px; | ||
40 | white-space: pre-wrap; | ||
41 | word-wrap: break-word; | ||
42 | } | ||
33 | } | 43 | } |
34 | 44 | ||
35 | .header { | 45 | .header { |
diff --git a/client/src/app/+admin/system/logs/logs.component.ts b/client/src/app/+admin/system/logs/logs.component.ts index b2aca8461..b63f11953 100644 --- a/client/src/app/+admin/system/logs/logs.component.ts +++ b/client/src/app/+admin/system/logs/logs.component.ts | |||
@@ -17,9 +17,11 @@ export class LogsComponent implements OnInit { | |||
17 | logs: LogRow[] = [] | 17 | logs: LogRow[] = [] |
18 | timeChoices: { id: string, label: string }[] = [] | 18 | timeChoices: { id: string, label: string }[] = [] |
19 | levelChoices: { id: LogLevel, label: string }[] = [] | 19 | levelChoices: { id: LogLevel, label: string }[] = [] |
20 | logTypeChoices: { id: 'audit' | 'standard', label: string }[] = [] | ||
20 | 21 | ||
21 | startDate: string | 22 | startDate: string |
22 | level: LogLevel | 23 | level: LogLevel |
24 | logType: 'audit' | 'standard' | ||
23 | 25 | ||
24 | constructor ( | 26 | constructor ( |
25 | private logsService: LogsService, | 27 | private logsService: LogsService, |
@@ -30,6 +32,7 @@ export class LogsComponent implements OnInit { | |||
30 | ngOnInit (): void { | 32 | ngOnInit (): void { |
31 | this.buildTimeChoices() | 33 | this.buildTimeChoices() |
32 | this.buildLevelChoices() | 34 | this.buildLevelChoices() |
35 | this.buildLogTypeChoices() | ||
33 | 36 | ||
34 | this.load() | 37 | this.load() |
35 | } | 38 | } |
@@ -42,7 +45,7 @@ export class LogsComponent implements OnInit { | |||
42 | load () { | 45 | load () { |
43 | this.loading = true | 46 | this.loading = true |
44 | 47 | ||
45 | this.logsService.getLogs(this.level, this.startDate) | 48 | this.logsService.getLogs({ isAuditLog: this.isAuditLog(), level: this.level, startDate: this.startDate }) |
46 | .subscribe( | 49 | .subscribe( |
47 | logs => { | 50 | logs => { |
48 | this.logs = logs | 51 | this.logs = logs |
@@ -58,6 +61,10 @@ export class LogsComponent implements OnInit { | |||
58 | ) | 61 | ) |
59 | } | 62 | } |
60 | 63 | ||
64 | isAuditLog () { | ||
65 | return this.logType === 'audit' | ||
66 | } | ||
67 | |||
61 | buildTimeChoices () { | 68 | buildTimeChoices () { |
62 | const lastHour = new Date() | 69 | const lastHour = new Date() |
63 | lastHour.setHours(lastHour.getHours() - 1) | 70 | lastHour.setHours(lastHour.getHours() - 1) |
@@ -108,4 +115,19 @@ export class LogsComponent implements OnInit { | |||
108 | 115 | ||
109 | this.level = 'warn' | 116 | this.level = 'warn' |
110 | } | 117 | } |
118 | |||
119 | buildLogTypeChoices () { | ||
120 | this.logTypeChoices = [ | ||
121 | { | ||
122 | id: 'standard', | ||
123 | label: this.i18n('Standard logs') | ||
124 | }, | ||
125 | { | ||
126 | id: 'audit', | ||
127 | label: this.i18n('Audit logs') | ||
128 | } | ||
129 | ] | ||
130 | |||
131 | this.logType = 'audit' | ||
132 | } | ||
111 | } | 133 | } |
diff --git a/client/src/app/+admin/system/logs/logs.service.ts b/client/src/app/+admin/system/logs/logs.service.ts index 24b9cb6d1..41b38c7ba 100644 --- a/client/src/app/+admin/system/logs/logs.service.ts +++ b/client/src/app/+admin/system/logs/logs.service.ts | |||
@@ -10,6 +10,7 @@ import { LogLevel } from '@shared/models/server/log-level.type' | |||
10 | @Injectable() | 10 | @Injectable() |
11 | export class LogsService { | 11 | export class LogsService { |
12 | private static BASE_LOG_URL = environment.apiUrl + '/api/v1/server/logs' | 12 | private static BASE_LOG_URL = environment.apiUrl + '/api/v1/server/logs' |
13 | private static BASE_AUDIT_LOG_URL = environment.apiUrl + '/api/v1/server/audit-logs' | ||
13 | 14 | ||
14 | constructor ( | 15 | constructor ( |
15 | private authHttp: HttpClient, | 16 | private authHttp: HttpClient, |
@@ -17,14 +18,25 @@ export class LogsService { | |||
17 | private restExtractor: RestExtractor | 18 | private restExtractor: RestExtractor |
18 | ) {} | 19 | ) {} |
19 | 20 | ||
20 | getLogs (level: LogLevel, startDate: string, endDate?: string): Observable<any[]> { | 21 | getLogs (options: { |
22 | isAuditLog: boolean, | ||
23 | startDate: string, | ||
24 | level?: LogLevel, | ||
25 | endDate?: string | ||
26 | }): Observable<any[]> { | ||
27 | const { isAuditLog, startDate } = options | ||
28 | |||
21 | let params = new HttpParams() | 29 | let params = new HttpParams() |
22 | params = params.append('startDate', startDate) | 30 | params = params.append('startDate', startDate) |
23 | params = params.append('level', level) | ||
24 | 31 | ||
25 | if (endDate) params.append('endDate', endDate) | 32 | if (!isAuditLog) params = params.append('level', options.level) |
33 | if (options.endDate) params.append('endDate', options.endDate) | ||
34 | |||
35 | const path = isAuditLog | ||
36 | ? LogsService.BASE_AUDIT_LOG_URL | ||
37 | : LogsService.BASE_LOG_URL | ||
26 | 38 | ||
27 | return this.authHttp.get<any[]>(LogsService.BASE_LOG_URL, { params }) | 39 | return this.authHttp.get<any[]>(path, { params }) |
28 | .pipe( | 40 | .pipe( |
29 | map(rows => rows.map(r => new LogRow(r))), | 41 | map(rows => rows.map(r => new LogRow(r))), |
30 | catchError(err => this.restExtractor.handleError(err)) | 42 | catchError(err => this.restExtractor.handleError(err)) |
diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html index 822bb53da..885335313 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.html +++ b/client/src/app/+admin/users/user-list/user-list.component.html | |||
@@ -49,7 +49,7 @@ | |||
49 | <ng-template pTemplate="body" let-expanded="expanded" let-user> | 49 | <ng-template pTemplate="body" let-expanded="expanded" let-user> |
50 | 50 | ||
51 | <tr [pSelectableRow]="user" [ngClass]="{ banned: user.blocked }"> | 51 | <tr [pSelectableRow]="user" [ngClass]="{ banned: user.blocked }"> |
52 | <td> | 52 | <td class="expand-cell"> |
53 | <p-tableCheckbox [value]="user"></p-tableCheckbox> | 53 | <p-tableCheckbox [value]="user"></p-tableCheckbox> |
54 | </td> | 54 | </td> |
55 | 55 | ||