Add debug component to help admins to fix IP issues
authorChocobozzz <me@florianbigard.com>
Thu, 11 Apr 2019 08:56:29 +0000 (10:56 +0200)
committerChocobozzz <me@florianbigard.com>
Thu, 11 Apr 2019 08:58:09 +0000 (10:58 +0200)
19 files changed:
client/src/app/+admin/admin.component.html
client/src/app/+admin/admin.component.ts
client/src/app/+admin/admin.module.ts
client/src/app/+admin/system/debug/debug.component.html [new file with mode: 0644]
client/src/app/+admin/system/debug/debug.component.scss [new file with mode: 0644]
client/src/app/+admin/system/debug/debug.component.ts [new file with mode: 0644]
client/src/app/+admin/system/debug/debug.service.ts [new file with mode: 0644]
client/src/app/+admin/system/debug/index.ts [new file with mode: 0644]
client/src/app/+admin/system/logs/logs.service.ts
client/src/app/+admin/system/system.component.html
client/src/app/+admin/system/system.component.ts
client/src/app/+admin/system/system.routes.ts
server/controllers/api/server/debug.ts [new file with mode: 0644]
server/controllers/api/server/index.ts
server/tests/api/check-params/debug.ts [new file with mode: 0644]
server/tests/api/check-params/index.ts
shared/models/server/debug.model.ts [new file with mode: 0644]
shared/models/server/index.ts
shared/models/users/user-right.enum.ts

index 065d92509b329fff0f7d965dc1c6d1b7b6681dd9..98f45a7d1bf5c7f4dfb17df24aa6bb8613d7e04b 100644 (file)
@@ -16,7 +16,7 @@
       Configuration
     </a>
 
-    <a i18n *ngIf="hasJobsRight() || hasLogsRight()" routerLink="/admin/system" routerLinkActive="active" class="title-page">
+    <a i18n *ngIf="hasJobsRight() || hasLogsRight() || hasDebugRight()" routerLink="/admin/system" routerLinkActive="active" class="title-page">
       System
     </a>
   </div>
index fc775a5a45c32c6ab1740a2909dfe34cc07b7c6a..408de4837bcd21a8ebf1a1324b555f8efe37a0e7 100644 (file)
@@ -24,15 +24,19 @@ export class AdminComponent {
     return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)
   }
 
