aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/initializers
diff options
context:
space:
mode:
Diffstat (limited to 'server/initializers')
-rw-r--r--server/initializers/checker-after-init.ts5
-rw-r--r--server/initializers/config.ts255
-rw-r--r--server/initializers/constants.ts321
-rw-r--r--server/initializers/database.ts2
-rw-r--r--server/initializers/installer.ts5
-rw-r--r--server/initializers/migrations/0075-video-resolutions.ts2
-rw-r--r--server/initializers/migrations/0140-actor-url.ts6
-rw-r--r--server/initializers/migrations/0235-delete-some-video-indexes.ts4
-rw-r--r--server/initializers/migrations/0240-drop-old-indexes.ts4
-rw-r--r--server/initializers/migrations/0345-video-playlists.ts4
10 files changed, 319 insertions, 289 deletions
diff --git a/server/initializers/checker-after-init.ts b/server/initializers/checker-after-init.ts
index 85f752284..db3115085 100644
--- a/server/initializers/checker-after-init.ts
+++ b/server/initializers/checker-after-init.ts
@@ -4,19 +4,20 @@ import { UserModel } from '../models/account/user'
4import { ApplicationModel } from '../models/application/application' 4import { ApplicationModel } from '../models/application/application'
5import { OAuthClientModel } from '../models/oauth/oauth-client' 5import { OAuthClientModel } from '../models/oauth/oauth-client'
6import { parse } from 'url' 6import { parse } from 'url'
7import { CONFIG } from './constants' 7import { CONFIG } from './config'
8import { logger } from '../helpers/logger' 8import { logger } from '../helpers/logger'
9import { getServerActor } from '../helpers/utils' 9import { getServerActor } from '../helpers/utils'
10import { RecentlyAddedStrategy } from '../../shared/models/redundancy' 10import { RecentlyAddedStrategy } from '../../shared/models/redundancy'
11import { isArray } from '../helpers/custom-validators/misc' 11import { isArray } from '../helpers/custom-validators/misc'
12import { uniq } from 'lodash' 12import { uniq } from 'lodash'
13import { Emailer } from '../lib/emailer' 13import { Emailer } from '../lib/emailer'
14import { WEBSERVER } from './constants'
14 15
15async function checkActivityPubUrls () { 16async function checkActivityPubUrls () {
16 const actor = await getServerActor() 17 const actor = await getServerActor()
17 18
18 const parsed = parse(actor.url) 19 const parsed = parse(actor.url)
19 if (CONFIG.WEBSERVER.HOST !== parsed.host) { 20 if (WEBSERVER.HOST !== parsed.host) {
20 const NODE_ENV = config.util.getEnv('NODE_ENV') 21 const NODE_ENV = config.util.getEnv('NODE_ENV')
21 const NODE_CONFIG_DIR = config.util.getEnv('NODE_CONFIG_DIR') 22 const NODE_CONFIG_DIR = config.util.getEnv('NODE_CONFIG_DIR')
22 23
diff --git a/server/initializers/config.ts b/server/initializers/config.ts
new file mode 100644
index 000000000..8dd62cba8
--- /dev/null
+++ b/server/initializers/config.ts
@@ -0,0 +1,255 @@
1import { IConfig } from 'config'
2import { dirname, join } from 'path'
3import { VideosRedundancy } from '../../shared/models'
4// Do not use barrels, remain constants as independent as possible
5import { buildPath, parseBytes, parseDuration, root } from '../helpers/core-utils'
6import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
7import * as bytes from 'bytes'
8
9// Use a variable to reload the configuration if we need
10let config: IConfig = require('config')
11
12const configChangedHandlers: Function[] = []
13
14const CONFIG = {
15 CUSTOM_FILE: getLocalConfigFilePath(),
16 LISTEN: {
17 PORT: config.get<number>('listen.port'),
18 HOSTNAME: config.get<string>('listen.hostname')
19 },
20 DATABASE: {
21 DBNAME: 'peertube' + config.get<string>('database.suffix'),
22 HOSTNAME: config.get<string>('database.hostname'),
23 PORT: config.get<number>('database.port'),
24 USERNAME: config.get<string>('database.username'),
25 PASSWORD: config.get<string>('database.password'),
26 POOL: {
27 MAX: config.get<number>('database.pool.max')
28 }
29 },
30 REDIS: {
31 HOSTNAME: config.has('redis.hostname') ? config.get<string>('redis.hostname') : null,
32 PORT: config.has('redis.port') ? config.get<number>('redis.port') : null,
33 SOCKET: config.has('redis.socket') ? config.get<string>('redis.socket') : null,
34 AUTH: config.has('redis.auth') ? config.get<string>('redis.auth') : null,
35 DB: config.has('redis.db') ? config.get<number>('redis.db') : null
36 },
37 SMTP: {
38 HOSTNAME: config.get<string>('smtp.hostname'),
39 PORT: config.get<number>('smtp.port'),
40 USERNAME: config.get<string>('smtp.username'),
41 PASSWORD: config.get<string>('smtp.password'),
42 TLS: config.get<boolean>('smtp.tls'),
43 DISABLE_STARTTLS: config.get<boolean>('smtp.disable_starttls'),
44 CA_FILE: config.get<string>('smtp.ca_file'),
45 FROM_ADDRESS: config.get<string>('smtp.from_address')
46 },
47 STORAGE: {
48 TMP_DIR: buildPath(config.get<string>('storage.tmp')),
49 AVATARS_DIR: buildPath(config.get<string>('storage.avatars')),
50 LOG_DIR: buildPath(config.get<string>('storage.logs')),
51 VIDEOS_DIR: buildPath(config.get<string>('storage.videos')),
52 STREAMING_PLAYLISTS_DIR: buildPath(config.get<string>('storage.streaming_playlists')),
53 REDUNDANCY_DIR: buildPath(config.get<string>('storage.redundancy')),
54 THUMBNAILS_DIR: buildPath(config.get<string>('storage.thumbnails')),
55 PREVIEWS_DIR: buildPath(config.get<string>('storage.previews')),
56 CAPTIONS_DIR: buildPath(config.get<string>('storage.captions')),
57 TORRENTS_DIR: buildPath(config.get<string>('storage.torrents')),
58 CACHE_DIR: buildPath(config.get<string>('storage.cache'))
59 },
60 WEBSERVER: {
61 SCHEME: config.get<boolean>('webserver.https') === true ? 'https' : 'http',
62 WS: config.get<boolean>('webserver.https') === true ? 'wss' : 'ws',
63 HOSTNAME: config.get<string>('webserver.hostname'),
64 PORT: config.get<number>('webserver.port')
65 },
66 TRUST_PROXY: config.get<string[]>('trust_proxy'),
67 LOG: {
68 LEVEL: config.get<string>('log.level')
69 },
70 SEARCH: {
71 REMOTE_URI: {
72 USERS: config.get<boolean>('search.remote_uri.users'),
73 ANONYMOUS: config.get<boolean>('search.remote_uri.anonymous')
74 }
75 },
76 TRENDING: {
77 VIDEOS: {
78 INTERVAL_DAYS: config.get<number>('trending.videos.interval_days')
79 }
80 },
81 REDUNDANCY: {
82 VIDEOS: {
83 CHECK_INTERVAL: parseDuration(config.get<string>('redundancy.videos.check_interval')),
84 STRATEGIES: buildVideosRedundancy(config.get<any[]>('redundancy.videos.strategies'))
85 }
86 },
87 CSP: {
88 ENABLED: config.get<boolean>('csp.enabled'),
89 REPORT_ONLY: config.get<boolean>('csp.report_only'),
90 REPORT_URI: config.get<boolean>('csp.report_uri')
91 },
92 TRACKER: {
93 ENABLED: config.get<boolean>('tracker.enabled'),
94 PRIVATE: config.get<boolean>('tracker.private'),
95 REJECT_TOO_MANY_ANNOUNCES: config.get<boolean>('tracker.reject_too_many_announces')
96 },
97 ADMIN: {
98 get EMAIL () { return config.get<string>('admin.email') }
99 },
100 CONTACT_FORM: {
101 get ENABLED () { return config.get<boolean>('contact_form.enabled') }
102 },
103 SIGNUP: {
104 get ENABLED () { return config.get<boolean>('signup.enabled') },
105 get LIMIT () { return config.get<number>('signup.limit') },
106 get REQUIRES_EMAIL_VERIFICATION () { return config.get<boolean>('signup.requires_email_verification') },
107 FILTERS: {
108 CIDR: {
109 get WHITELIST () { return config.get<string[]>('signup.filters.cidr.whitelist') },
110 get BLACKLIST () { return config.get<string[]>('signup.filters.cidr.blacklist') }
111 }
112 }
113 },
114 USER: {
115 get VIDEO_QUOTA () { return parseBytes(config.get<number>('user.video_quota')) },
116 get VIDEO_QUOTA_DAILY () { return parseBytes(config.get<number>('user.video_quota_daily')) }
117 },
118 TRANSCODING: {
119 get ENABLED () { return config.get<boolean>('transcoding.enabled') },
120 get ALLOW_ADDITIONAL_EXTENSIONS () { return config.get<boolean>('transcoding.allow_additional_extensions') },
121 get THREADS () { return config.get<number>('transcoding.threads') },
122 RESOLUTIONS: {
123 get '240p' () { return config.get<boolean>('transcoding.resolutions.240p') },
124 get '360p' () { return config.get<boolean>('transcoding.resolutions.360p') },
125 get '480p' () { return config.get<boolean>('transcoding.resolutions.480p') },
126 get '720p' () { return config.get<boolean>('transcoding.resolutions.720p') },
127 get '1080p' () { return config.get<boolean>('transcoding.resolutions.1080p') }
128 },
129 HLS: {
130 get ENABLED () { return config.get<boolean>('transcoding.hls.enabled') }
131 }
132 },
133 IMPORT: {
134 VIDEOS: {
135 HTTP: {
136 get ENABLED () { return config.get<boolean>('import.videos.http.enabled') }
137 },
138 TORRENT: {
139 get ENABLED () { return config.get<boolean>('import.videos.torrent.enabled') }
140 }
141 }
142 },
143 AUTO_BLACKLIST: {
144 VIDEOS: {
145 OF_USERS: {
146 get ENABLED () { return config.get<boolean>('auto_blacklist.videos.of_users.enabled') }
147 }
148 }
149 },
150 CACHE: {
151 PREVIEWS: {
152 get SIZE () { return config.get<number>('cache.previews.size') }
153 },
154 VIDEO_CAPTIONS: {
155 get SIZE () { return config.get<number>('cache.captions.size') }
156 }
157 },
158 INSTANCE: {
159 get NAME () { return config.get<string>('instance.name') },
160 get SHORT_DESCRIPTION () { return config.get<string>('instance.short_description') },
161 get DESCRIPTION () { return config.get<string>('instance.description') },
162 get TERMS () { return config.get<string>('instance.terms') },
163 get IS_NSFW () { return config.get<boolean>('instance.is_nsfw') },
164 get DEFAULT_CLIENT_ROUTE () { return config.get<string>('instance.default_client_route') },
165 get DEFAULT_NSFW_POLICY () { return config.get<NSFWPolicyType>('instance.default_nsfw_policy') },
166 CUSTOMIZATIONS: {
167 get JAVASCRIPT () { return config.get<string>('instance.customizations.javascript') },
168 get CSS () { return config.get<string>('instance.customizations.css') }
169 },
170 get ROBOTS () { return config.get<string>('instance.robots') },
171 get SECURITYTXT () { return config.get<string>('instance.securitytxt') },
172 get SECURITYTXT_CONTACT () { return config.get<string>('admin.email') }
173 },
174 SERVICES: {
175 TWITTER: {
176 get USERNAME () { return config.get<string>('services.twitter.username') },
177 get WHITELISTED () { return config.get<boolean>('services.twitter.whitelisted') }
178 }
179 },
180 FOLLOWERS: {
181 INSTANCE: {
182 get ENABLED () { return config.get<boolean>('followers.instance.enabled') },
183 get MANUAL_APPROVAL () { return config.get<boolean>('followers.instance.manual_approval') }
184 }
185 }
186}
187
188function registerConfigChangedHandler (fun: Function) {
189 configChangedHandlers.push(fun)
190}
191
192// ---------------------------------------------------------------------------
193
194export {
195 CONFIG,
196 registerConfigChangedHandler
197}
198
199// ---------------------------------------------------------------------------
200
201function getLocalConfigFilePath () {
202 const configSources = config.util.getConfigSources()
203 if (configSources.length === 0) throw new Error('Invalid config source.')
204
205 let filename = 'local'
206 if (process.env.NODE_ENV) filename += `-${process.env.NODE_ENV}`
207 if (process.env.NODE_APP_INSTANCE) filename += `-${process.env.NODE_APP_INSTANCE}`
208
209 return join(dirname(configSources[ 0 ].name), filename + '.json')
210}
211
212function buildVideosRedundancy (objs: any[]): VideosRedundancy[] {
213 if (!objs) return []
214
215 if (!Array.isArray(objs)) return objs
216
217 return objs.map(obj => {
218 return Object.assign({}, obj, {
219 minLifetime: parseDuration(obj.min_lifetime),
220 size: bytes.parse(obj.size),
221 minViews: obj.min_views
222 })
223 })
224}
225
226export function reloadConfig () {
227
228 function directory () {
229 if (process.env.NODE_CONFIG_DIR) {
230 return process.env.NODE_CONFIG_DIR
231 }
232
233 return join(root(), 'config')
234 }
235
236 function purge () {
237 for (const fileName in require.cache) {
238 if (-1 === fileName.indexOf(directory())) {
239 continue
240 }
241
242 delete require.cache[fileName]
243 }
244
245 delete require.cache[require.resolve('config')]
246 }
247
248 purge()
249
250 config = require('config')
251
252 for (const configChangedHandler of configChangedHandlers) {
253 configChangedHandler()
254 }
255}
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 739ea5502..3922d8515 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -1,20 +1,16 @@
1import { IConfig } from 'config' 1import { join } from 'path'
2import { dirname, join } from 'path' 2import { JobType, VideoRateType, VideoState } from '../../shared/models'
3import { JobType, VideoRateType, VideoState, VideosRedundancy } from '../../shared/models'
4import { ActivityPubActorType } from '../../shared/models/activitypub' 3import { ActivityPubActorType } from '../../shared/models/activitypub'
5import { FollowState } from '../../shared/models/actors' 4import { FollowState } from '../../shared/models/actors'
6import { VideoAbuseState, VideoImportState, VideoPrivacy, VideoTranscodingFPS } from '../../shared/models/videos' 5import { VideoAbuseState, VideoImportState, VideoPrivacy, VideoTranscodingFPS } from '../../shared/models/videos'
7// Do not use barrels, remain constants as independent as possible 6// Do not use barrels, remain constants as independent as possible
8import { buildPath, isTestInstance, parseDuration, parseBytes, root, sanitizeHost, sanitizeUrl } from '../helpers/core-utils' 7import { isTestInstance, sanitizeHost, sanitizeUrl } from '../helpers/core-utils'
9import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type' 8import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
10import { invert } from 'lodash' 9import { invert } from 'lodash'
11import { CronRepeatOptions, EveryRepeatOptions } from 'bull' 10import { CronRepeatOptions, EveryRepeatOptions } from 'bull'
12import * as bytes from 'bytes'
13import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model' 11import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model'
14import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model' 12import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model'
15 13import { CONFIG, registerConfigChangedHandler } from './config'
16// Use a variable to reload the configuration if we need
17let config: IConfig = require('config')
18 14
19// --------------------------------------------------------------------------- 15// ---------------------------------------------------------------------------
20 16
@@ -32,6 +28,15 @@ const PAGINATION = {
32 } 28 }
33} 29}
34 30
31const WEBSERVER = {
32 URL: '',
33 HOST: '',
34 SCHEME: '',
35 WS: '',
36 HOSTNAME: '',
37 PORT: 0
38}
39
35// Sortable columns per schema 40// Sortable columns per schema
36const SORTABLE_COLUMNS = { 41const SORTABLE_COLUMNS = {
37 USERS: [ 'id', 'username', 'createdAt' ], 42 USERS: [ 'id', 'username', 'createdAt' ],
@@ -102,7 +107,7 @@ const REMOTE_SCHEME = {
102} 107}
103 108
104// TODO: remove 'video-file' 109// TODO: remove 'video-file'
105const JOB_ATTEMPTS: { [ id in (JobType | 'video-file') ]: number } = { 110const JOB_ATTEMPTS: { [id in (JobType | 'video-file')]: number } = {
106 'activitypub-http-broadcast': 5, 111 'activitypub-http-broadcast': 5,
107 'activitypub-http-unicast': 5, 112 'activitypub-http-unicast': 5,
108 'activitypub-http-fetcher': 5, 113 'activitypub-http-fetcher': 5,
@@ -115,7 +120,7 @@ const JOB_ATTEMPTS: { [ id in (JobType | 'video-file') ]: number } = {
115 'videos-views': 1, 120 'videos-views': 1,
116 'activitypub-refresher': 1 121 'activitypub-refresher': 1
117} 122}
118const JOB_CONCURRENCY: { [ id in (JobType | 'video-file') ]: number } = { 123const JOB_CONCURRENCY: { [id in (JobType | 'video-file')]: number } = {
119 'activitypub-http-broadcast': 1, 124 'activitypub-http-broadcast': 1,
120 'activitypub-http-unicast': 5, 125 'activitypub-http-unicast': 5,
121 'activitypub-http-fetcher': 1, 126 'activitypub-http-fetcher': 1,
@@ -128,7 +133,7 @@ const JOB_CONCURRENCY: { [ id in (JobType | 'video-file') ]: number } = {
128 'videos-views': 1, 133 'videos-views': 1,
129 'activitypub-refresher': 1 134 'activitypub-refresher': 1
130} 135}
131const JOB_TTL: { [ id in (JobType | 'video-file') ]: number } = { 136const JOB_TTL: { [id in (JobType | 'video-file')]: number } = {
132 'activitypub-http-broadcast': 60000 * 10, // 10 minutes 137 'activitypub-http-broadcast': 60000 * 10, // 10 minutes
133 'activitypub-http-unicast': 60000 * 10, // 10 minutes 138 'activitypub-http-unicast': 60000 * 10, // 10 minutes
134 'activitypub-http-fetcher': 60000 * 10, // 10 minutes 139 'activitypub-http-fetcher': 60000 * 10, // 10 minutes
@@ -163,184 +168,6 @@ let SCHEDULER_INTERVALS_MS = {
163 168
164// --------------------------------------------------------------------------- 169// ---------------------------------------------------------------------------
165 170
166const CONFIG = {
167 CUSTOM_FILE: getLocalConfigFilePath(),
168 LISTEN: {
169 PORT: config.get<number>('listen.port'),
170 HOSTNAME: config.get<string>('listen.hostname')
171 },
172 DATABASE: {
173 DBNAME: 'peertube' + config.get<string>('database.suffix'),
174 HOSTNAME: config.get<string>('database.hostname'),
175 PORT: config.get<number>('database.port'),
176 USERNAME: config.get<string>('database.username'),
177 PASSWORD: config.get<string>('database.password'),
178 POOL: {
179 MAX: config.get<number>('database.pool.max')
180 }
181 },
182 REDIS: {
183 HOSTNAME: config.has('redis.hostname') ? config.get<string>('redis.hostname') : null,
184 PORT: config.has('redis.port') ? config.get<number>('redis.port') : null,
185 SOCKET: config.has('redis.socket') ? config.get<string>('redis.socket') : null,
186 AUTH: config.has('redis.auth') ? config.get<string>('redis.auth') : null,
187 DB: config.has('redis.db') ? config.get<number>('redis.db') : null
188 },
189 SMTP: {
190 HOSTNAME: config.get<string>('smtp.hostname'),
191 PORT: config.get<number>('smtp.port'),
192 USERNAME: config.get<string>('smtp.username'),
193 PASSWORD: config.get<string>('smtp.password'),
194 TLS: config.get<boolean>('smtp.tls'),
195 DISABLE_STARTTLS: config.get<boolean>('smtp.disable_starttls'),
196 CA_FILE: config.get<string>('smtp.ca_file'),
197 FROM_ADDRESS: config.get<string>('smtp.from_address')
198 },
199 STORAGE: {
200 TMP_DIR: buildPath(config.get<string>('storage.tmp')),
201 AVATARS_DIR: buildPath(config.get<string>('storage.avatars')),
202 LOG_DIR: buildPath(config.get<string>('storage.logs')),
203 VIDEOS_DIR: buildPath(config.get<string>('storage.videos')),
204 STREAMING_PLAYLISTS_DIR: buildPath(config.get<string>('storage.streaming_playlists')),
205 REDUNDANCY_DIR: buildPath(config.get<string>('storage.redundancy')),
206 THUMBNAILS_DIR: buildPath(config.get<string>('storage.thumbnails')),
207 PREVIEWS_DIR: buildPath(config.get<string>('storage.previews')),
208 CAPTIONS_DIR: buildPath(config.get<string>('storage.captions')),
209 TORRENTS_DIR: buildPath(config.get<string>('storage.torrents')),
210 CACHE_DIR: buildPath(config.get<string>('storage.cache'))
211 },
212 WEBSERVER: {
213 SCHEME: config.get<boolean>('webserver.https') === true ? 'https' : 'http',
214 WS: config.get<boolean>('webserver.https') === true ? 'wss' : 'ws',
215 HOSTNAME: config.get<string>('webserver.hostname'),
216 PORT: config.get<number>('webserver.port'),
217 URL: '',
218 HOST: ''
219 },
220 TRUST_PROXY: config.get<string[]>('trust_proxy'),
221 LOG: {
222 LEVEL: config.get<string>('log.level')
223 },
224 SEARCH: {
225 REMOTE_URI: {
226 USERS: config.get<boolean>('search.remote_uri.users'),
227 ANONYMOUS: config.get<boolean>('search.remote_uri.anonymous')
228 }
229 },
230 TRENDING: {
231 VIDEOS: {
232 INTERVAL_DAYS: config.get<number>('trending.videos.interval_days')
233 }
234 },
235 REDUNDANCY: {
236 VIDEOS: {
237 CHECK_INTERVAL: parseDuration(config.get<string>('redundancy.videos.check_interval')),
238 STRATEGIES: buildVideosRedundancy(config.get<any[]>('redundancy.videos.strategies'))
239 }
240 },
241 CSP: {
242 ENABLED: config.get<boolean>('csp.enabled'),
243 REPORT_ONLY: config.get<boolean>('csp.report_only'),
244 REPORT_URI: config.get<boolean>('csp.report_uri')
245 },
246 TRACKER: {
247 ENABLED: config.get<boolean>('tracker.enabled'),
248 PRIVATE: config.get<boolean>('tracker.private'),
249 REJECT_TOO_MANY_ANNOUNCES: config.get<boolean>('tracker.reject_too_many_announces')
250 },
251 ADMIN: {
252 get EMAIL () { return config.get<string>('admin.email') }
253 },
254 CONTACT_FORM: {
255 get ENABLED () { return config.get<boolean>('contact_form.enabled') }
256 },
257 SIGNUP: {
258 get ENABLED () { return config.get<boolean>('signup.enabled') },
259 get LIMIT () { return config.get<number>('signup.limit') },
260 get REQUIRES_EMAIL_VERIFICATION () { return config.get<boolean>('signup.requires_email_verification') },
261 FILTERS: {
262 CIDR: {
263 get WHITELIST () { return config.get<string[]>('signup.filters.cidr.whitelist') },
264 get BLACKLIST () { return config.get<string[]>('signup.filters.cidr.blacklist') }
265 }
266 }
267 },
268 USER: {
269 get VIDEO_QUOTA () { return parseBytes(config.get<number>('user.video_quota')) },
270 get VIDEO_QUOTA_DAILY () { return parseBytes(config.get<number>('user.video_quota_daily')) }
271 },
272 TRANSCODING: {
273 get ENABLED () { return config.get<boolean>('transcoding.enabled') },
274 get ALLOW_ADDITIONAL_EXTENSIONS () { return config.get<boolean>('transcoding.allow_additional_extensions') },
275 get THREADS () { return config.get<number>('transcoding.threads') },
276 RESOLUTIONS: {
277 get '240p' () { return config.get<boolean>('transcoding.resolutions.240p') },
278 get '360p' () { return config.get<boolean>('transcoding.resolutions.360p') },
279 get '480p' () { return config.get<boolean>('transcoding.resolutions.480p') },
280 get '720p' () { return config.get<boolean>('transcoding.resolutions.720p') },
281 get '1080p' () { return config.get<boolean>('transcoding.resolutions.1080p') }
282 },
283 HLS: {
284 get ENABLED () { return config.get<boolean>('transcoding.hls.enabled') }
285 }
286 },
287 IMPORT: {
288 VIDEOS: {
289 HTTP: {
290 get ENABLED () { return config.get<boolean>('import.videos.http.enabled') }
291 },
292 TORRENT: {
293 get ENABLED () { return config.get<boolean>('import.videos.torrent.enabled') }
294 }
295 }
296 },
297 AUTO_BLACKLIST: {
298 VIDEOS: {
299 OF_USERS: {
300 get ENABLED () { return config.get<boolean>('auto_blacklist.videos.of_users.enabled') }
301 }
302 }
303 },
304 CACHE: {
305 PREVIEWS: {
306 get SIZE () { return config.get<number>('cache.previews.size') }
307 },
308 VIDEO_CAPTIONS: {
309 get SIZE () { return config.get<number>('cache.captions.size') }
310 }
311 },
312 INSTANCE: {
313 get NAME () { return config.get<string>('instance.name') },
314 get SHORT_DESCRIPTION () { return config.get<string>('instance.short_description') },
315 get DESCRIPTION () { return config.get<string>('instance.description') },
316 get TERMS () { return config.get<string>('instance.terms') },
317 get IS_NSFW () { return config.get<boolean>('instance.is_nsfw') },
318 get DEFAULT_CLIENT_ROUTE () { return config.get<string>('instance.default_client_route') },
319 get DEFAULT_NSFW_POLICY () { return config.get<NSFWPolicyType>('instance.default_nsfw_policy') },
320 CUSTOMIZATIONS: {
321 get JAVASCRIPT () { return config.get<string>('instance.customizations.javascript') },
322 get CSS () { return config.get<string>('instance.customizations.css') }
323 },
324 get ROBOTS () { return config.get<string>('instance.robots') },
325 get SECURITYTXT () { return config.get<string>('instance.securitytxt') },
326 get SECURITYTXT_CONTACT () { return config.get<string>('admin.email') }
327 },
328 SERVICES: {
329 TWITTER: {
330 get USERNAME () { return config.get<string>('services.twitter.username') },
331 get WHITELISTED () { return config.get<boolean>('services.twitter.whitelisted') }
332 }
333 },
334 FOLLOWERS: {
335 INSTANCE: {
336 get ENABLED () { return config.get<boolean>('followers.instance.enabled') },
337 get MANUAL_APPROVAL () { return config.get<boolean>('followers.instance.manual_approval') }
338 }
339 }
340}
341
342// ---------------------------------------------------------------------------
343
344let CONSTRAINTS_FIELDS = { 171let CONSTRAINTS_FIELDS = {
345 USERS: { 172 USERS: {
346 NAME: { min: 1, max: 120 }, // Length 173 NAME: { min: 1, max: 120 }, // Length
@@ -517,38 +344,38 @@ const VIDEO_LICENCES = {
517const VIDEO_LANGUAGES = buildLanguages() 344const VIDEO_LANGUAGES = buildLanguages()
518 345
519const VIDEO_PRIVACIES = { 346const VIDEO_PRIVACIES = {
520 [VideoPrivacy.PUBLIC]: 'Public', 347 [ VideoPrivacy.PUBLIC ]: 'Public',
521 [VideoPrivacy.UNLISTED]: 'Unlisted', 348 [ VideoPrivacy.UNLISTED ]: 'Unlisted',
522 [VideoPrivacy.PRIVATE]: 'Private' 349 [ VideoPrivacy.PRIVATE ]: 'Private'
523} 350}
524 351
525const VIDEO_STATES = { 352const VIDEO_STATES = {
526 [VideoState.PUBLISHED]: 'Published', 353 [ VideoState.PUBLISHED ]: 'Published',
527 [VideoState.TO_TRANSCODE]: 'To transcode', 354 [ VideoState.TO_TRANSCODE ]: 'To transcode',
528 [VideoState.TO_IMPORT]: 'To import' 355 [ VideoState.TO_IMPORT ]: 'To import'
529} 356}
530 357
531const VIDEO_IMPORT_STATES = { 358const VIDEO_IMPORT_STATES = {
532 [VideoImportState.FAILED]: 'Failed', 359 [ VideoImportState.FAILED ]: 'Failed',
533 [VideoImportState.PENDING]: 'Pending', 360 [ VideoImportState.PENDING ]: 'Pending',
534 [VideoImportState.SUCCESS]: 'Success' 361 [ VideoImportState.SUCCESS ]: 'Success'
535} 362}
536 363
537const VIDEO_ABUSE_STATES = { 364const VIDEO_ABUSE_STATES = {
538 [VideoAbuseState.PENDING]: 'Pending', 365 [ VideoAbuseState.PENDING ]: 'Pending',
539 [VideoAbuseState.REJECTED]: 'Rejected', 366 [ VideoAbuseState.REJECTED ]: 'Rejected',
540 [VideoAbuseState.ACCEPTED]: 'Accepted' 367 [ VideoAbuseState.ACCEPTED ]: 'Accepted'
541} 368}
542 369
543const VIDEO_PLAYLIST_PRIVACIES = { 370const VIDEO_PLAYLIST_PRIVACIES = {
544 [VideoPlaylistPrivacy.PUBLIC]: 'Public', 371 [ VideoPlaylistPrivacy.PUBLIC ]: 'Public',
545 [VideoPlaylistPrivacy.UNLISTED]: 'Unlisted', 372 [ VideoPlaylistPrivacy.UNLISTED ]: 'Unlisted',
546 [VideoPlaylistPrivacy.PRIVATE]: 'Private' 373 [ VideoPlaylistPrivacy.PRIVATE ]: 'Private'
547} 374}
548 375
549const VIDEO_PLAYLIST_TYPES = { 376const VIDEO_PLAYLIST_TYPES = {
550 [VideoPlaylistType.REGULAR]: 'Regular', 377 [ VideoPlaylistType.REGULAR ]: 'Regular',
551 [VideoPlaylistType.WATCH_LATER]: 'Watch later' 378 [ VideoPlaylistType.WATCH_LATER ]: 'Watch later'
552} 379}
553 380
554const MIMETYPES = { 381const MIMETYPES = {
@@ -634,7 +461,7 @@ const USER_PASSWORD_RESET_LIFETIME = 60000 * 5 // 5 minutes
634 461
635const USER_EMAIL_VERIFY_LIFETIME = 60000 * 60 // 60 minutes 462const USER_EMAIL_VERIFY_LIFETIME = 60000 * 60 // 60 minutes
636 463
637const NSFW_POLICY_TYPES: { [ id: string]: NSFWPolicyType } = { 464const NSFW_POLICY_TYPES: { [ id: string ]: NSFWPolicyType } = {
638 DO_NOT_LIST: 'do_not_list', 465 DO_NOT_LIST: 'do_not_list',
639 BLUR: 'blur', 466 BLUR: 'blur',
640 DISPLAY: 'display' 467 DISPLAY: 'display'
@@ -765,14 +592,14 @@ if (isTestInstance() === true) {
765 SCHEDULER_INTERVALS_MS.actorFollowScores = 1000 592 SCHEDULER_INTERVALS_MS.actorFollowScores = 1000
766 SCHEDULER_INTERVALS_MS.removeOldJobs = 10000 593 SCHEDULER_INTERVALS_MS.removeOldJobs = 10000
767 SCHEDULER_INTERVALS_MS.updateVideos = 5000 594 SCHEDULER_INTERVALS_MS.updateVideos = 5000
768 REPEAT_JOBS['videos-views'] = { every: 5000 } 595 REPEAT_JOBS[ 'videos-views' ] = { every: 5000 }
769 596
770 REDUNDANCY.VIDEOS.RANDOMIZED_FACTOR = 1 597 REDUNDANCY.VIDEOS.RANDOMIZED_FACTOR = 1
771 598
772 VIDEO_VIEW_LIFETIME = 1000 // 1 second 599 VIDEO_VIEW_LIFETIME = 1000 // 1 second
773 CONTACT_FORM_LIFETIME = 1000 // 1 second 600 CONTACT_FORM_LIFETIME = 1000 // 1 second
774 601
775 JOB_ATTEMPTS['email'] = 1 602 JOB_ATTEMPTS[ 'email' ] = 1
776 603
777 FILES_CACHE.VIDEO_CAPTIONS.MAX_AGE = 3000 604 FILES_CACHE.VIDEO_CAPTIONS.MAX_AGE = 3000
778 MEMOIZE_TTL.OVERVIEWS_SAMPLE = 1 605 MEMOIZE_TTL.OVERVIEWS_SAMPLE = 1
@@ -783,9 +610,15 @@ if (isTestInstance() === true) {
783 610
784updateWebserverUrls() 611updateWebserverUrls()
785 612
613registerConfigChangedHandler(() => {
614 updateWebserverUrls()
615 updateWebserverConfig()
616})
617
786// --------------------------------------------------------------------------- 618// ---------------------------------------------------------------------------
787 619
788export { 620export {
621 WEBSERVER,
789 API_VERSION, 622 API_VERSION,
790 HLS_REDUNDANCY_DIRECTORY, 623 HLS_REDUNDANCY_DIRECTORY,
791 P2P_MEDIA_LOADER_PEER_VERSION, 624 P2P_MEDIA_LOADER_PEER_VERSION,
@@ -794,7 +627,6 @@ export {
794 BCRYPT_SALT_SIZE, 627 BCRYPT_SALT_SIZE,
795 TRACKER_RATE_LIMITS, 628 TRACKER_RATE_LIMITS,
796 FILES_CACHE, 629 FILES_CACHE,
797 CONFIG,
798 CONSTRAINTS_FIELDS, 630 CONSTRAINTS_FIELDS,
799 EMBED_SIZE, 631 EMBED_SIZE,
800 REDUNDANCY, 632 REDUNDANCY,
@@ -857,17 +689,6 @@ export {
857 689
858// --------------------------------------------------------------------------- 690// ---------------------------------------------------------------------------
859 691
860function getLocalConfigFilePath () {
861 const configSources = config.util.getConfigSources()
862 if (configSources.length === 0) throw new Error('Invalid config source.')
863
864 let filename = 'local'
865 if (process.env.NODE_ENV) filename += `-${process.env.NODE_ENV}`
866 if (process.env.NODE_APP_INSTANCE) filename += `-${process.env.NODE_APP_INSTANCE}`
867
868 return join(dirname(configSources[ 0 ].name), filename + '.json')
869}
870
871function buildVideoMimetypeExt () { 692function buildVideoMimetypeExt () {
872 const data = { 693 const data = {
873 'video/webm': '.webm', 694 'video/webm': '.webm',
@@ -890,8 +711,12 @@ function buildVideoMimetypeExt () {
890} 711}
891 712
892function updateWebserverUrls () { 713function updateWebserverUrls () {
893 CONFIG.WEBSERVER.URL = sanitizeUrl(CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT) 714 WEBSERVER.URL = sanitizeUrl(CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT)
894 CONFIG.WEBSERVER.HOST = sanitizeHost(CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT, REMOTE_SCHEME.HTTP) 715 WEBSERVER.HOST = sanitizeHost(CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT, REMOTE_SCHEME.HTTP)
716 WEBSERVER.SCHEME = CONFIG.WEBSERVER.SCHEME
717 WEBSERVER.WS = CONFIG.WEBSERVER.WS
718 WEBSERVER.HOSTNAME = CONFIG.WEBSERVER.HOSTNAME
719 WEBSERVER.PORT = CONFIG.WEBSERVER.PORT
895} 720}
896 721
897function updateWebserverConfig () { 722function updateWebserverConfig () {
@@ -907,20 +732,6 @@ function buildVideosExtname () {
907 : [ '.mp4', '.ogv', '.webm' ] 732 : [ '.mp4', '.ogv', '.webm' ]
908} 733}
909 734
910function buildVideosRedundancy (objs: any[]): VideosRedundancy[] {
911 if (!objs) return []
912
913 if (!Array.isArray(objs)) return objs
914
915 return objs.map(obj => {
916 return Object.assign({}, obj, {
917 minLifetime: parseDuration(obj.min_lifetime),
918 size: bytes.parse(obj.size),
919 minViews: obj.min_views
920 })
921 })
922}
923
924function buildLanguages () { 735function buildLanguages () {
925 const iso639 = require('iso-639-3') 736 const iso639 = require('iso-639-3')
926 737
@@ -953,42 +764,12 @@ function buildLanguages () {
953 iso639 764 iso639
954 .filter(l => { 765 .filter(l => {
955 return (l.iso6391 !== null && l.type === 'living') || 766 return (l.iso6391 !== null && l.type === 'living') ||
956 additionalLanguages[l.iso6393] === true 767 additionalLanguages[ l.iso6393 ] === true
957 }) 768 })
958 .forEach(l => languages[l.iso6391 || l.iso6393] = l.name) 769 .forEach(l => languages[ l.iso6391 || l.iso6393 ] = l.name)
959 770
960 // Override Occitan label 771 // Override Occitan label
961 languages['oc'] = 'Occitan' 772 languages[ 'oc' ] = 'Occitan'
962 773
963 return languages 774 return languages
964} 775}
965
966export function reloadConfig () {
967
968 function directory () {
969 if (process.env.NODE_CONFIG_DIR) {
970 return process.env.NODE_CONFIG_DIR
971 }
972
973 return join(root(), 'config')
974 }
975
976 function purge () {
977 for (const fileName in require.cache) {
978 if (-1 === fileName.indexOf(directory())) {
979 continue
980 }
981
982 delete require.cache[fileName]
983 }
984
985 delete require.cache[require.resolve('config')]
986 }
987
988 purge()
989
990 config = require('config')
991
992 updateWebserverConfig()
993 updateWebserverUrls()
994}
diff --git a/server/initializers/database.ts b/server/initializers/database.ts
index 541ebbecf..872a56220 100644
--- a/server/initializers/database.ts
+++ b/server/initializers/database.ts
@@ -21,7 +21,7 @@ import { VideoCommentModel } from '../models/video/video-comment'
21import { VideoFileModel } from '../models/video/video-file' 21import { VideoFileModel } from '../models/video/video-file'
22import { VideoShareModel } from '../models/video/video-share' 22import { VideoShareModel } from '../models/video/video-share'
23import { VideoTagModel } from '../models/video/video-tag' 23import { VideoTagModel } from '../models/video/video-tag'
24import { CONFIG } from './constants' 24import { CONFIG } from './config'
25import { ScheduleVideoUpdateModel } from '../models/video/schedule-video-update' 25import { ScheduleVideoUpdateModel } from '../models/video/schedule-video-update'
26import { VideoCaptionModel } from '../models/video/video-caption' 26import { VideoCaptionModel } from '../models/video/video-caption'
27import { VideoImportModel } from '../models/video/video-import' 27import { VideoImportModel } from '../models/video/video-import'
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts
index 07af96b68..127449577 100644
--- a/server/initializers/installer.ts
+++ b/server/initializers/installer.ts
@@ -6,9 +6,10 @@ import { UserModel } from '../models/account/user'
6import { ApplicationModel } from '../models/application/application' 6import { ApplicationModel } from '../models/application/application'
7import { OAuthClientModel } from '../models/oauth/oauth-client' 7import { OAuthClientModel } from '../models/oauth/oauth-client'
8import { applicationExist, clientsExist, usersExist } from './checker-after-init' 8import { applicationExist, clientsExist, usersExist } from './checker-after-init'
9import { FILES_CACHE, CONFIG, HLS_STREAMING_PLAYLIST_DIRECTORY, LAST_MIGRATION_VERSION } from './constants' 9import { FILES_CACHE, HLS_STREAMING_PLAYLIST_DIRECTORY, LAST_MIGRATION_VERSION } from './constants'
10import { sequelizeTypescript } from './database' 10import { sequelizeTypescript } from './database'
11import { remove, ensureDir } from 'fs-extra' 11import { ensureDir, remove } from 'fs-extra'
12import { CONFIG } from './config'
12 13
13async function installApplication () { 14async function installApplication () {
14 try { 15 try {
diff --git a/server/initializers/migrations/0075-video-resolutions.ts b/server/initializers/migrations/0075-video-resolutions.ts
index 26a188e5e..e4f26cb77 100644
--- a/server/initializers/migrations/0075-video-resolutions.ts
+++ b/server/initializers/migrations/0075-video-resolutions.ts
@@ -1,6 +1,6 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { join } from 'path' 2import { join } from 'path'
3import { CONFIG } from '../../initializers/constants' 3import { CONFIG } from '../../initializers/config'
4import { getVideoFileResolution } from '../../helpers/ffmpeg-utils' 4import { getVideoFileResolution } from '../../helpers/ffmpeg-utils'
5import { readdir, rename } from 'fs-extra' 5import { readdir, rename } from 'fs-extra'
6 6
diff --git a/server/initializers/migrations/0140-actor-url.ts b/server/initializers/migrations/0140-actor-url.ts
index e64ee3487..020499391 100644
--- a/server/initializers/migrations/0140-actor-url.ts
+++ b/server/initializers/migrations/0140-actor-url.ts
@@ -1,13 +1,13 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { CONFIG } from '../constants' 2import { WEBSERVER } from '../constants'
3 3
4async function up (utils: { 4async function up (utils: {
5 transaction: Sequelize.Transaction, 5 transaction: Sequelize.Transaction,
6 queryInterface: Sequelize.QueryInterface, 6 queryInterface: Sequelize.QueryInterface,
7 sequelize: Sequelize.Sequelize 7 sequelize: Sequelize.Sequelize
8}): Promise<void> { 8}): Promise<void> {
9 const toReplace = CONFIG.WEBSERVER.HOSTNAME + ':443' 9 const toReplace = WEBSERVER.HOSTNAME + ':443'
10 const by = CONFIG.WEBSERVER.HOST 10 const by = WEBSERVER.HOST
11 const replacer = column => `replace("${column}", '${toReplace}', '${by}')` 11 const replacer = column => `replace("${column}", '${toReplace}', '${by}')`
12 12
13 { 13 {
diff --git a/server/initializers/migrations/0235-delete-some-video-indexes.ts b/server/initializers/migrations/0235-delete-some-video-indexes.ts
index e362f240c..5964b0dc5 100644
--- a/server/initializers/migrations/0235-delete-some-video-indexes.ts
+++ b/server/initializers/migrations/0235-delete-some-video-indexes.ts
@@ -1,8 +1,4 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { createClient } from 'redis'
3import { CONFIG } from '../constants'
4import { JobQueue } from '../../lib/job-queue'
5import { initDatabaseModels } from '../database'
6 2
7async function up (utils: { 3async function up (utils: {
8 transaction: Sequelize.Transaction 4 transaction: Sequelize.Transaction
diff --git a/server/initializers/migrations/0240-drop-old-indexes.ts b/server/initializers/migrations/0240-drop-old-indexes.ts
index ba961e3f9..39868fa2d 100644
--- a/server/initializers/migrations/0240-drop-old-indexes.ts
+++ b/server/initializers/migrations/0240-drop-old-indexes.ts
@@ -1,8 +1,4 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { createClient } from 'redis'
3import { CONFIG } from '../constants'
4import { JobQueue } from '../../lib/job-queue'
5import { initDatabaseModels } from '../database'
6 2
7async function up (utils: { 3async function up (utils: {
8 transaction: Sequelize.Transaction 4 transaction: Sequelize.Transaction
diff --git a/server/initializers/migrations/0345-video-playlists.ts b/server/initializers/migrations/0345-video-playlists.ts
index 11670b11d..6953f5553 100644
--- a/server/initializers/migrations/0345-video-playlists.ts
+++ b/server/initializers/migrations/0345-video-playlists.ts
@@ -1,7 +1,7 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { CONFIG } from '../constants'
3import { VideoPlaylistPrivacy, VideoPlaylistType } from '../../../shared/models/videos' 2import { VideoPlaylistPrivacy, VideoPlaylistType } from '../../../shared/models/videos'
4import * as uuidv4 from 'uuid/v4' 3import * as uuidv4 from 'uuid/v4'
4import { WEBSERVER } from '../constants'
5 5
6async function up (utils: { 6async function up (utils: {
7 transaction: Sequelize.Transaction, 7 transaction: Sequelize.Transaction,
@@ -57,7 +57,7 @@ CREATE TABLE IF NOT EXISTS "videoPlaylistElement"
57 for (const username of usernames) { 57 for (const username of usernames) {
58 const uuid = uuidv4() 58 const uuid = uuidv4()
59 59
60 const baseUrl = CONFIG.WEBSERVER.URL + '/video-playlists/' + uuid 60 const baseUrl = WEBSERVER.URL + '/video-playlists/' + uuid
61 const query = ` 61 const query = `
62 INSERT INTO "videoPlaylist" ("url", "uuid", "name", "privacy", "type", "ownerAccountId", "createdAt", "updatedAt") 62 INSERT INTO "videoPlaylist" ("url", "uuid", "name", "privacy", "type", "ownerAccountId", "createdAt", "updatedAt")
63 SELECT '${baseUrl}' AS "url", 63 SELECT '${baseUrl}' AS "url",