server-config-manager.ts 13 KB

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