config.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. import { IConfig } from 'config'
  2. import { dirname, join } from 'path'
  3. import { VideosRedundancy } from '../../shared/models'
  4. // Do not use barrels, remain constants as independent as possible
  5. import { buildPath, parseBytes, parseDurationToMs, root } from '../helpers/core-utils'
  6. import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
  7. import * as bytes from 'bytes'
  8. // Use a variable to reload the configuration if we need
  9. let config: IConfig = require('config')
  10. const configChangedHandlers: Function[] = []
  11. const CONFIG = {
  12. CUSTOM_FILE: getLocalConfigFilePath(),
  13. LISTEN: {
  14. PORT: config.get<number>('listen.port'),
  15. HOSTNAME: config.get<string>('listen.hostname')
  16. },
  17. DATABASE: {
  18. DBNAME: 'peertube' + config.get<string>('database.suffix'),
  19. HOSTNAME: config.get<string>('database.hostname'),
  20. PORT: config.get<number>('database.port'),
  21. USERNAME: config.get<string>('database.username'),
  22. PASSWORD: config.get<string>('database.password'),
  23. POOL: {
  24. MAX: config.get<number>('database.pool.max')
  25. }
  26. },
  27. REDIS: {
  28. HOSTNAME: config.has('redis.hostname') ? config.get<string>('redis.hostname') : null,
  29. PORT: config.has('redis.port') ? config.get<number>('redis.port') : null,
  30. SOCKET: config.has('redis.socket') ? config.get<string>('redis.socket') : null,
  31. AUTH: config.has('redis.auth') ? config.get<string>('redis.auth') : null,
  32. DB: config.has('redis.db') ? config.get<number>('redis.db') : null
  33. },
  34. SMTP: {
  35. HOSTNAME: config.get<string>('smtp.hostname'),
  36. PORT: config.get<number>('smtp.port'),
  37. USERNAME: config.get<string>('smtp.username'),
  38. PASSWORD: config.get<string>('smtp.password'),
  39. TLS: config.get<boolean>('smtp.tls'),
  40. DISABLE_STARTTLS: config.get<boolean>('smtp.disable_starttls'),
  41. CA_FILE: config.get<string>('smtp.ca_file'),
  42. FROM_ADDRESS: config.get<string>('smtp.from_address')
  43. },
  44. EMAIL: {
  45. BODY: {
  46. SIGNATURE: config.get<string>('email.body.signature')
  47. },
  48. SUBJECT: {
  49. PREFIX: config.get<string>('email.subject.prefix') + ' '
  50. }
  51. },
  52. STORAGE: {
  53. TMP_DIR: buildPath(config.get<string>('storage.tmp')),
  54. AVATARS_DIR: buildPath(config.get<string>('storage.avatars')),
  55. LOG_DIR: buildPath(config.get<string>('storage.logs')),
  56. VIDEOS_DIR: buildPath(config.get<string>('storage.videos')),
  57. STREAMING_PLAYLISTS_DIR: buildPath(config.get<string>('storage.streaming_playlists')),
  58. REDUNDANCY_DIR: buildPath(config.get<string>('storage.redundancy')),
  59. THUMBNAILS_DIR: buildPath(config.get<string>('storage.thumbnails')),
  60. PREVIEWS_DIR: buildPath(config.get<string>('storage.previews')),
  61. CAPTIONS_DIR: buildPath(config.get<string>('storage.captions')),
  62. TORRENTS_DIR: buildPath(config.get<string>('storage.torrents')),
  63. CACHE_DIR: buildPath(config.get<string>('storage.cache')),
  64. PLUGINS_DIR: buildPath(config.get<string>('storage.plugins'))
  65. },
  66. WEBSERVER: {
  67. SCHEME: config.get<boolean>('webserver.https') === true ? 'https' : 'http',
  68. WS: config.get<boolean>('webserver.https') === true ? 'wss' : 'ws',
  69. HOSTNAME: config.get<string>('webserver.hostname'),
  70. PORT: config.get<number>('webserver.port')
  71. },
  72. RATES_LIMIT: {
  73. API: {
  74. WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.api.window')),
  75. MAX: config.get<number>('rates_limit.api.max')
  76. },
  77. SIGNUP: {
  78. WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.signup.window')),
  79. MAX: config.get<number>('rates_limit.signup.max')
  80. },
  81. LOGIN: {
  82. WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.login.window')),
  83. MAX: config.get<number>('rates_limit.login.max')
  84. },
  85. ASK_SEND_EMAIL: {
  86. WINDOW_MS: parseDurationToMs(config.get<string>('rates_limit.ask_send_email.window')),
  87. MAX: config.get<number>('rates_limit.ask_send_email.max')
  88. }
  89. },
  90. TRUST_PROXY: config.get<string[]>('trust_proxy'),
  91. LOG: {
  92. LEVEL: config.get<string>('log.level'),
  93. ROTATION: config.get<boolean>('log.rotation.enabled')
  94. },
  95. SEARCH: {
  96. REMOTE_URI: {
  97. USERS: config.get<boolean>('search.remote_uri.users'),
  98. ANONYMOUS: config.get<boolean>('search.remote_uri.anonymous')
  99. }
  100. },
  101. TRENDING: {
  102. VIDEOS: {
  103. INTERVAL_DAYS: config.get<number>('trending.videos.interval_days')
  104. }
  105. },
  106. REDUNDANCY: {
  107. VIDEOS: {
  108. CHECK_INTERVAL: parseDurationToMs(config.get<string>('redundancy.videos.check_interval')),
  109. STRATEGIES: buildVideosRedundancy(config.get<any[]>('redundancy.videos.strategies'))
  110. }
  111. },
  112. CSP: {
  113. ENABLED: config.get<boolean>('csp.enabled'),
  114. REPORT_ONLY: config.get<boolean>('csp.report_only'),
  115. REPORT_URI: config.get<boolean>('csp.report_uri')
  116. },
  117. TRACKER: {
  118. ENABLED: config.get<boolean>('tracker.enabled'),
  119. PRIVATE: config.get<boolean>('tracker.private'),
  120. REJECT_TOO_MANY_ANNOUNCES: config.get<boolean>('tracker.reject_too_many_announces')
  121. },
  122. HISTORY: {
  123. VIDEOS: {
  124. MAX_AGE: parseDurationToMs(config.get('history.videos.max_age'))
  125. }
  126. },
  127. VIEWS: {
  128. VIDEOS: {
  129. REMOTE: {
  130. MAX_AGE: parseDurationToMs(config.get('views.videos.remote.max_age'))
  131. }
  132. }
  133. },
  134. PLUGINS: {
  135. INDEX: {
  136. ENABLED: config.get<boolean>('plugins.index.enabled'),
  137. CHECK_LATEST_VERSIONS_INTERVAL: parseDurationToMs(config.get<string>('plugins.index.check_latest_versions_interval')),
  138. URL: config.get<string>('plugins.index.url')
  139. }
  140. },
  141. ADMIN: {
  142. get EMAIL () { return config.get<string>('admin.email') }
  143. },
  144. CONTACT_FORM: {
  145. get ENABLED () { return config.get<boolean>('contact_form.enabled') }
  146. },
  147. SIGNUP: {
  148. get ENABLED () { return config.get<boolean>('signup.enabled') },
  149. get LIMIT () { return config.get<number>('signup.limit') },
  150. get REQUIRES_EMAIL_VERIFICATION () { return config.get<boolean>('signup.requires_email_verification') },
  151. FILTERS: {
  152. CIDR: {
  153. get WHITELIST () { return config.get<string[]>('signup.filters.cidr.whitelist') },
  154. get BLACKLIST () { return config.get<string[]>('signup.filters.cidr.blacklist') }
  155. }
  156. }
  157. },
  158. USER: {
  159. get VIDEO_QUOTA () { return parseBytes(config.get<number>('user.video_quota')) },
  160. get VIDEO_QUOTA_DAILY () { return parseBytes(config.get<number>('user.video_quota_daily')) }
  161. },
  162. TRANSCODING: {
  163. get ENABLED () { return config.get<boolean>('transcoding.enabled') },
  164. get ALLOW_ADDITIONAL_EXTENSIONS () { return config.get<boolean>('transcoding.allow_additional_extensions') },
  165. get ALLOW_AUDIO_FILES () { return config.get<boolean>('transcoding.allow_audio_files') },
  166. get THREADS () { return config.get<number>('transcoding.threads') },
  167. RESOLUTIONS: {
  168. get '240p' () { return config.get<boolean>('transcoding.resolutions.240p') },
  169. get '360p' () { return config.get<boolean>('transcoding.resolutions.360p') },
  170. get '480p' () { return config.get<boolean>('transcoding.resolutions.480p') },
  171. get '720p' () { return config.get<boolean>('transcoding.resolutions.720p') },
  172. get '1080p' () { return config.get<boolean>('transcoding.resolutions.1080p') },
  173. get '2160p' () { return config.get<boolean>('transcoding.resolutions.2160p') }
  174. },
  175. HLS: {
  176. get ENABLED () { return config.get<boolean>('transcoding.hls.enabled') }
  177. },
  178. WEBTORRENT: {
  179. get ENABLED () { return config.get<boolean>('transcoding.webtorrent.enabled') }
  180. }
  181. },
  182. IMPORT: {
  183. VIDEOS: {
  184. HTTP: {
  185. get ENABLED () { return config.get<boolean>('import.videos.http.enabled') }
  186. },
  187. TORRENT: {
  188. get ENABLED () { return config.get<boolean>('import.videos.torrent.enabled') }
  189. }
  190. }
  191. },
  192. AUTO_BLACKLIST: {
  193. VIDEOS: {
  194. OF_USERS: {
  195. get ENABLED () { return config.get<boolean>('auto_blacklist.videos.of_users.enabled') }
  196. }
  197. }
  198. },
  199. CACHE: {
  200. PREVIEWS: {
  201. get SIZE () { return config.get<number>('cache.previews.size') }
  202. },
  203. VIDEO_CAPTIONS: {
  204. get SIZE () { return config.get<number>('cache.captions.size') }
  205. }
  206. },
  207. INSTANCE: {
  208. get NAME () { return config.get<string>('instance.name') },
  209. get SHORT_DESCRIPTION () { return config.get<string>('instance.short_description') },
  210. get DESCRIPTION () { return config.get<string>('instance.description') },
  211. get TERMS () { return config.get<string>('instance.terms') },
  212. get CODE_OF_CONDUCT () { return config.get<string>('instance.code_of_conduct') },
  213. get CREATION_REASON () { return config.get<string>('instance.creation_reason') },
  214. get MODERATION_INFORMATION () { return config.get<string>('instance.moderation_information') },
  215. get ADMINISTRATOR () { return config.get<string>('instance.administrator') },
  216. get MAINTENANCE_LIFETIME () { return config.get<string>('instance.maintenance_lifetime') },
  217. get BUSINESS_MODEL () { return config.get<string>('instance.business_model') },
  218. get HARDWARE_INFORMATION () { return config.get<string>('instance.hardware_information') },
  219. get LANGUAGES () { return config.get<string[]>('instance.languages') || [] },
  220. get CATEGORIES () { return config.get<number[]>('instance.categories') || [] },
  221. get IS_NSFW () { return config.get<boolean>('instance.is_nsfw') },
  222. get DEFAULT_CLIENT_ROUTE () { return config.get<string>('instance.default_client_route') },
  223. get DEFAULT_NSFW_POLICY () { return config.get<NSFWPolicyType>('instance.default_nsfw_policy') },
  224. CUSTOMIZATIONS: {
  225. get JAVASCRIPT () { return config.get<string>('instance.customizations.javascript') },
  226. get CSS () { return config.get<string>('instance.customizations.css') }
  227. },
  228. get ROBOTS () { return config.get<string>('instance.robots') },
  229. get SECURITYTXT () { return config.get<string>('instance.securitytxt') },
  230. get SECURITYTXT_CONTACT () { return config.get<string>('admin.email') }
  231. },
  232. SERVICES: {
  233. TWITTER: {
  234. get USERNAME () { return config.get<string>('services.twitter.username') },
  235. get WHITELISTED () { return config.get<boolean>('services.twitter.whitelisted') }
  236. }
  237. },
  238. FOLLOWERS: {
  239. INSTANCE: {
  240. get ENABLED () { return config.get<boolean>('followers.instance.enabled') },
  241. get MANUAL_APPROVAL () { return config.get<boolean>('followers.instance.manual_approval') }
  242. }
  243. },
  244. FOLLOWINGS: {
  245. INSTANCE: {
  246. AUTO_FOLLOW_BACK: {
  247. get ENABLED () {
  248. return config.get<boolean>('followings.instance.auto_follow_back.enabled')
  249. }
  250. },
  251. AUTO_FOLLOW_INDEX: {
  252. get ENABLED () {
  253. return config.get<boolean>('followings.instance.auto_follow_index.enabled')
  254. },
  255. get INDEX_URL () {
  256. return config.get<string>('followings.instance.auto_follow_index.index_url')
  257. }
  258. }
  259. }
  260. },
  261. THEME: {
  262. get DEFAULT () { return config.get<string>('theme.default') }
  263. }
  264. }
  265. function registerConfigChangedHandler (fun: Function) {
  266. configChangedHandlers.push(fun)
  267. }
  268. // ---------------------------------------------------------------------------
  269. export {
  270. CONFIG,
  271. registerConfigChangedHandler
  272. }
  273. // ---------------------------------------------------------------------------
  274. function getLocalConfigFilePath () {
  275. const configSources = config.util.getConfigSources()
  276. if (configSources.length === 0) throw new Error('Invalid config source.')
  277. let filename = 'local'
  278. if (process.env.NODE_ENV) filename += `-${process.env.NODE_ENV}`
  279. if (process.env.NODE_APP_INSTANCE) filename += `-${process.env.NODE_APP_INSTANCE}`
  280. return join(dirname(configSources[ 0 ].name), filename + '.json')
  281. }
  282. function buildVideosRedundancy (objs: any[]): VideosRedundancy[] {
  283. if (!objs) return []
  284. if (!Array.isArray(objs)) return objs
  285. return objs.map(obj => {
  286. return Object.assign({}, obj, {
  287. minLifetime: parseDurationToMs(obj.min_lifetime),
  288. size: bytes.parse(obj.size),
  289. minViews: obj.min_views
  290. })
  291. })
  292. }
  293. export function reloadConfig () {
  294. function directory () {
  295. if (process.env.NODE_CONFIG_DIR) {
  296. return process.env.NODE_CONFIG_DIR
  297. }
  298. return join(root(), 'config')
  299. }
  300. function purge () {
  301. for (const fileName in require.cache) {
  302. if (-1 === fileName.indexOf(directory())) {
  303. continue
  304. }
  305. delete require.cache[fileName]
  306. }
  307. delete require.cache[require.resolve('config')]
  308. }
  309. purge()
  310. config = require('config')
  311. for (const configChangedHandler of configChangedHandlers) {
  312. configChangedHandler()
  313. }
  314. }