aboutsummaryrefslogtreecommitdiffhomepage
path: root/client
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-12-11 14:14:01 +0100
committerChocobozzz <me@florianbigard.com>2019-12-11 14:14:01 +0100
commit566c125d6eee3bd907404523d94e1e0b5e403a46 (patch)
treec477cdd2ba745015d80052968c37927b1bca1254 /client
parent92e0f42e8ce5f1ab5e4023900b8194627231a11b (diff)
downloadPeerTube-566c125d6eee3bd907404523d94e1e0b5e403a46.tar.gz
PeerTube-566c125d6eee3bd907404523d94e1e0b5e403a46.tar.zst
PeerTube-566c125d6eee3bd907404523d94e1e0b5e403a46.zip
Serve audit logs to client
Diffstat (limited to 'client')
-rw-r--r--client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html2
-rw-r--r--client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html2
-rw-r--r--client/src/app/+admin/system/jobs/jobs.component.html2
-rw-r--r--client/src/app/+admin/system/logs/log-row.model.ts19
-rw-r--r--client/src/app/+admin/system/logs/logs.component.html13
-rw-r--r--client/src/app/+admin/system/logs/logs.component.scss10
-rw-r--r--client/src/app/+admin/system/logs/logs.component.ts24
-rw-r--r--client/src/app/+admin/system/logs/logs.service.ts20
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.html2
-rw-r--r--client/src/sass/primeng-custom.scss2
10 files changed, 84 insertions, 12 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()
11export class LogsService { 11export 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
diff --git a/client/src/sass/primeng-custom.scss b/client/src/sass/primeng-custom.scss
index 6c3100746..0acffef3c 100644
--- a/client/src/sass/primeng-custom.scss
+++ b/client/src/sass/primeng-custom.scss
@@ -37,7 +37,7 @@ p-table {
37 td { 37 td {
38 padding-left: 15px !important; 38 padding-left: 15px !important;
39 39
40 &:not(.action-cell) { 40 &:not(.action-cell):not(.expand-cell) {
41 overflow: hidden !important; 41 overflow: hidden !important;
42 text-overflow: ellipsis !important; 42 text-overflow: ellipsis !important;
43 white-space: nowrap !important; 43 white-space: nowrap !important;