aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-05-14 12:04:44 +0200
committerChocobozzz <me@florianbigard.com>2021-05-14 13:37:23 +0200
commitaea0b0e7cde7495e60fe07b4444067f53d35ce3f (patch)
tree61d1e161bb32be144d46b9f5f51f1386e6819b0b
parentc76ecc3ff746d78519404db4c525fd024f9a51c0 (diff)
downloadPeerTube-aea0b0e7cde7495e60fe07b4444067f53d35ce3f.tar.gz
PeerTube-aea0b0e7cde7495e60fe07b4444067f53d35ce3f.tar.zst
PeerTube-aea0b0e7cde7495e60fe07b4444067f53d35ce3f.zip
Inject server config in HTML
-rw-r--r--client/src/app/+videos/video-list/trending/video-trending-header.component.ts12
-rw-r--r--client/src/app/+videos/video-list/trending/video-trending.component.ts7
-rw-r--r--client/src/app/app.component.ts2
-rw-r--r--client/src/app/core/routing/redirect.service.ts35
-rw-r--r--client/src/app/core/server/server.service.ts25
-rw-r--r--client/src/index.html1
-rw-r--r--client/src/standalone/videos/embed.html12
-rw-r--r--client/src/standalone/videos/embed.ts52
-rw-r--r--server/initializers/constants.ts3
-rw-r--r--server/lib/client-html.ts18
-rw-r--r--server/lib/config.ts31
-rw-r--r--server/models/abuse/abuse.ts3
-rw-r--r--server/tests/client.ts28
-rw-r--r--shared/extra-utils/server/servers.ts3
-rw-r--r--shared/models/server/server-config.model.ts2
15 files changed, 144 insertions, 90 deletions
diff --git a/client/src/app/+videos/video-list/trending/video-trending-header.component.ts b/client/src/app/+videos/video-list/trending/video-trending-header.component.ts
index 55040f3c9..bbb02a236 100644
--- a/client/src/app/+videos/video-list/trending/video-trending-header.component.ts
+++ b/client/src/app/+videos/video-list/trending/video-trending-header.component.ts
@@ -31,7 +31,8 @@ export class VideoTrendingHeaderComponent extends VideoListHeaderComponent imple
31 private route: ActivatedRoute, 31 private route: ActivatedRoute,
32 private router: Router, 32 private router: Router,
33 private auth: AuthService, 33 private auth: AuthService,
34 private serverService: ServerService 34 private serverService: ServerService,
35 private redirectService: RedirectService
35 ) { 36 ) {
36 super(data) 37 super(data)
37 38
@@ -84,12 +85,7 @@ export class VideoTrendingHeaderComponent extends VideoListHeaderComponent imple
84 85
85 this.algorithmChangeSub = this.route.queryParams.subscribe( 86 this.algorithmChangeSub = this.route.queryParams.subscribe(
86 queryParams => { 87 queryParams => {
87 const algorithm = queryParams['alg'] 88 this.data.model = queryParams['alg'] || this.redirectService.getDefaultTrendingAlgorithm()
88 if (algorithm) {
89 this.data.model = algorithm
90 } else {
91 this.data.model = RedirectService.DEFAULT_TRENDING_ALGORITHM
92 }
93 } 89 }
94 ) 90 )
95 } 91 }
@@ -99,7 +95,7 @@ export class VideoTrendingHeaderComponent extends VideoListHeaderComponent imple
99 } 95 }
100 96
101 setSort () { 97 setSort () {
102 const alg = this.data.model !== RedirectService.DEFAULT_TRENDING_ALGORITHM 98 const alg = this.data.model !== this.redirectService.getDefaultTrendingAlgorithm()
103 ? this.data.model 99 ? this.data.model
104 : undefined 100 : undefined
105 101
diff --git a/client/src/app/+videos/video-list/trending/video-trending.component.ts b/client/src/app/+videos/video-list/trending/video-trending.component.ts
index e50d6ec3a..ebec672f3 100644
--- a/client/src/app/+videos/video-list/trending/video-trending.component.ts
+++ b/client/src/app/+videos/video-list/trending/video-trending.component.ts
@@ -35,11 +35,12 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
35 protected storageService: LocalStorageService, 35 protected storageService: LocalStorageService,
36 protected cfr: ComponentFactoryResolver, 36 protected cfr: ComponentFactoryResolver,
37 private videoService: VideoService, 37 private videoService: VideoService,
38 private redirectService: RedirectService,
38 private hooks: HooksService 39 private hooks: HooksService
39 ) { 40 ) {
40 super() 41 super()
41 42
42 this.defaultSort = this.parseAlgorithm(RedirectService.DEFAULT_TRENDING_ALGORITHM) 43 this.defaultSort = this.parseAlgorithm(this.redirectService.getDefaultTrendingAlgorithm())
43 44
44 this.headerComponentInjector = this.getInjector() 45 this.headerComponentInjector = this.getInjector()
45 } 46 }
@@ -106,7 +107,7 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
106 } 107 }
107 108
108 protected loadPageRouteParams (queryParams: Params) { 109 protected loadPageRouteParams (queryParams: Params) {
109 const algorithm = queryParams['alg'] || RedirectService.DEFAULT_TRENDING_ALGORITHM 110 const algorithm = queryParams['alg'] || this.redirectService.getDefaultTrendingAlgorithm()
110 111
111 this.sort = this.parseAlgorithm(algorithm) 112 this.sort = this.parseAlgorithm(algorithm)
112 } 113 }
@@ -115,8 +116,10 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
115 switch (algorithm) { 116 switch (algorithm) {
116 case 'most-viewed': 117 case 'most-viewed':
117 return '-trending' 118 return '-trending'
119
118 case 'most-liked': 120 case 'most-liked':
119 return '-likes' 121 return '-likes'
122
120 default: 123 default:
121 return '-' + algorithm as VideoSortField 124 return '-' + algorithm as VideoSortField
122 } 125 }
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts
index 66d871b4a..239e275a4 100644
--- a/client/src/app/app.component.ts
+++ b/client/src/app/app.component.ts
@@ -67,7 +67,7 @@ export class AppComponent implements OnInit, AfterViewInit {
67 } 67 }
68 68
69 goToDefaultRoute () { 69 goToDefaultRoute () {
70 return this.router.navigateByUrl(RedirectService.DEFAULT_ROUTE) 70 return this.router.navigateByUrl(this.redirectService.getDefaultRoute())
71 } 71 }
72 72
73 ngOnInit () { 73 ngOnInit () {
diff --git a/client/src/app/core/routing/redirect.service.ts b/client/src/app/core/routing/redirect.service.ts
index 6d26fb504..cf690a4d0 100644
--- a/client/src/app/core/routing/redirect.service.ts
+++ b/client/src/app/core/routing/redirect.service.ts
@@ -6,14 +6,14 @@ import { ServerService } from '../server'
6export class RedirectService { 6export class RedirectService {
7 // Default route could change according to the instance configuration 7 // Default route could change according to the instance configuration
8 static INIT_DEFAULT_ROUTE = '/videos/trending' 8 static INIT_DEFAULT_ROUTE = '/videos/trending'
9 static DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE
10 static INIT_DEFAULT_TRENDING_ALGORITHM = 'most-viewed' 9 static INIT_DEFAULT_TRENDING_ALGORITHM = 'most-viewed'
11 static DEFAULT_TRENDING_ALGORITHM = RedirectService.INIT_DEFAULT_TRENDING_ALGORITHM
12 10
13 private previousUrl: string 11 private previousUrl: string
14 private currentUrl: string 12 private currentUrl: string
15 13
16 private redirectingToHomepage = false 14 private redirectingToHomepage = false
15 private defaultTrendingAlgorithm = RedirectService.INIT_DEFAULT_TRENDING_ALGORITHM
16 private defaultRoute = RedirectService.INIT_DEFAULT_ROUTE
17 17
18 constructor ( 18 constructor (
19 private router: Router, 19 private router: Router,
@@ -22,10 +22,10 @@ export class RedirectService {
22 // The config is first loaded from the cache so try to get the default route 22 // The config is first loaded from the cache so try to get the default route
23 const tmpConfig = this.serverService.getTmpConfig() 23 const tmpConfig = this.serverService.getTmpConfig()
24 if (tmpConfig?.instance?.defaultClientRoute) { 24 if (tmpConfig?.instance?.defaultClientRoute) {
25 RedirectService.DEFAULT_ROUTE = tmpConfig.instance.defaultClientRoute 25 this.defaultRoute = tmpConfig.instance.defaultClientRoute
26 } 26 }
27 if (tmpConfig?.trending?.videos?.algorithms?.default) { 27 if (tmpConfig?.trending?.videos?.algorithms?.default) {
28 RedirectService.DEFAULT_TRENDING_ALGORITHM = tmpConfig.trending.videos.algorithms.default 28 this.defaultTrendingAlgorithm = tmpConfig.trending.videos.algorithms.default
29 } 29 }
30 30
31 // Load default route 31 // Load default route
@@ -34,13 +34,8 @@ export class RedirectService {
34 const defaultRouteConfig = config.instance.defaultClientRoute 34 const defaultRouteConfig = config.instance.defaultClientRoute
35 const defaultTrendingConfig = config.trending.videos.algorithms.default 35 const defaultTrendingConfig = config.trending.videos.algorithms.default
36 36
37 if (defaultRouteConfig) { 37 if (defaultRouteConfig) this.defaultRoute = defaultRouteConfig
38 RedirectService.DEFAULT_ROUTE = defaultRouteConfig 38 if (defaultTrendingConfig) this.defaultTrendingAlgorithm = defaultTrendingConfig
39 }
40
41 if (defaultTrendingConfig) {
42 RedirectService.DEFAULT_TRENDING_ALGORITHM = defaultTrendingConfig
43 }
44 }) 39 })
45 40
46 // Track previous url 41 // Track previous url
@@ -53,6 +48,14 @@ export class RedirectService {
53 }) 48 })
54 } 49 }
55 50
51 getDefaultRoute () {
52 return this.defaultRoute
53 }
54
55 getDefaultTrendingAlgorithm () {
56 return this.defaultTrendingAlgorithm
57 }
58
56 redirectToPreviousRoute () { 59 redirectToPreviousRoute () {
57 const exceptions = [ 60 const exceptions = [
58 '/verify-account', 61 '/verify-account',
@@ -72,21 +75,21 @@ export class RedirectService {
72 75
73 this.redirectingToHomepage = true 76 this.redirectingToHomepage = true
74 77
75 console.log('Redirecting to %s...', RedirectService.DEFAULT_ROUTE) 78 console.log('Redirecting to %s...', this.defaultRoute)
76 79
77 this.router.navigateByUrl(RedirectService.DEFAULT_ROUTE, { skipLocationChange }) 80 this.router.navigateByUrl(this.defaultRoute, { skipLocationChange })
78 .then(() => this.redirectingToHomepage = false) 81 .then(() => this.redirectingToHomepage = false)
79 .catch(() => { 82 .catch(() => {
80 this.redirectingToHomepage = false 83 this.redirectingToHomepage = false
81 84
82 console.error( 85 console.error(
83 'Cannot navigate to %s, resetting default route to %s.', 86 'Cannot navigate to %s, resetting default route to %s.',
84 RedirectService.DEFAULT_ROUTE, 87 this.defaultRoute,
85 RedirectService.INIT_DEFAULT_ROUTE 88 RedirectService.INIT_DEFAULT_ROUTE
86 ) 89 )
87 90
88 RedirectService.DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE 91 this.defaultRoute = RedirectService.INIT_DEFAULT_ROUTE
89 return this.router.navigateByUrl(RedirectService.DEFAULT_ROUTE, { skipLocationChange }) 92 return this.router.navigateByUrl(this.defaultRoute, { skipLocationChange })
90 }) 93 })
91 94
92 } 95 }
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts
index 906191ae1..e48786e18 100644
--- a/client/src/app/core/server/server.service.ts
+++ b/client/src/app/core/server/server.service.ts
@@ -3,7 +3,6 @@ import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators'
3import { HttpClient } from '@angular/common/http' 3import { HttpClient } from '@angular/common/http'
4import { Inject, Injectable, LOCALE_ID } from '@angular/core' 4import { Inject, Injectable, LOCALE_ID } from '@angular/core'
5import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers' 5import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
6import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
7import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n' 6import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
8import { SearchTargetType, ServerConfig, ServerStats, VideoConstant } from '@shared/models' 7import { SearchTargetType, ServerConfig, ServerStats, VideoConstant } from '@shared/models'
9import { environment } from '../../../environments/environment' 8import { environment } from '../../../environments/environment'
@@ -16,8 +15,6 @@ export class ServerService {
16 private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/' 15 private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/'
17 private static BASE_STATS_URL = environment.apiUrl + '/api/v1/server/stats' 16 private static BASE_STATS_URL = environment.apiUrl + '/api/v1/server/stats'
18 17
19 private static CONFIG_LOCAL_STORAGE_KEY = 'server-config'
20
21 configReloaded = new Subject<ServerConfig>() 18 configReloaded = new Subject<ServerConfig>()
22 19
23 private localeObservable: Observable<any> 20 private localeObservable: Observable<any>
@@ -212,7 +209,6 @@ export class ServerService {
212 if (!this.configObservable) { 209 if (!this.configObservable) {
213 this.configObservable = this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL) 210 this.configObservable = this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL)
214 .pipe( 211 .pipe(
215 tap(config => this.saveConfigLocally(config)),
216 tap(config => { 212 tap(config => {
217 this.config = config 213 this.config = config
218 this.configLoaded = true 214 this.configLoaded = true
@@ -343,20 +339,15 @@ export class ServerService {
343 ) 339 )
344 } 340 }
345 341
346 private saveConfigLocally (config: ServerConfig) {
347 peertubeLocalStorage.setItem(ServerService.CONFIG_LOCAL_STORAGE_KEY, JSON.stringify(config))
348 }
349
350 private loadConfigLocally () { 342 private loadConfigLocally () {
351 const configString = peertubeLocalStorage.getItem(ServerService.CONFIG_LOCAL_STORAGE_KEY) 343 const configString = window['PeerTubeServerConfig']
352 344 if (!configString) return
353 if (configString) { 345
354 try { 346 try {
355 const parsed = JSON.parse(configString) 347 const parsed = JSON.parse(configString)
356 Object.assign(this.config, parsed) 348 Object.assign(this.config, parsed)
357 } catch (err) { 349 } catch (err) {
358 console.error('Cannot parse config saved in local storage.', err) 350 console.error('Cannot parse config saved in from index.html.', err)
359 }
360 } 351 }
361 } 352 }
362} 353}
diff --git a/client/src/index.html b/client/src/index.html
index 72c184dc1..28667cdd0 100644
--- a/client/src/index.html
+++ b/client/src/index.html
@@ -29,6 +29,7 @@
29 <!-- description tag --> 29 <!-- description tag -->
30 <!-- custom css tag --> 30 <!-- custom css tag -->
31 <!-- meta tags --> 31 <!-- meta tags -->
32 <!-- server config -->
32 33
33 <!-- /!\ Do not remove it /!\ --> 34 <!-- /!\ Do not remove it /!\ -->
34 </head> 35 </head>
diff --git a/client/src/standalone/videos/embed.html b/client/src/standalone/videos/embed.html
index 7d09bfb8f..e13a4dc24 100644
--- a/client/src/standalone/videos/embed.html
+++ b/client/src/standalone/videos/embed.html
@@ -1,14 +1,22 @@
1<!DOCTYPE html> 1<!DOCTYPE html>
2<html> 2<html>
3 <head> 3 <head>
4 <!-- title tag -->
5
6 <meta charset="UTF-8"> 4 <meta charset="UTF-8">
7 <meta name="viewport" content="width=device-width, initial-scale=1"> 5 <meta name="viewport" content="width=device-width, initial-scale=1">
8 <meta name="robots" content="noindex"> 6 <meta name="robots" content="noindex">
9 <meta property="og:platform" content="PeerTube" /> 7 <meta property="og:platform" content="PeerTube" />
10 8
9
10 <!-- /!\ The following comment is used by the server to prerender some tags /!\ -->
11
12 <!-- title tag -->
13 <!-- description tag -->
11 <!-- custom css tag --> 14 <!-- custom css tag -->
15 <!-- meta tags -->
16 <!-- server config -->
17
18 <!-- /!\ Do not remove it /!\ -->
19
12 <link rel="icon" type="image/png" href="/client/assets/images/favicon.png" /> 20 <link rel="icon" type="image/png" href="/client/assets/images/favicon.png" />
13 </head> 21 </head>
14 22
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index 3a90fdc58..fc61d3730 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -1,28 +1,28 @@
1import './embed.scss' 1import './embed.scss'
2import videojs from 'video.js' 2import videojs from 'video.js'
3import { peertubeTranslate } from '../../../../shared/core-utils/i18n' 3import { peertubeTranslate } from '../../../../shared/core-utils/i18n'
4import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
4import { 5import {
6 ClientHookName,
7 HTMLServerConfig,
8 PluginType,
5 ResultList, 9 ResultList,
6 ServerConfig,
7 UserRefreshToken, 10 UserRefreshToken,
8 VideoCaption, 11 VideoCaption,
9 VideoDetails, 12 VideoDetails,
10 VideoPlaylist, 13 VideoPlaylist,
11 VideoPlaylistElement, 14 VideoPlaylistElement,
12 VideoStreamingPlaylistType, 15 VideoStreamingPlaylistType
13 PluginType,
14 ClientHookName
15} from '../../../../shared/models' 16} from '../../../../shared/models'
16import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
17import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player/peertube-player-manager' 17import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player/peertube-player-manager'
18import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' 18import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
19import { TranslationsManager } from '../../assets/player/translations-manager' 19import { TranslationsManager } from '../../assets/player/translations-manager'
20import { peertubeLocalStorage } from '../../root-helpers/peertube-web-storage'
20import { Hooks, loadPlugin, runHook } from '../../root-helpers/plugins' 21import { Hooks, loadPlugin, runHook } from '../../root-helpers/plugins'
21import { Tokens } from '../../root-helpers/users' 22import { Tokens } from '../../root-helpers/users'
22import { peertubeLocalStorage } from '../../root-helpers/peertube-web-storage'
23import { objectToUrlEncoded } from '../../root-helpers/utils' 23import { objectToUrlEncoded } from '../../root-helpers/utils'
24import { PeerTubeEmbedApi } from './embed-api'
25import { RegisterClientHelpers } from '../../types/register-client-option.model' 24import { RegisterClientHelpers } from '../../types/register-client-option.model'
25import { PeerTubeEmbedApi } from './embed-api'
26 26
27type Translations = { [ id: string ]: string } 27type Translations = { [ id: string ]: string }
28 28
@@ -56,8 +56,9 @@ export class PeerTubeEmbed {
56 CLIENT_SECRET: 'client_secret' 56 CLIENT_SECRET: 'client_secret'
57 } 57 }
58 58
59 config: HTMLServerConfig
60
59 private translationsPromise: Promise<{ [id: string]: string }> 61 private translationsPromise: Promise<{ [id: string]: string }>
60 private configPromise: Promise<ServerConfig>
61 private PeertubePlayerManagerModulePromise: Promise<any> 62 private PeertubePlayerManagerModulePromise: Promise<any>
62 63
63 private playlist: VideoPlaylist 64 private playlist: VideoPlaylist
@@ -77,6 +78,12 @@ export class PeerTubeEmbed {
77 78
78 constructor (private videoWrapperId: string) { 79 constructor (private videoWrapperId: string) {
79 this.wrapperElement = document.getElementById(this.videoWrapperId) 80 this.wrapperElement = document.getElementById(this.videoWrapperId)
81
82 try {
83 this.config = JSON.parse(window['PeerTubeServerConfig'])
84 } catch (err) {
85 console.error('Cannot parse HTML config.', err)
86 }
80 } 87 }
81 88
82 getVideoUrl (id: string) { 89 getVideoUrl (id: string) {
@@ -166,11 +173,6 @@ export class PeerTubeEmbed {
166 return this.refreshFetch(url.toString(), { headers: this.headers }) 173 return this.refreshFetch(url.toString(), { headers: this.headers })
167 } 174 }
168 175
169 loadConfig (): Promise<ServerConfig> {
170 return this.refreshFetch('/api/v1/config')
171 .then(res => res.json())
172 }
173
174 removeElement (element: HTMLElement) { 176 removeElement (element: HTMLElement) {
175 element.parentElement.removeChild(element) 177 element.parentElement.removeChild(element)
176 } 178 }
@@ -466,6 +468,12 @@ export class PeerTubeEmbed {
466 this.playerElement.setAttribute('playsinline', 'true') 468 this.playerElement.setAttribute('playsinline', 'true')
467 this.wrapperElement.appendChild(this.playerElement) 469 this.wrapperElement.appendChild(this.playerElement)
468 470
471 // Issue when we parsed config from HTML, fallback to API
472 if (!this.config) {
473 this.config = await this.refreshFetch('/api/v1/config')
474 .then(res => res.json())
475 }
476
469 const videoInfoPromise = videoResponse.json() 477 const videoInfoPromise = videoResponse.json()
470 .then((videoInfo: VideoDetails) => { 478 .then((videoInfo: VideoDetails) => {
471 if (!alreadyHadPlayer) this.loadPlaceholder(videoInfo) 479 if (!alreadyHadPlayer) this.loadPlaceholder(videoInfo)
@@ -473,15 +481,14 @@ export class PeerTubeEmbed {
473 return videoInfo 481 return videoInfo
474 }) 482 })
475 483
476 const [ videoInfoTmp, serverTranslations, captionsResponse, config, PeertubePlayerManagerModule ] = await Promise.all([ 484 const [ videoInfoTmp, serverTranslations, captionsResponse, PeertubePlayerManagerModule ] = await Promise.all([
477 videoInfoPromise, 485 videoInfoPromise,
478 this.translationsPromise, 486 this.translationsPromise,
479 captionsPromise, 487 captionsPromise,
480 this.configPromise,
481 this.PeertubePlayerManagerModulePromise 488 this.PeertubePlayerManagerModulePromise
482 ]) 489 ])
483 490
484 await this.ensurePluginsAreLoaded(config, serverTranslations) 491 await this.ensurePluginsAreLoaded(serverTranslations)
485 492
486 const videoInfo: VideoDetails = videoInfoTmp 493 const videoInfo: VideoDetails = videoInfoTmp
487 494
@@ -576,7 +583,7 @@ export class PeerTubeEmbed {
576 583
577 this.buildCSS() 584 this.buildCSS()
578 585
579 await this.buildDock(videoInfo, config) 586 await this.buildDock(videoInfo)
580 587
581 this.initializeApi() 588 this.initializeApi()
582 589
@@ -598,7 +605,6 @@ export class PeerTubeEmbed {
598 private async initCore () { 605 private async initCore () {
599 if (this.userTokens) this.setHeadersFromTokens() 606 if (this.userTokens) this.setHeadersFromTokens()
600 607
601 this.configPromise = this.loadConfig()
602 this.translationsPromise = TranslationsManager.getServerTranslations(window.location.origin, navigator.language) 608 this.translationsPromise = TranslationsManager.getServerTranslations(window.location.origin, navigator.language)
603 this.PeertubePlayerManagerModulePromise = import('../../assets/player/peertube-player-manager') 609 this.PeertubePlayerManagerModulePromise = import('../../assets/player/peertube-player-manager')
604 610
@@ -653,7 +659,7 @@ export class PeerTubeEmbed {
653 } 659 }
654 } 660 }
655 661
656 private async buildDock (videoInfo: VideoDetails, config: ServerConfig) { 662 private async buildDock (videoInfo: VideoDetails) {
657 if (!this.controls) return 663 if (!this.controls) return
658 664
659 // On webtorrent fallback, player may have been disposed 665 // On webtorrent fallback, player may have been disposed
@@ -661,7 +667,7 @@ export class PeerTubeEmbed {
661 667
662 const title = this.title ? videoInfo.name : undefined 668 const title = this.title ? videoInfo.name : undefined
663 669
664 const description = config.tracker.enabled && this.warningTitle 670 const description = this.config.tracker.enabled && this.warningTitle
665 ? '<span class="text">' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '</span>' 671 ? '<span class="text">' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '</span>'
666 : undefined 672 : undefined
667 673
@@ -733,10 +739,10 @@ export class PeerTubeEmbed {
733 return window.location.pathname.split('/')[1] === 'video-playlists' 739 return window.location.pathname.split('/')[1] === 'video-playlists'
734 } 740 }
735 741
736 private async ensurePluginsAreLoaded (config: ServerConfig, translations?: { [ id: string ]: string }) { 742 private async ensurePluginsAreLoaded (translations?: { [ id: string ]: string }) {
737 if (config.plugin.registered.length === 0) return 743 if (this.config.plugin.registered.length === 0) return
738 744
739 for (const plugin of config.plugin.registered) { 745 for (const plugin of this.config.plugin.registered) {
740 for (const key of Object.keys(plugin.clientScripts)) { 746 for (const key of Object.keys(plugin.clientScripts)) {
741 const clientScript = plugin.clientScripts[key] 747 const clientScript = plugin.clientScripts[key]
742 748
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 6f388420e..4cf7dcf0a 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -702,7 +702,8 @@ const CUSTOM_HTML_TAG_COMMENTS = {
702 TITLE: '<!-- title tag -->', 702 TITLE: '<!-- title tag -->',
703 DESCRIPTION: '<!-- description tag -->', 703 DESCRIPTION: '<!-- description tag -->',
704 CUSTOM_CSS: '<!-- custom css tag -->', 704 CUSTOM_CSS: '<!-- custom css tag -->',
705 META_TAGS: '<!-- meta tags -->' 705 META_TAGS: '<!-- meta tags -->',
706 SERVER_CONFIG: '<!-- server config -->'
706} 707}
707 708
708// --------------------------------------------------------------------------- 709// ---------------------------------------------------------------------------
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts
index 203bd3893..85fdc8754 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -2,12 +2,14 @@ import * as express from 'express'
2import { readFile } from 'fs-extra' 2import { readFile } from 'fs-extra'
3import { join } from 'path' 3import { join } from 'path'
4import validator from 'validator' 4import validator from 'validator'
5import { escapeHTML } from '@shared/core-utils/renderer'
6import { HTMLServerConfig } from '@shared/models'
5import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n' 7import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n'
6import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 8import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
7import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos' 9import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos'
8import { isTestInstance, sha256 } from '../helpers/core-utils' 10import { isTestInstance, sha256 } from '../helpers/core-utils'
9import { escapeHTML } from '@shared/core-utils/renderer'
10import { logger } from '../helpers/logger' 11import { logger } from '../helpers/logger'
12import { mdToPlainText } from '../helpers/markdown'
11import { CONFIG } from '../initializers/config' 13import { CONFIG } from '../initializers/config'
12import { 14import {
13 ACCEPT_HEADERS, 15 ACCEPT_HEADERS,
@@ -24,7 +26,7 @@ import { VideoChannelModel } from '../models/video/video-channel'
24import { getActivityStreamDuration } from '../models/video/video-format-utils' 26import { getActivityStreamDuration } from '../models/video/video-format-utils'
25import { VideoPlaylistModel } from '../models/video/video-playlist' 27import { VideoPlaylistModel } from '../models/video/video-playlist'
26import { MAccountActor, MChannelActor } from '../types/models' 28import { MAccountActor, MChannelActor } from '../types/models'
27import { mdToPlainText } from '../helpers/markdown' 29import { getHTMLServerConfig } from './config'
28 30
29type Tags = { 31type Tags = {
30 ogType: string 32 ogType: string
@@ -209,11 +211,14 @@ class ClientHtml {
209 if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] 211 if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path]
210 212
211 const buffer = await readFile(path) 213 const buffer = await readFile(path)
214 const serverConfig = await getHTMLServerConfig()
212 215
213 let html = buffer.toString() 216 let html = buffer.toString()
214 html = await ClientHtml.addAsyncPluginCSS(html) 217 html = await ClientHtml.addAsyncPluginCSS(html)
215 html = ClientHtml.addCustomCSS(html) 218 html = ClientHtml.addCustomCSS(html)
216 html = ClientHtml.addTitleTag(html) 219 html = ClientHtml.addTitleTag(html)
220 html = ClientHtml.addDescriptionTag(html)
221 html = ClientHtml.addServerConfig(html, serverConfig)
217 222
218 ClientHtml.htmlCache[path] = html 223 ClientHtml.htmlCache[path] = html
219 224
@@ -275,6 +280,7 @@ class ClientHtml {
275 if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] 280 if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path]
276 281
277 const buffer = await readFile(path) 282 const buffer = await readFile(path)
283 const serverConfig = await getHTMLServerConfig()
278 284
279 let html = buffer.toString() 285 let html = buffer.toString()
280 286
@@ -283,6 +289,7 @@ class ClientHtml {
283 html = ClientHtml.addFaviconContentHash(html) 289 html = ClientHtml.addFaviconContentHash(html)
284 html = ClientHtml.addLogoContentHash(html) 290 html = ClientHtml.addLogoContentHash(html)
285 html = ClientHtml.addCustomCSS(html) 291 html = ClientHtml.addCustomCSS(html)
292 html = ClientHtml.addServerConfig(html, serverConfig)
286 html = await ClientHtml.addAsyncPluginCSS(html) 293 html = await ClientHtml.addAsyncPluginCSS(html)
287 294
288 ClientHtml.htmlCache[path] = html 295 ClientHtml.htmlCache[path] = html
@@ -355,6 +362,13 @@ class ClientHtml {
355 return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag) 362 return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag)
356 } 363 }
357 364
365 private static addServerConfig (htmlStringPage: string, serverConfig: HTMLServerConfig) {
366 const serverConfigString = JSON.stringify(serverConfig)
367 const configScriptTag = `<script type="application/javascript">window.PeerTubeServerConfig = '${serverConfigString}'</script>`
368
369 return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.SERVER_CONFIG, configScriptTag)
370 }
371
358 private static async addAsyncPluginCSS (htmlStringPage: string) { 372 private static async addAsyncPluginCSS (htmlStringPage: string) {
359 const globalCSSContent = await readFile(PLUGIN_GLOBAL_CSS_PATH) 373 const globalCSSContent = await readFile(PLUGIN_GLOBAL_CSS_PATH)
360 if (globalCSSContent.byteLength === 0) return htmlStringPage 374 if (globalCSSContent.byteLength === 0) return htmlStringPage
diff --git a/server/lib/config.ts b/server/lib/config.ts
index fed468fe1..18d49f05a 100644
--- a/server/lib/config.ts
+++ b/server/lib/config.ts
@@ -2,17 +2,13 @@ import { isSignupAllowed, isSignupAllowedForCurrentIP } from '@server/helpers/si
2import { getServerCommit } from '@server/helpers/utils' 2import { getServerCommit } from '@server/helpers/utils'
3import { CONFIG, isEmailEnabled } from '@server/initializers/config' 3import { CONFIG, isEmailEnabled } from '@server/initializers/config'
4import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '@server/initializers/constants' 4import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '@server/initializers/constants'
5import { RegisteredExternalAuthConfig, RegisteredIdAndPassAuthConfig, ServerConfig } from '@shared/models' 5import { HTMLServerConfig, RegisteredExternalAuthConfig, RegisteredIdAndPassAuthConfig, ServerConfig } from '@shared/models'
6import { Hooks } from './plugins/hooks' 6import { Hooks } from './plugins/hooks'
7import { PluginManager } from './plugins/plugin-manager' 7import { PluginManager } from './plugins/plugin-manager'
8import { getThemeOrDefault } from './plugins/theme-utils' 8import { getThemeOrDefault } from './plugins/theme-utils'
9import { VideoTranscodingProfilesManager } from './transcoding/video-transcoding-profiles' 9import { VideoTranscodingProfilesManager } from './transcoding/video-transcoding-profiles'
10 10
11let serverCommit: string
12
13async function getServerConfig (ip?: string): Promise<ServerConfig> { 11async function getServerConfig (ip?: string): Promise<ServerConfig> {
14 if (serverCommit === undefined) serverCommit = await getServerCommit()
15
16 const { allowed } = await Hooks.wrapPromiseFun( 12 const { allowed } = await Hooks.wrapPromiseFun(
17 isSignupAllowed, 13 isSignupAllowed,
18 { 14 {
@@ -22,6 +18,23 @@ async function getServerConfig (ip?: string): Promise<ServerConfig> {
22 ) 18 )
23 19
24 const allowedForCurrentIP = isSignupAllowedForCurrentIP(ip) 20 const allowedForCurrentIP = isSignupAllowedForCurrentIP(ip)
21
22 const signup = {
23 allowed,
24 allowedForCurrentIP,
25 requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION
26 }
27
28 const htmlConfig = await getHTMLServerConfig()
29
30 return { ...htmlConfig, signup }
31}
32
33// Config injected in HTML
34let serverCommit: string
35async function getHTMLServerConfig (): Promise<HTMLServerConfig> {
36 if (serverCommit === undefined) serverCommit = await getServerCommit()
37
25 const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME) 38 const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME)
26 39
27 return { 40 return {
@@ -65,11 +78,6 @@ async function getServerConfig (ip?: string): Promise<ServerConfig> {
65 }, 78 },
66 serverVersion: PEERTUBE_VERSION, 79 serverVersion: PEERTUBE_VERSION,
67 serverCommit, 80 serverCommit,
68 signup: {
69 allowed,
70 allowedForCurrentIP,
71 requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION
72 },
73 transcoding: { 81 transcoding: {
74 hls: { 82 hls: {
75 enabled: CONFIG.TRANSCODING.HLS.ENABLED 83 enabled: CONFIG.TRANSCODING.HLS.ENABLED
@@ -223,7 +231,8 @@ export {
223 getServerConfig, 231 getServerConfig,
224 getRegisteredThemes, 232 getRegisteredThemes,
225 getEnabledResolutions, 233 getEnabledResolutions,
226 getRegisteredPlugins 234 getRegisteredPlugins,
235 getHTMLServerConfig
227} 236}
228 237
229// --------------------------------------------------------------------------- 238// ---------------------------------------------------------------------------
diff --git a/server/models/abuse/abuse.ts b/server/models/abuse/abuse.ts
index ffe109c2f..3518f5c02 100644
--- a/server/models/abuse/abuse.ts
+++ b/server/models/abuse/abuse.ts
@@ -16,8 +16,7 @@ import {
16 UpdatedAt 16 UpdatedAt
17} from 'sequelize-typescript' 17} from 'sequelize-typescript'
18import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses' 18import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses'
19import { AttributesOnly } from '@shared/core-utils' 19import { abusePredefinedReasonsMap, AttributesOnly } from '@shared/core-utils'
20import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
21import { 20import {
22 AbuseFilter, 21 AbuseFilter,
23 AbuseObject, 22 AbuseObject,
diff --git a/server/tests/client.ts b/server/tests/client.ts
index 3c99bcd1f..a385edd26 100644
--- a/server/tests/client.ts
+++ b/server/tests/client.ts
@@ -3,7 +3,7 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import * as request from 'supertest' 5import * as request from 'supertest'
6import { Account, VideoPlaylistPrivacy } from '@shared/models' 6import { Account, HTMLServerConfig, ServerConfig, VideoPlaylistPrivacy } from '@shared/models'
7import { 7import {
8 addVideoInPlaylist, 8 addVideoInPlaylist,
9 cleanupTests, 9 cleanupTests,
@@ -11,6 +11,7 @@ import {
11 doubleFollow, 11 doubleFollow,
12 flushAndRunMultipleServers, 12 flushAndRunMultipleServers,
13 getAccount, 13 getAccount,
14 getConfig,
14 getCustomConfig, 15 getCustomConfig,
15 getVideosList, 16 getVideosList,
16 makeHTMLRequest, 17 makeHTMLRequest,
@@ -25,13 +26,17 @@ import {
25 waitJobs 26 waitJobs
26} from '../../shared/extra-utils' 27} from '../../shared/extra-utils'
27import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 28import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
29import { omit } from 'lodash'
28 30
29const expect = chai.expect 31const expect = chai.expect
30 32
31function checkIndexTags (html: string, title: string, description: string, css: string) { 33function checkIndexTags (html: string, title: string, description: string, css: string, config: ServerConfig) {
32 expect(html).to.contain('<title>' + title + '</title>') 34 expect(html).to.contain('<title>' + title + '</title>')
33 expect(html).to.contain('<meta name="description" content="' + description + '" />') 35 expect(html).to.contain('<meta name="description" content="' + description + '" />')
34 expect(html).to.contain('<style class="custom-css-style">' + css + '</style>') 36 expect(html).to.contain('<style class="custom-css-style">' + css + '</style>')
37
38 const htmlConfig: HTMLServerConfig = omit(config, 'signup')
39 expect(html).to.contain(`<script type="application/javascript">window.PeerTubeServerConfig = '${JSON.stringify(htmlConfig)}'</script>`)
35} 40}
36 41
37describe('Test a client controllers', function () { 42describe('Test a client controllers', function () {
@@ -296,10 +301,11 @@ describe('Test a client controllers', function () {
296 describe('Index HTML', function () { 301 describe('Index HTML', function () {
297 302
298 it('Should have valid index html tags (title, description...)', async function () { 303 it('Should have valid index html tags (title, description...)', async function () {
304 const resConfig = await getConfig(servers[0].url)
299 const res = await makeHTMLRequest(servers[0].url, '/videos/trending') 305 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
300 306
301 const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.' 307 const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
302 checkIndexTags(res.text, 'PeerTube', description, '') 308 checkIndexTags(res.text, 'PeerTube', description, '', resConfig.body)
303 }) 309 })
304 310
305 it('Should update the customized configuration and have the correct index html tags', async function () { 311 it('Should update the customized configuration and have the correct index html tags', async function () {
@@ -318,15 +324,17 @@ describe('Test a client controllers', function () {
318 } 324 }
319 }) 325 })
320 326
327 const resConfig = await getConfig(servers[0].url)
321 const res = await makeHTMLRequest(servers[0].url, '/videos/trending') 328 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
322 329
323 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }') 330 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body)
324 }) 331 })
325 332
326 it('Should have valid index html updated tags (title, description...)', async function () { 333 it('Should have valid index html updated tags (title, description...)', async function () {
334 const resConfig = await getConfig(servers[0].url)
327 const res = await makeHTMLRequest(servers[0].url, '/videos/trending') 335 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
328 336
329 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }') 337 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body)
330 }) 338 })
331 339
332 it('Should use the original video URL for the canonical tag', async function () { 340 it('Should use the original video URL for the canonical tag', async function () {
@@ -350,6 +358,16 @@ describe('Test a client controllers', function () {
350 }) 358 })
351 }) 359 })
352 360
361 describe('Embed HTML', function () {
362
363 it('Should have the correct embed html tags', async function () {
364 const resConfig = await getConfig(servers[0].url)
365 const res = await makeHTMLRequest(servers[0].url, servers[0].video.embedPath)
366
367 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body)
368 })
369 })
370
353 after(async function () { 371 after(async function () {
354 await cleanupTests(servers) 372 await cleanupTests(servers)
355 }) 373 })
diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts
index 479f08e12..d04757470 100644
--- a/shared/extra-utils/server/servers.ts
+++ b/shared/extra-utils/server/servers.ts
@@ -45,9 +45,12 @@ interface ServerInfo {
45 uuid: string 45 uuid: string
46 name?: string 46 name?: string
47 url?: string 47 url?: string
48
48 account?: { 49 account?: {
49 name: string 50 name: string
50 } 51 }
52
53 embedPath?: string
51 } 54 }
52 55
53 remoteVideo?: { 56 remoteVideo?: {
diff --git a/shared/models/server/server-config.model.ts b/shared/models/server/server-config.model.ts
index 85d84af44..2c5026b30 100644
--- a/shared/models/server/server-config.model.ts
+++ b/shared/models/server/server-config.model.ts
@@ -215,3 +215,5 @@ export interface ServerConfig {
215 dismissable: boolean 215 dismissable: boolean
216 } 216 }
217} 217}
218
219export type HTMLServerConfig = Omit<ServerConfig, 'signup'>