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>
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)
}
}
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: [
SystemComponent,
JobsComponent,
LogsComponent,
+ DebugComponent,
ConfigComponent,
EditCustomConfigComponent
RedundancyService,
JobService,
LogsService,
+ DebugService,
ConfigService
]
})
--- /dev/null
+<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>
--- /dev/null
+@import '_variables';
+@import '_mixins';
+
+.root {
+ font-size: 14px;
+}
--- /dev/null
+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)
+ )
+ }
+}
--- /dev/null
+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))
+ )
+ }
+}
--- /dev/null
+export * from './debug.component'
+export * from './debug.service'
@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,
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))
<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>
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)
+ }
}
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 = [
{
title: 'Logs'
}
}
+ },
+ {
+ path: 'debug',
+ canActivate: [ UserRightGuard ],
+ component: DebugComponent,
+ data: {
+ meta: {
+ userRight: UserRight.MANAGE_DEBUG,
+ title: 'Debug'
+ }
+ }
}
]
}
--- /dev/null
+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()
+}
import { serverBlocklistRouter } from './server-blocklist'
import { contactRouter } from './contact'
import { logsRouter } from './logs'
+import { debugRouter } from './debug'
const serverRouter = express.Router()
serverRouter.use('/', serverBlocklistRouter)
serverRouter.use('/', contactRouter)
serverRouter.use('/', logsRouter)
+serverRouter.use('/', debugRouter)
// ---------------------------------------------------------------------------
--- /dev/null
+/* 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()
+ }
+ })
+})
import './blocklist'
import './config'
import './contact-form'
+import './debug'
import './follows'
import './jobs'
import './logs'
--- /dev/null
+export interface Debug {
+ ip: string
+}
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'
MANAGE_LOGS,
+ MANAGE_DEBUG,
+
MANAGE_SERVER_REDUNDANCY,
MANAGE_VIDEO_ABUSES,