-  hasJobsRight () {
-    return this.auth.getUser().hasRight(UserRight.MANAGE_JOBS)
+  hasConfigRight () {
+    return this.auth.getUser().hasRight(UserRight.MANAGE_CONFIGURATION)
   }
 
   hasLogsRight () {
     return this.auth.getUser().hasRight(UserRight.MANAGE_LOGS)
   }
 
-  hasConfigRight () {
-    return this.auth.getUser().hasRight(UserRight.MANAGE_CONFIGURATION)
+  hasJobsRight () {
+    return this.auth.getUser().hasRight(UserRight.MANAGE_JOBS)
+  }
+
+  hasDebugRight () {
+    return this.auth.getUser().hasRight(UserRight.MANAGE_DEBUG)
   }
 }
index ae0af686b1766cfe81a7e53dc72352ab482900af..71a4dfc4a588d547bc18ecd165b588fe38e93080 100644 (file)
@@ -20,6 +20,7 @@ import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service
 import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist'
 import { JobsComponent } from '@app/+admin/system/jobs/jobs.component'
 import { JobService, LogsComponent, LogsService, SystemComponent } from '@app/+admin/system'
+import { DebugComponent, DebugService } from '@app/+admin/system/debug'
 
 @NgModule({
   imports: [
@@ -54,6 +55,7 @@ import { JobService, LogsComponent, LogsService, SystemComponent } from '@app/+a
     SystemComponent,
     JobsComponent,
     LogsComponent,
+    DebugComponent,
 
     ConfigComponent,
     EditCustomConfigComponent
@@ -68,6 +70,7 @@ import { JobService, LogsComponent, LogsService, SystemComponent } from '@app/+a
     RedundancyService,
     JobService,
     LogsService,
+    DebugService,
     ConfigService
   ]
 })
diff --git a/client/src/app/+admin/system/debug/debug.component.html b/client/src/app/+admin/system/debug/debug.component.html
new file mode 100644 (file)
index 0000000..f35414b
--- /dev/null
@@ -0,0 +1,19 @@
+<div class="root">
+  <h4>IP</h4>
+
+  <p>PeerTube thinks your public IP is <strong>{{ debug?.ip }}</strong>.</p>
+
+  <p>If this is not your correct public IP, please consider fixing it because:</p>
+  <ul>
+    <li>Views may not be counted correctly (reduced compared to what they should be)</li>
+    <li>Anti brute force system could be overzealous</li>
+    <li>P2P system could not work correctly</li>
+  </ul>
+
+  <p>To fix it:<p>
+  <ul>
+    <li>Check the <code>trust_proxy</code> configuration key</li>
+    <li>If you run PeerTube using Docker, check you run the <code>reverse-proxy</code> with <code>network_mode: "host"</code>
+      (see <a href="https://github.com/Chocobozzz/PeerTube/issues/1643#issuecomment-464789666">issue 1643</a>)</li>
+  </ul>
+</div>
diff --git a/client/src/app/+admin/system/debug/debug.component.scss b/client/src/app/+admin/system/debug/debug.component.scss
new file mode 100644 (file)
index 0000000..90addd2
--- /dev/null
@@ -0,0 +1,6 @@
+@import '_variables';
+@import '_mixins';
+
+.root {
+  font-size: 14px;
+}
diff --git a/client/src/app/+admin/system/debug/debug.component.ts b/client/src/app/+admin/system/debug/debug.component.ts
new file mode 100644 (file)
index 0000000..8a77f79
--- /dev/null
@@ -0,0 +1,31 @@
+import { Component, OnInit } from '@angular/core'
+import { Notifier } from '@app/core'
+import { Debug } from '@shared/models/server'
+import { DebugService } from '@app/+admin/system/debug/debug.service'
+
+@Component({
+  templateUrl: './debug.component.html',
+  styleUrls: [ './debug.component.scss' ]
+})
+export class DebugComponent implements OnInit {
+  debug: Debug
+
+  constructor (
+    private debugService: DebugService,
+    private notifier: Notifier
+  ) {
+  }
+
+  ngOnInit (): void {
+    this.load()
+  }
+
+  load () {
+    this.debugService.getDebug()
+        .subscribe(
+          debug => this.debug = debug,
+
+          err => this.notifier.error(err.message)
+        )
+  }
+}
diff --git a/client/src/app/+admin/system/debug/debug.service.ts b/client/src/app/+admin/system/debug/debug.service.ts
new file mode 100644 (file)
index 0000000..6c722d1
--- /dev/null
@@ -0,0 +1,25 @@
+import { catchError } from 'rxjs/operators'
+import { HttpClient } from '@angular/common/http'
+import { Injectable } from '@angular/core'
+import { Observable } from 'rxjs'
+import { environment } from '../../../../environments/environment'
+import { RestExtractor, RestService } from '../../../shared'
+import { Debug } from '@shared/models/server'
+
+@Injectable()
+export class DebugService {
+  private static BASE_DEBUG_URL = environment.apiUrl + '/api/v1/server/debug'
+
+  constructor (
+    private authHttp: HttpClient,
+    private restService: RestService,
+    private restExtractor: RestExtractor
+  ) {}
+
+  getDebug (): Observable<Debug> {
+    return this.authHttp.get<Debug>(DebugService.BASE_DEBUG_URL)
+               .pipe(
+                 catchError(err => this.restExtractor.handleError(err))
+               )
+  }
+}
diff --git a/client/src/app/+admin/system/debug/index.ts b/client/src/app/+admin/system/debug/index.ts
new file mode 100644 (file)
index 0000000..7fc7a07
--- /dev/null
@@ -0,0 +1,2 @@
+export * from './debug.component'
+export * from './debug.service'
index 4db79a1fa07382a6de0f1a8aaad1ae70afd90aa7..24b9cb6d1f3ea567e6ae828d516b82d2bfe5734d 100644 (file)
@@ -9,7 +9,7 @@ import { LogLevel } from '@shared/models/server/log-level.type'
 
 @Injectable()
 export class LogsService {
-  private static BASE_JOB_URL = environment.apiUrl + '/api/v1/server/logs'
+  private static BASE_LOG_URL = environment.apiUrl + '/api/v1/server/logs'
 
   constructor (
     private authHttp: HttpClient,
@@ -17,14 +17,14 @@ export class LogsService {
     private restExtractor: RestExtractor
   ) {}
 
-  getLogs (level: LogLevel, startDate: string, endDate?: string): Observable<any> {
+  getLogs (level: LogLevel, startDate: string, endDate?: string): Observable<any[]> {
     let params = new HttpParams()
     params = params.append('startDate', startDate)
     params = params.append('level', level)
 
     if (endDate) params.append('endDate', endDate)
 
-    return this.authHttp.get<any[]>(LogsService.BASE_JOB_URL, { params })
+    return this.authHttp.get<any[]>(LogsService.BASE_LOG_URL, { params })
                .pipe(
                  map(rows => rows.map(r => new LogRow(r))),
                  catchError(err => this.restExtractor.handleError(err))
index 345a101e64bd9407479f1bf08e5cb7d1186a5eaf..7c4278d35c6dfd5bb65e53d9413680b58f441b80 100644 (file)
@@ -2,9 +2,11 @@
   <div i18n class="form-sub-title">System</div>
 
   <div class="admin-sub-nav">
-    <a i18n routerLink="jobs" routerLinkActive="active">Jobs</a>
+    <a *ngIf="hasJobsRight()" i18n routerLink="jobs" routerLinkActive="active">Jobs</a>
 
-    <a i18n routerLink="logs" routerLinkActive="active">Logs</a>
+    <a *ngIf="hasLogsRight()" i18n routerLink="logs" routerLinkActive="active">Logs</a>
+
+    <a *ngIf="hasDebugRight()" i18n routerLink="debug" routerLinkActive="active">Debug</a>
   </div>
 </div>
 
index 992d9c8af1b3b8cf28307ef8e48cf41822da5826..b544c2a97905ab4b76daecc7fdc34c66ef96dbe3 100644 (file)
@@ -1,8 +1,24 @@
 import { Component } from '@angular/core'
+import { UserRight } from '@shared/models'
+import { AuthService } from '@app/core'
 
 @Component({
   templateUrl: './system.component.html',
   styleUrls: [ './system.component.scss' ]
 })
 export class SystemComponent {
+
+  constructor (private auth: AuthService) {}
+
+  hasLogsRight () {
+    return this.auth.getUser().hasRight(UserRight.MANAGE_LOGS)
+  }
+
+  hasJobsRight () {
+    return this.auth.getUser().hasRight(UserRight.MANAGE_JOBS)
+  }
+
+  hasDebugRight () {
+    return this.auth.getUser().hasRight(UserRight.MANAGE_DEBUG)
+  }
 }
index e6d45b7608c6dc1fcf3784e01028a92bd49bdef6..2d851794d22faa038bbc28ee63591f2cd49fab24 100644 (file)
@@ -4,6 +4,7 @@ import { UserRight } from '../../../../../shared'
 import { JobsComponent } from '@app/+admin/system/jobs/jobs.component'
 import { LogsComponent } from '@app/+admin/system/logs'
 import { SystemComponent } from '@app/+admin/system/system.component'
+import { DebugComponent } from '@app/+admin/system/debug'
 
 export const SystemRoutes: Routes = [
   {
@@ -38,6 +39,17 @@ export const SystemRoutes: Routes = [
             title: 'Logs'
           }
         }
+      },
+      {
+        path: 'debug',
+        canActivate: [ UserRightGuard ],
+        component: DebugComponent,
+        data: {
+          meta: {
+            userRight: UserRight.MANAGE_DEBUG,
+            title: 'Debug'
+          }
+        }
       }
     ]
   }
diff --git a/server/controllers/api/server/debug.ts b/server/controllers/api/server/debug.ts
new file mode 100644 (file)
index 0000000..4450038
--- /dev/null
@@ -0,0 +1,25 @@
+import * as express from 'express'
+import { UserRight } from '../../../../shared/models/users'
+import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares'
+
+const debugRouter = express.Router()
+
+debugRouter.get('/debug',
+  authenticate,
+  ensureUserHasRight(UserRight.MANAGE_DEBUG),
+  asyncMiddleware(getDebug)
+)
+
+// ---------------------------------------------------------------------------
+
+export {
+  debugRouter
+}
+
+// ---------------------------------------------------------------------------
+
+async function getDebug (req: express.Request, res: express.Response) {
+  return res.json({
+    ip: req.ip
+  }).end()
+}
index de09588df2f6f9a13a04b86c0cf2db1e33c77ee0..6b8793a196d108fecf646db9c77eb40e865bc447 100644 (file)
@@ -5,6 +5,7 @@ import { serverRedundancyRouter } from './redundancy'
 import { serverBlocklistRouter } from './server-blocklist'
 import { contactRouter } from './contact'
 import { logsRouter } from './logs'
+import { debugRouter } from './debug'
 
 const serverRouter = express.Router()
 
@@ -14,6 +15,7 @@ serverRouter.use('/', statsRouter)
 serverRouter.use('/', serverBlocklistRouter)
 serverRouter.use('/', contactRouter)
 serverRouter.use('/', logsRouter)
+serverRouter.use('/', debugRouter)
 
 // ---------------------------------------------------------------------------
 
diff --git a/server/tests/api/check-params/debug.ts b/server/tests/api/check-params/debug.ts
new file mode 100644 (file)
index 0000000..9bf6646
--- /dev/null
@@ -0,0 +1,78 @@
+/* tslint:disable:no-unused-expression */
+
+import 'mocha'
+
+import {
+  createUser,
+  flushTests,
+  killallServers,
+  runServer,
+  ServerInfo,
+  setAccessTokensToServers,
+  userLogin
+} from '../../../../shared/utils'
+import { makeGetRequest } from '../../../../shared/utils/requests/requests'
+
+describe('Test debug API validators', function () {
+  const path = '/api/v1/server/debug'
+  let server: ServerInfo
+  let userAccessToken = ''
+
+  // ---------------------------------------------------------------
+
+  before(async function () {
+    this.timeout(120000)
+
+    await flushTests()
+
+    server = await runServer(1)
+
+    await setAccessTokensToServers([ server ])
+
+    const user = {
+      username: 'user1',
+      password: 'my super password'
+    }
+    await createUser(server.url, server.accessToken, user.username, user.password)
+    userAccessToken = await userLogin(server, user)
+  })
+
+  describe('When getting debug endpoint', function () {
+
+    it('Should fail with a non authenticated user', async function () {
+      await makeGetRequest({
+        url: server.url,
+        path,
+        statusCodeExpected: 401
+      })
+    })
+
+    it('Should fail with a non admin user', async function () {
+      await makeGetRequest({
+        url: server.url,
+        path,
+        token: userAccessToken,
+        statusCodeExpected: 403
+      })
+    })
+
+    it('Should succeed with the correct params', async function () {
+      await makeGetRequest({
+        url: server.url,
+        path,
+        token: server.accessToken,
+        query: { startDate: new Date().toISOString() },
+        statusCodeExpected: 200
+      })
+    })
+  })
+
+  after(async function () {
+    killallServers([ server ])
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
index bdac95025619c848dd61f735dded5abcf5539d02..844fa31c5eac6774a1a95cd61f1f95aeac40aef0 100644 (file)
@@ -2,6 +2,7 @@ import './accounts'
 import './blocklist'
 import './config'
 import './contact-form'
+import './debug'
 import './follows'
 import './jobs'
 import './logs'
diff --git a/shared/models/server/debug.model.ts b/shared/models/server/debug.model.ts
new file mode 100644 (file)
index 0000000..61cba65
--- /dev/null
@@ -0,0 +1,3 @@
+export interface Debug {
+  ip: string
+}
index c42f6f67f6234b629c7b50313b2de903b36907a6..bf61ab270607af8593bb3aad87880dfd71e80514 100644 (file)
@@ -1,6 +1,7 @@
 export * from './about.model'
 export * from './contact-form.model'
 export * from './custom-config.model'
+export * from './debug.model'
 export * from './job.model'
 export * from './server-config.model'
 export * from './server-stats.model'
index 5ec255ea543a60dad5111c961cecffbf9f1cdbc8..71701bdb480a92e932abed05602f7dac4ae35505 100644 (file)
@@ -7,6 +7,8 @@ export enum UserRight {
 
   MANAGE_LOGS,
 
+  MANAGE_DEBUG,
+
   MANAGE_SERVER_REDUNDANCY,
 
   MANAGE_VIDEO_ABUSES,