Serve audit logs to client
authorChocobozzz <me@florianbigard.com>
Wed, 11 Dec 2019 13:14:01 +0000 (14:14 +0100)
committerChocobozzz <me@florianbigard.com>
Wed, 11 Dec 2019 13:14:01 +0000 (14:14 +0100)
18 files changed:
client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html
client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html
client/src/app/+admin/system/jobs/jobs.component.html
client/src/app/+admin/system/logs/log-row.model.ts
client/src/app/+admin/system/logs/logs.component.html
client/src/app/+admin/system/logs/logs.component.scss
client/src/app/+admin/system/logs/logs.component.ts
client/src/app/+admin/system/logs/logs.service.ts
client/src/app/+admin/users/user-list/user-list.component.html
client/src/sass/primeng-custom.scss
server/controllers/api/server/logs.ts
server/helpers/audit-logger.ts
server/helpers/logger.ts
server/initializers/constants.ts
server/middlewares/validators/logs.ts
server/tests/api/server/logs.ts
shared/extra-utils/logs/logs.ts
shared/models/server/log-level.type.ts

index 62743705343989fa79f25eb6fbb35e34b28f610e..30eb2dbde82e0bb3ea5428ed3d4277e273eef231 100644 (file)
@@ -15,7 +15,7 @@
 
   <ng-template pTemplate="body" let-expanded="expanded" let-videoAbuse>
     <tr>
-      <td>
+      <td class="expand-cell">
         <span class="expander" [pRowToggler]="videoAbuse">
           <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i>
         </span>
index 608dff2d871b5882f73f4c42007331406c5297b3..a0b89acc6134f9abf6600c095d15cf3fa6a90d50 100644 (file)
@@ -15,7 +15,7 @@
 
   <ng-template pTemplate="body" let-videoBlacklist let-expanded="expanded">
     <tr>
-      <td>
+      <td class="expand-cell">
         <span *ngIf="videoBlacklist.reason" class="expander" [pRowToggler]="videoBlacklist">
           <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i>
         </span>
index cd26257dd2ea8000ee794ab270bc1d84436b42f3..de43b644842ddc99ed3069d282a5f421289dd5d7 100644 (file)
@@ -38,7 +38,7 @@
 
   <ng-template pTemplate="body" let-expanded="expanded" let-job>
     <tr>
-      <td>
+      <td class="expand-cell">
         <span class="expander" [pRowToggler]="job">
           <i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i>
         </span>
index 9bc7dafdd6ac4a75486b9fd333d6d4930fa8aea2..b22581b5a4a264a509d4e8100e5ce2cd1474a161 100644 (file)
@@ -8,6 +8,10 @@ export class LogRow {
   message: string
   meta: string
 
+  by: string
+  domain: string
+  action: string
+
   constructor (row: any) {
     this.date = new Date(row.timestamp)
     this.localeDate = this.date.toLocaleString()
@@ -17,5 +21,20 @@ export class LogRow {
     const metaObj = omit(row, 'timestamp', 'level', 'message', 'label')
 
     if (Object.keys(metaObj).length !== 0) this.meta = JSON.stringify(metaObj, undefined, 2)
+
+    if (row.level === 'audit') {
+      try {
+        const message = JSON.parse(row.message)
+
+        this.by = message.user
+        this.domain = message.domain
+        this.action = message.action
+
+        this.meta = JSON.stringify(message, null, 2)
+        this.message = ''
+      } catch (err) {
+        console.error('Cannot parse audit message.', err)
+      }
+    }
   }
 }
index 45723a655d32af9295b81b4978f2dd83e5128c74..ddad1314f868254517743ecc3052516176811fb9 100644 (file)
@@ -1,11 +1,17 @@
 <div class="header">
+  <div class="peertube-select-container">
+    <select [(ngModel)]="logType" (ngModelChange)="refresh()">
+      <option *ngFor="let logTypeChoice of logTypeChoices" [value]="logTypeChoice.id">{{ logTypeChoice.label }}</option>
+    </select>
+  </div>
+
   <div class="peertube-select-container">
     <select [(ngModel)]="startDate" (ngModelChange)="refresh()">
       <option *ngFor="let timeChoice of timeChoices" [value]="timeChoice.id">{{ timeChoice.label }}</option>
     </select>
   </div>
 
-  <div class="peertube-select-container">
+  <div class="peertube-select-container" *ngIf="!isAuditLog()">
     <select [(ngModel)]="level" (ngModelChange)="refresh()">
       <option *ngFor="let levelChoice of levelChoices" [value]="levelChoice.id">{{ levelChoice.label }}</option>
     </select>
 
       <span class="log-date">[{{ log.localeDate }}]</span>
 
+      <strong class="log-by" *ngIf="log.by" i18n>By {{ log.by }} -></strong>
+      <strong class="log-domain-action" *ngIf="log.domain">{{ log.domain }} -> {{ log.action }}</strong>
+
       {{ log.message }}
 
-      {{ log.meta }}
+      <pre>{{ log.meta }}</pre>
     </div>
   </div>
 </div>
index 7ad2e853cfe618435c3747f5c9342d41ca7c9b06..dae8b21c7d8c13080dd5f0b38a54f6798b649b59 100644 (file)
     margin-right: 5px;
   }
 
