]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/lib/server-config-manager.ts
744186cfccc4f0b741c14798a9d2fa457efe1072
[github/Chocobozzz/PeerTube.git] / server / lib / server-config-manager.ts
1 import { getServerCommit } from '@server/helpers/utils'
2 import { CONFIG, isEmailEnabled } from '@server/initializers/config'
3 import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '@server/initializers/constants'
4 import { isSignupAllowed, isSignupAllowedForCurrentIP } from '@server/lib/signup'
5 import { ActorCustomPageModel } from '@server/models/account/actor-custom-page'
6 import { PluginModel } from '@server/models/server/plugin'
7 import { HTMLServerConfig, RegisteredExternalAuthConfig, RegisteredIdAndPassAuthConfig, ServerConfig } from '@shared/models'
8 import { Hooks } from './plugins/hooks'
9 import { PluginManager } from './plugins/plugin-manager'
10 import { getThemeOrDefault } from './plugins/theme-utils'
11 import { VideoTranscodingProfilesManager } from './transcoding/default-transcoding-profiles'
12
13 /**
14 *
15 * Used to send the server config to clients (using REST/API or plugins API)
16 * We need a singleton class to manage config state depending on external events (to build menu entries etc)
17 *
18 */
19
20 class ServerConfigManager {
21
22 private static instance: ServerConfigManager
23
24 private serverCommit: string
25
26 private homepageEnabled = false
27
28 private constructor () {}
29
30 async init () {
31 const instanceHomepage = await ActorCustomPageModel.loadInstanceHomepage()
32
33 this.updateHomepageState(instanceHomepage?.content)
34 }
35
36 updateHomepageState (content: string) {
37 this.homepageEnabled = !!content
38 }
39
40 async getHTMLServerConfig (): Promise<HTMLServerConfig> {
41 if (this.serverCommit === undefined) this.serverCommit = await getServerCommit()
42
43 const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME)
44
45 return {
46 client: {
47 videos: {
48 miniature: {
49 displayAuthorAvatar: CONFIG.CLIENT.VIDEOS.MINIATURE.DISPLAY_AUTHOR_AVATAR,
50 preferAuthorDisplayName: CONFIG.CLIENT.VIDEOS.MINIATURE.PREFER_AUTHOR_DISPLAY_NAME
51 }
52 },
53 menu: {
54 login: {
55 redirectOnSingleExternalAuth: CONFIG.CLIENT.MENU.LOGIN.REDIRECT_ON_SINGLE_EXTERNAL_AUTH
56 }
57 }
58 },
59
60 defaults: {
61 publish: {
62 downloadEnabled: CONFIG.DEFAULTS.PUBLISH.DOWNLOAD_ENABLED,
63 commentsEnabled: CONFIG.DEFAULTS.PUBLISH.COMMENTS_ENABLED,
64 privacy: CONFIG.DEFAULTS.PUBLISH.PRIVACY,
65 licence: CONFIG.DEFAULTS.PUBLISH.LICENCE
66 },
67 p2p: {
68 webapp: {
69 enabled: CONFIG.DEFAULTS.P2P.WEBAPP.ENABLED
70 },
71 embed: {
72 enabled: CONFIG.DEFAULTS.P2P.EMBED.ENABLED
73 }
74 }
75 },
76
77 webadmin: {
78 configuration: {
79 edition: {
80 allowed: CONFIG.WEBADMIN.CONFIGURATION.EDITION.ALLOWED
81 }
82 }
83 },
84
85 instance: {
86 name: CONFIG.INSTANCE.NAME,
87 shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION,
88 isNSFW: CONFIG.INSTANCE.IS_NSFW,
89 defaultNSFWPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
90 defaultClientRoute: CONFIG.INSTANCE.DEFAULT_CLIENT_ROUTE,
91 customizations: {
92 javascript: CONFIG.INSTANCE.CUSTOMIZATIONS.JAVASCRIPT,
93 css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS
94 }
95 },
96 search: {
97 remoteUri: {
98 users: CONFIG.SEARCH.REMOTE_URI.USERS,
99 anonymous: CONFIG.SEARCH.REMOTE_URI.ANONYMOUS
100 },
101 searchIndex: {
102 enabled: CONFIG.SEARCH.SEARCH_INDEX.ENABLED,
103 url: CONFIG.SEARCH.SEARCH_INDEX.URL,
104 disableLocalSearch: CONFIG.SEARCH.SEARCH_INDEX.DISABLE_LOCAL_SEARCH,
105 isDefaultSearch: CONFIG.SEARCH.SEARCH_INDEX.IS_DEFAULT_SEARCH
106 }
107 },
108 plugin: {
109 registered: this.getRegisteredPlugins(),
110 registeredExternalAuths: this.getExternalAuthsPlugins(),
111 registeredIdAndPassAuths: this.getIdAndPassAuthPlugins()
112 },
113 theme: {
114 registered: this.getRegisteredThemes(),
115 default: defaultTheme
116 },
117 email: {
118 enabled: isEmailEnabled()
119 },
120 contactForm: {
121 enabled: CONFIG.CONTACT_FORM.ENABLED
122 },
123 serverVersion: PEERTUBE_VERSION,
124 serverCommit: this.serverCommit,
125 transcoding: {
126 hls: {
127 enabled: CONFIG.TRANSCODING.HLS.ENABLED
128 },
129 webtorrent: {
130 enabled: CONFIG.TRANSCODING.WEBTORRENT.ENABLED
131 },
132 enabledResolutions: this.getEnabledResolutions('vod'),
133 profile: CONFIG.TRANSCODING.PROFILE,
134 availableProfiles: VideoTranscodingProfilesManager.Instance.getAvailableProfiles('vod')
135 },
136 live: {
137 enabled: CONFIG.LIVE.ENABLED,
138
139 allowReplay: CONFIG.LIVE.ALLOW_REPLAY,
140 latencySetting: {
141 enabled: CONFIG.LIVE.LATENCY_SETTING.ENABLED
142 },
143
144 maxDuration: CONFIG.LIVE.MAX_DURATION,
145 maxInstanceLives: CONFIG.LIVE.MAX_INSTANCE_LIVES,
146 maxUserLives: CONFIG.LIVE.MAX_USER_LIVES,
147
148 transcoding: {
149 enabled: CONFIG.LIVE.TRANSCODING.ENABLED,
150 enabledResolutions: this.getEnabledResolutions('live'),
151 profile: CONFIG.LIVE.TRANSCODING.PROFILE,
152 availableProfiles: VideoTranscodingProfilesManager.Instance.getAvailableProfiles('live')
153 },
154
155 rtmp: {
156 port: CONFIG.LIVE.RTMP.PORT
157 }
158 },
159 videoEditor: {
160 enabled: CONFIG.VIDEO_EDITOR.ENABLED
161 },
162 import: {
163 videos: {
164 http: {
165 enabled: CONFIG.IMPORT.VIDEOS.HTTP.ENABLED
166 },
167 torrent: {
168 enabled: CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED
169 }
170 }
171 },
172 autoBlacklist: {
173 videos: {
174 ofUsers: {
175 enabled: CONFIG.AUTO_BLACKLIST.VIDEOS.OF_USERS.ENABLED
176 }
177 }
178 },
179 avatar: {
180 file: {
181 size: {
182 max: CONSTRAINTS_FIELDS.ACTORS.IMAGE.FILE_SIZE.max
183 },
184 extensions: CONSTRAINTS_FIELDS.ACTORS.IMAGE.EXTNAME
185 }
186 },
187 banner: {
188 file: {
189 size: {
190 max: CONSTRAINTS_FIELDS.ACTORS.IMAGE.FILE_SIZE.max
191 },
192 extensions: CONSTRAINTS_FIELDS.ACTORS.IMAGE.EXTNAME
193 }
194 },
195 video: {
196 image: {
197 extensions: CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME,
198 size: {
199 max: CONSTRAINTS_FIELDS.VIDEOS.IMAGE.FILE_SIZE.max
200 }
201 },
202 file: {
203 extensions: CONSTRAINTS_FIELDS.VIDEOS.EXTNAME
204 }
205 },
206 videoCaption: {
207 file: {
208 size: {
209 max: CONSTRAINTS_FIELDS.VIDEO_CAPTIONS.CAPTION_FILE.FILE_SIZE.max
210 },
211 extensions: CONSTRAINTS_FIELDS.VIDEO_CAPTIONS.CAPTION_FILE.EXTNAME
212 }
213 },
214 user: {
215 videoQuota: CONFIG.USER.VIDEO_QUOTA,
216 videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY
217 },
218 videoChannels: {
219 maxPerUser: CONFIG.VIDEO_CHANNELS.MAX_PER_USER
220 },
221 trending: {
222 videos: {
223 intervalDays: CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS,
224 algorithms: {
225 enabled: CONFIG.TRENDING.VIDEOS.ALGORITHMS.ENABLED,
226 default: CONFIG.TRENDING.VIDEOS.ALGORITHMS.DEFAULT
227 }
228 }
229 },
230 tracker: {
231 enabled: CONFIG.TRACKER.ENABLED
232 },
233
234 followings: {
235 instance: {
236 autoFollowIndex: {
237 indexUrl: CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_INDEX.INDEX_URL
238 }
239 }
240 },
241
242 broadcastMessage: {
243 enabled: CONFIG.BROADCAST_MESSAGE.ENABLED,
244 message: CONFIG.BROADCAST_MESSAGE.MESSAGE,
245 level: CONFIG.BROADCAST_MESSAGE.LEVEL,
246 dismissable: CONFIG.BROADCAST_MESSAGE.DISMISSABLE
247 },
248
249 homepage: {
250 enabled: this.homepageEnabled
251 }
252 }
253 }
254
255 async getServerConfig (ip?: string): Promise<ServerConfig> {
256 const { allowed } = await Hooks.wrapPromiseFun(
257 isSignupAllowed,
258 {
259 ip
260 },
261 'filter:api.user.signup.allowed.result'
262 )
263
264 const allowedForCurrentIP = isSignupAllowedForCurrentIP(ip)
265
266 const signup = {
267 allowed,
268 allowedForCurrentIP,
269 minimumAge: CONFIG.SIGNUP.MINIMUM_AGE,
270 requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION
271 }
272
273 const htmlConfig = await this.getHTMLServerConfig()
274
275 return { ...htmlConfig, signup }
276 }
277
278 getRegisteredThemes () {
279 return PluginManager.Instance.getRegisteredThemes()
280 .map(t => ({
281 npmName: PluginModel.buildNpmName(t.name, t.type),
282 name: t.name,
283 version: t.version,
284 description: t.description,
285 css: t.css,
286 clientScripts: t.clientScripts
287 }))
288 }
289
290 getRegisteredPlugins () {
291 return PluginManager.Instance.getRegisteredPlugins()
292 .map(p => ({
293 npmName: PluginModel.buildNpmName(p.name, p.type),
294 name: p.name,
295 version: p.version,
296 description: p.description,
297 clientScripts: p.clientScripts
298 }))
299 }
300
301 getEnabledResolutions (type: 'vod' | 'live') {
302 const transcoding = type === 'vod'
303 ? CONFIG.TRANSCODING
304 : CONFIG.LIVE.TRANSCODING
305
306 return Object.keys(transcoding.RESOLUTIONS)
307 .filter(key => transcoding.ENABLED && transcoding.RESOLUTIONS[key] === true)
308 .map(r => parseInt(r, 10))
309 }
310
311 private getIdAndPassAuthPlugins () {
312 const result: RegisteredIdAndPassAuthConfig[] = []
313
314 for (const p of PluginManager.Instance.getIdAndPassAuths()) {
315 for (const auth of p.idAndPassAuths) {
316 result.push({
317 npmName: p.npmName,
318 name: p.name,
319 version: p.version,
320 authName: auth.authName,
321 weight: auth.getWeight()
322 })
323 }
324 }
325
326 return result
327 }
328
329 private getExternalAuthsPlugins () {
330 const result: RegisteredExternalAuthConfig[] = []
331
332 for (const p of PluginManager.Instance.getExternalAuths()) {
333 for (const auth of p.externalAuths) {
334 result.push({
335 npmName: p.npmName,
336 name: p.name,
337 version: p.version,
338 authName: auth.authName,
339 authDisplayName: auth.authDisplayName()
340 })
341 }
342 }
343
344 return result
345 }
346
347 static get Instance () {
348 return this.instance || (this.instance = new this())
349 }
350 }
351
352 // ---------------------------------------------------------------------------
353
354 export {
355 ServerConfigManager
356 }