+  .log-by {
+    margin: 0 5px;
+  }
+
   .warn {
     color: $orange-color;
   }
   .error {
     color: $red;
   }
+
+  pre {
+    margin-bottom: 5px;
+    white-space: pre-wrap;
+    word-wrap: break-word;
+  }
 }
 
 .header {
index b2aca8461e590883c2e8a5568f931bb028474073..b63f1195310fb09cc9b7a22e000bc864b14d696a 100644 (file)
@@ -17,9 +17,11 @@ export class LogsComponent implements OnInit {
   logs: LogRow[] = []
   timeChoices: { id: string, label: string }[] = []
   levelChoices: { id: LogLevel, label: string }[] = []
+  logTypeChoices: { id: 'audit' | 'standard', label: string }[] = []
 
   startDate: string
   level: LogLevel
+  logType: 'audit' | 'standard'
 
   constructor (
     private logsService: LogsService,
@@ -30,6 +32,7 @@ export class LogsComponent implements OnInit {
   ngOnInit (): void {
     this.buildTimeChoices()
     this.buildLevelChoices()
+    this.buildLogTypeChoices()
 
     this.load()
   }
@@ -42,7 +45,7 @@ export class LogsComponent implements OnInit {
   load () {
     this.loading = true
 
-    this.logsService.getLogs(this.level, this.startDate)
+    this.logsService.getLogs({ isAuditLog: this.isAuditLog(), level: this.level, startDate: this.startDate })
         .subscribe(
           logs => {
             this.logs = logs
@@ -58,6 +61,10 @@ export class LogsComponent implements OnInit {
         )
   }
 
+  isAuditLog () {
+    return this.logType === 'audit'
+  }
+
   buildTimeChoices () {
     const lastHour = new Date()
     lastHour.setHours(lastHour.getHours() - 1)
@@ -108,4 +115,19 @@ export class LogsComponent implements OnInit {
 
     this.level = 'warn'
   }
+
+  buildLogTypeChoices () {
+    this.logTypeChoices = [
+      {
+        id: 'standard',
+        label: this.i18n('Standard logs')
+      },
+      {
+        id: 'audit',
+        label: this.i18n('Audit logs')
+      }
+    ]
+
+    this.logType = 'audit'
+  }
 }
index 24b9cb6d1f3ea567e6ae828d516b82d2bfe5734d..41b38c7baf65504b9163fa832fd92d6fd8d79cda 100644 (file)
@@ -10,6 +10,7 @@ import { LogLevel } from '@shared/models/server/log-level.type'
 @Injectable()
 export class LogsService {
   private static BASE_LOG_URL = environment.apiUrl + '/api/v1/server/logs'
+  private static BASE_AUDIT_LOG_URL = environment.apiUrl + '/api/v1/server/audit-logs'
 
   constructor (
     private authHttp: HttpClient,
@@ -17,14 +18,25 @@ export class LogsService {
     private restExtractor: RestExtractor
   ) {}
 
-  getLogs (level: LogLevel, startDate: string, endDate?: string): Observable<any[]> {
+  getLogs (options: {
+    isAuditLog: boolean,
+    startDate: string,
+    level?: LogLevel,
+    endDate?: string
+  }): Observable<any[]> {
+    const { isAuditLog, startDate } = options
+
     let params = new HttpParams()
     params = params.append('startDate', startDate)
-    params = params.append('level', level)
 
-    if (endDate) params.append('endDate', endDate)
+    if (!isAuditLog) params = params.append('level', options.level)
+    if (options.endDate) params.append('endDate', options.endDate)
+
+    const path = isAuditLog
+      ? LogsService.BASE_AUDIT_LOG_URL
+      : LogsService.BASE_LOG_URL
 
-    return this.authHttp.get<any[]>(LogsService.BASE_LOG_URL, { params })
+    return this.authHttp.get<any[]>(path, { params })
                .pipe(
                  map(rows => rows.map(r => new LogRow(r))),
                  catchError(err => this.restExtractor.handleError(err))
index 822bb53da624e3091ea2fb61a150df84f8741a03..885335313c24cc2f8cf58d38a019aa59c1ed0d39 100644 (file)
@@ -49,7 +49,7 @@
   <ng-template pTemplate="body" let-expanded="expanded" let-user>
 
     <tr [pSelectableRow]="user" [ngClass]="{ banned: user.blocked }">
-      <td>
+      <td class="expand-cell">
         <p-tableCheckbox [value]="user"></p-tableCheckbox>
       </td>
 
index 6c310074628c5a41a7d4810f27d6517098528e18..0acffef3c2ad3fed0f5b9abd957685f728296528 100644 (file)
@@ -37,7 +37,7 @@ p-table {
   td {
     padding-left: 15px !important;
 
-    &:not(.action-cell) {
+    &:not(.action-cell):not(.expand-cell) {
       overflow: hidden !important;
       text-overflow: ellipsis !important;
       white-space: nowrap !important;
index e9d1f2efdeca013095f879dd4f195ffd1628af4d..a0ca21cd556d9f1671e37232ca87851b33706402 100644 (file)
@@ -3,11 +3,12 @@ import { UserRight } from '../../../../shared/models/users'
 import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares'
 import { mtimeSortFilesDesc } from '../../../../shared/core-utils/logs/logs'
 import { readdir, readFile } from 'fs-extra'
-import { MAX_LOGS_OUTPUT_CHARACTERS } from '../../../initializers/constants'
+import { AUDIT_LOG_FILENAME, MAX_LOGS_OUTPUT_CHARACTERS, LOG_FILENAME } from '../../../initializers/constants'
 import { join } from 'path'
-import { getLogsValidator } from '../../../middlewares/validators/logs'
+import { getAuditLogsValidator, getLogsValidator } from '../../../middlewares/validators/logs'
 import { LogLevel } from '../../../../shared/models/server/log-level.type'
 import { CONFIG } from '../../../initializers/config'
+import { logger } from '@server/helpers/logger'
 
 const logsRouter = express.Router()
 
@@ -18,6 +19,13 @@ logsRouter.get('/logs',
   asyncMiddleware(getLogs)
 )
 
+logsRouter.get('/audit-logs',
+  authenticate,
+  ensureUserHasRight(UserRight.MANAGE_LOGS),
+  getAuditLogsValidator,
+  asyncMiddleware(getAuditLogs)
+)
+
 // ---------------------------------------------------------------------------
 
 export {
@@ -26,18 +34,50 @@ export {
 
 // ---------------------------------------------------------------------------
 
+const auditLogNameFilter = generateLogNameFilter(AUDIT_LOG_FILENAME)
+async function getAuditLogs (req: express.Request, res: express.Response) {
+  const output = await generateOutput({
+    startDateQuery: req.query.startDate,
+    endDateQuery: req.query.endDate,
+    level: 'audit',
+    nameFilter: auditLogNameFilter
+  })
+
+  return res.json(output).end()
+}
+
+const logNameFilter = generateLogNameFilter(LOG_FILENAME)
 async function getLogs (req: express.Request, res: express.Response) {
+  const output = await generateOutput({
+    startDateQuery: req.query.startDate,
+    endDateQuery: req.query.endDate,
+    level: req.query.level || 'info',
+    nameFilter: logNameFilter
+  })
+
+  return res.json(output).end()
+}
+
+async function generateOutput (options: {
+  startDateQuery: string,
+  endDateQuery?: string,
+  level: LogLevel,
+  nameFilter: RegExp
+}) {
+  const { startDateQuery, level, nameFilter } = options
+
   const logFiles = await readdir(CONFIG.STORAGE.LOG_DIR)
   const sortedLogFiles = await mtimeSortFilesDesc(logFiles, CONFIG.STORAGE.LOG_DIR)
   let currentSize = 0
 
-  const startDate = new Date(req.query.startDate)
-  const endDate = req.query.endDate ? new Date(req.query.endDate) : new Date()
-  const level: LogLevel = req.query.level || 'info'
+  const startDate = new Date(startDateQuery)
+  const endDate = options.endDateQuery ? new Date(options.endDateQuery) : new Date()
 
   let output: string[] = []
 
   for (const meta of sortedLogFiles) {
+    if (nameFilter.exec(meta.file) === null) continue
+
     const path = join(CONFIG.STORAGE.LOG_DIR, meta.file)
 
     const result = await getOutputFromFile(path, startDate, endDate, level, currentSize)
@@ -49,7 +89,7 @@ async function getLogs (req: express.Request, res: express.Response) {
     if (currentSize > MAX_LOGS_OUTPUT_CHARACTERS || (result.logTime && result.logTime < startDate.getTime())) break
   }
 
-  return res.json(output).end()
+  return output
 }
 
 async function getOutputFromFile (path: string, startDate: Date, endDate: Date, level: LogLevel, currentSize: number) {
@@ -58,6 +98,7 @@ async function getOutputFromFile (path: string, startDate: Date, endDate: Date,
   let logTime: number
 
   const logsLevel: { [ id in LogLevel ]: number } = {
+    audit: -1,
     debug: 0,
     info: 1,
     warn: 2,
@@ -93,3 +134,7 @@ async function getOutputFromFile (path: string, startDate: Date, endDate: Date,
 
   return { currentSize, output: output.reverse(), logTime }
 }
+
+function generateLogNameFilter (baseName: string) {
+  return new RegExp('^' + baseName.replace(/\.log$/, '') + '\d*.log$')
+}
index f536da43917345545c5c0df13c0ca0563460a07c..9b258dc3ae86bca21c848953e27566ceeb5452a9 100644 (file)
@@ -9,6 +9,7 @@ import { User, VideoAbuse, VideoChannel, VideoDetails, VideoImport } from '../..
 import { VideoComment } from '../../shared/models/videos/video-comment.model'
 import { CustomConfig } from '../../shared/models/server/custom-config.model'
 import { CONFIG } from '../initializers/config'
+import { AUDIT_LOG_FILENAME } from '@server/initializers/constants'
 
 function getAuditIdFromRes (res: express.Response) {
   return res.locals.oauth.token.User.username
@@ -29,7 +30,7 @@ const auditLogger = winston.createLogger({
   levels: { audit: 0 },
   transports: [
     new winston.transports.File({
-      filename: path.join(CONFIG.STORAGE.LOG_DIR, 'peertube-audit.log'),
+      filename: path.join(CONFIG.STORAGE.LOG_DIR, AUDIT_LOG_FILENAME),
       level: 'audit',
       maxsize: 5242880,
       maxFiles: 5,
index d21746963d01f4b659a1fa2b34bb897f53b13727..c2ff2bae6f34fbf64cad69f2e49ed850182ce431 100644 (file)
@@ -5,6 +5,7 @@ import * as winston from 'winston'
 import { FileTransportOptions } from 'winston/lib/winston/transports'
 import { CONFIG } from '../initializers/config'
 import { omit } from 'lodash'
+import { LOG_FILENAME } from '@server/initializers/constants'
 
 const label = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
 
@@ -58,7 +59,7 @@ const labelFormatter = winston.format.label({
 })
 
 const fileLoggerOptions: FileTransportOptions = {
-  filename: path.join(CONFIG.STORAGE.LOG_DIR, 'peertube.log'),
+  filename: path.join(CONFIG.STORAGE.LOG_DIR, LOG_FILENAME),
   handleExceptions: true,
   format: winston.format.combine(
     winston.format.timestamp(),
index af70e7b88a365f6a9ba883f14eda251aefa3b7a0..bdabe7f6605667482b43ff1152e4a929ad58c95d 100644 (file)
@@ -603,6 +603,8 @@ const FEEDS = {
 }
 
 const MAX_LOGS_OUTPUT_CHARACTERS = 10 * 1000 * 1000
+const LOG_FILENAME = 'peertube.log'
+const AUDIT_LOG_FILENAME = 'peertube-audit.log'
 
 // ---------------------------------------------------------------------------
 
@@ -684,6 +686,7 @@ export {
   BCRYPT_SALT_SIZE,
   TRACKER_RATE_LIMITS,
   FILES_CACHE,
+  LOG_FILENAME,
   CONSTRAINTS_FIELDS,
   EMBED_SIZE,
   REDUNDANCY,
@@ -693,6 +696,7 @@ export {
   OAUTH_LIFETIME,
   CUSTOM_HTML_TAG_COMMENTS,
   BROADCAST_CONCURRENCY,
+  AUDIT_LOG_FILENAME,
   PAGINATION,
   ACTOR_FOLLOW_SCORE,
   PREVIEWS_SIZE,
index 07f3f552f40bbe076f75cffb4a46439981d8de5d..70e4d0d99de196caf5af73910c9a905ecd7c901a 100644 (file)
@@ -24,8 +24,25 @@ const getLogsValidator = [
   }
 ]
 
+const getAuditLogsValidator = [
+  query('startDate')
+    .custom(isDateValid).withMessage('Should have a valid start date'),
+  query('endDate')
+    .optional()
+    .custom(isDateValid).withMessage('Should have a valid end date'),
+
+  (req: express.Request, res: express.Response, next: express.NextFunction) => {
+    logger.debug('Checking getAuditLogsValidator parameters.', { parameters: req.query })
+
+    if (areValidationErrors(req, res)) return
+
+    return next()
+  }
+]
+
 // ---------------------------------------------------------------------------
 
 export {
-  getLogsValidator
+  getLogsValidator,
+  getAuditLogsValidator
 }
index 68f4421993ed276852720bcfe79ca13233ffac8f..d3c87740840f0b9b240d5c4921d66463f6db7f3b 100644 (file)
@@ -2,17 +2,10 @@
 
 import * as chai from 'chai'
 import 'mocha'
-import {
-  flushTests,
-  killallServers,
-  flushAndRunServer,
-  ServerInfo,
-  setAccessTokensToServers,
-  cleanupTests
-} from '../../../../shared/extra-utils/index'
+import { cleanupTests, flushAndRunServer, ServerInfo, setAccessTokensToServers } from '../../../../shared/extra-utils/index'
 import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
 import { uploadVideo } from '../../../../shared/extra-utils/videos/videos'
-import { getLogs } from '../../../../shared/extra-utils/logs/logs'
+import { getAuditLogs, getLogs } from '../../../../shared/extra-utils/logs/logs'
 
 const expect = chai.expect
 
@@ -26,69 +19,123 @@ describe('Test logs', function () {
     await setAccessTokensToServers([ server ])
   })
 
-  it('Should get logs with a start date', async function () {
-    this.timeout(10000)
+  describe('With the standard log file', function () {
+    it('Should get logs with a start date', async function () {
+      this.timeout(10000)
 
-    await uploadVideo(server.url, server.accessToken, { name: 'video 1' })
-    await waitJobs([ server ])
+      await uploadVideo(server.url, server.accessToken, { name: 'video 1' })
+      await waitJobs([ server ])
 
-    const now = new Date()
+      const now = new Date()
 
-    await uploadVideo(server.url, server.accessToken, { name: 'video 2' })
-    await waitJobs([ server ])
+      await uploadVideo(server.url, server.accessToken, { name: 'video 2' })
+      await waitJobs([ server ])
 
-    const res = await getLogs(server.url, server.accessToken, now)
-    const logsString = JSON.stringify(res.body)
+      const res = await getLogs(server.url, server.accessToken, now)
+      const logsString = JSON.stringify(res.body)
 
-    expect(logsString.includes('video 1')).to.be.false
-    expect(logsString.includes('video 2')).to.be.true
-  })
+      expect(logsString.includes('video 1')).to.be.false
+      expect(logsString.includes('video 2')).to.be.true
+    })
+
+    it('Should get logs with an end date', async function () {
+      this.timeout(20000)
+
+      await uploadVideo(server.url, server.accessToken, { name: 'video 3' })
+      await waitJobs([ server ])
+
+      const now1 = new Date()
+
+      await uploadVideo(server.url, server.accessToken, { name: 'video 4' })
+      await waitJobs([ server ])
+
+      const now2 = new Date()
+
+      await uploadVideo(server.url, server.accessToken, { name: 'video 5' })
+      await waitJobs([ server ])
+
+      const res = await getLogs(server.url, server.accessToken, now1, now2)
+      const logsString = JSON.stringify(res.body)
 
-  it('Should get logs with an end date', async function () {
-    this.timeout(20000)
+      expect(logsString.includes('video 3')).to.be.false
+      expect(logsString.includes('video 4')).to.be.true
+      expect(logsString.includes('video 5')).to.be.false
+    })
 
-    await uploadVideo(server.url, server.accessToken, { name: 'video 3' })
-    await waitJobs([ server ])
+    it('Should get filter by level', async function () {
+      this.timeout(10000)
 
-    const now1 = new Date()
+      const now = new Date()
 
-    await uploadVideo(server.url, server.accessToken, { name: 'video 4' })
-    await waitJobs([ server ])
+      await uploadVideo(server.url, server.accessToken, { name: 'video 6' })
+      await waitJobs([ server ])
 
-    const now2 = new Date()
+      {
+        const res = await getLogs(server.url, server.accessToken, now, undefined, 'info')
+        const logsString = JSON.stringify(res.body)
 
-    await uploadVideo(server.url, server.accessToken, { name: 'video 5' })
-    await waitJobs([ server ])
+        expect(logsString.includes('video 6')).to.be.true
+      }
 
-    const res = await getLogs(server.url, server.accessToken, now1, now2)
-    const logsString = JSON.stringify(res.body)
+      {
+        const res = await getLogs(server.url, server.accessToken, now, undefined, 'warn')
+        const logsString = JSON.stringify(res.body)
 
-    expect(logsString.includes('video 3')).to.be.false
-    expect(logsString.includes('video 4')).to.be.true
-    expect(logsString.includes('video 5')).to.be.false
+        expect(logsString.includes('video 6')).to.be.false
+      }
+    })
   })
 
-  it('Should get filter by level', async function () {
-    this.timeout(10000)
+  describe('With the audit log', function () {
+    it('Should get logs with a start date', async function () {
+      this.timeout(10000)
 
-    const now = new Date()
+      await uploadVideo(server.url, server.accessToken, { name: 'video 7' })
+      await waitJobs([ server ])
 
-    await uploadVideo(server.url, server.accessToken, { name: 'video 6' })
-    await waitJobs([ server ])
+      const now = new Date()
 
-    {
-      const res = await getLogs(server.url, server.accessToken, now, undefined, 'info')
+      await uploadVideo(server.url, server.accessToken, { name: 'video 8' })
+      await waitJobs([ server ])
+
+      const res = await getAuditLogs(server.url, server.accessToken, now)
       const logsString = JSON.stringify(res.body)
 
-      expect(logsString.includes('video 6')).to.be.true
-    }
+      expect(logsString.includes('video 7')).to.be.false
+      expect(logsString.includes('video 8')).to.be.true
+
+      expect(res.body).to.have.lengthOf(1)
+
+      const item = res.body[0]
+
+      const message = JSON.parse(item.message)
+      expect(message.domain).to.equal('videos')
+      expect(message.action).to.equal('create')
+    })
+
+    it('Should get logs with an end date', async function () {
+      this.timeout(20000)
+
+      await uploadVideo(server.url, server.accessToken, { name: 'video 9' })
+      await waitJobs([ server ])
+
+      const now1 = new Date()
+
+      await uploadVideo(server.url, server.accessToken, { name: 'video 10' })
+      await waitJobs([ server ])
+
+      const now2 = new Date()
+
+      await uploadVideo(server.url, server.accessToken, { name: 'video 11' })
+      await waitJobs([ server ])
 
-    {
-      const res = await getLogs(server.url, server.accessToken, now, undefined, 'warn')
+      const res = await getAuditLogs(server.url, server.accessToken, now1, now2)
       const logsString = JSON.stringify(res.body)
 
-      expect(logsString.includes('video 6')).to.be.false
-    }
+      expect(logsString.includes('video 9')).to.be.false
+      expect(logsString.includes('video 10')).to.be.true
+      expect(logsString.includes('video 11')).to.be.false
+    })
   })
 
   after(async function () {
index cbb1afb930ab46d614728908d15908941bd91759..c494c1f1eb785767fcdb0b974e51361ee6b83dbf 100644 (file)
@@ -13,6 +13,19 @@ function getLogs (url: string, accessToken: string, startDate: Date, endDate?: D
   })
 }
 
+function getAuditLogs (url: string, accessToken: string, startDate: Date, endDate?: Date) {
+  const path = '/api/v1/server/audit-logs'
+
+  return makeGetRequest({
+    url,
+    path,
+    token: accessToken,
+    query: { startDate, endDate },
+    statusCodeExpected: 200
+  })
+}
+
 export {
-  getLogs
+  getLogs,
+  getAuditLogs
 }
index ce91559e368dc36a6cf8d6b839c8b52a0014e126..4afb92d11482db613f141fb8e90f65e2804e7ccd 100644 (file)
@@ -1 +1 @@
-export type LogLevel = 'debug' | 'info' | 'warn' | 'error'
+export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'audit'