aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/initializers
diff options
context:
space:
mode:
Diffstat (limited to 'server/initializers')
-rw-r--r--server/initializers/checker-after-init.ts15
-rw-r--r--server/initializers/checker-before-init.ts15
-rw-r--r--server/initializers/config.ts21
-rw-r--r--server/initializers/constants.ts70
-rw-r--r--server/initializers/installer.ts10
-rw-r--r--server/initializers/migrations/0745-user-otp.ts29
6 files changed, 141 insertions, 19 deletions
diff --git a/server/initializers/checker-after-init.ts b/server/initializers/checker-after-init.ts
index 42839d1c9..09e878eee 100644
--- a/server/initializers/checker-after-init.ts
+++ b/server/initializers/checker-after-init.ts
@@ -42,6 +42,7 @@ function checkConfig () {
42 logger.warn('services.csp-logger configuration has been renamed to csp.report_uri. Please update your configuration file.') 42 logger.warn('services.csp-logger configuration has been renamed to csp.report_uri. Please update your configuration file.')
43 } 43 }
44 44
45 checkSecretsConfig()
45 checkEmailConfig() 46 checkEmailConfig()
46 checkNSFWPolicyConfig() 47 checkNSFWPolicyConfig()
47 checkLocalRedundancyConfig() 48 checkLocalRedundancyConfig()
@@ -103,6 +104,12 @@ export {
103 104
104// --------------------------------------------------------------------------- 105// ---------------------------------------------------------------------------
105 106
107function checkSecretsConfig () {
108 if (!CONFIG.SECRETS.PEERTUBE) {
109 throw new Error('secrets.peertube is missing in config. Generate one using `openssl rand -hex 32`')
110 }
111}
112
106function checkEmailConfig () { 113function checkEmailConfig () {
107 if (!isEmailEnabled()) { 114 if (!isEmailEnabled()) {
108 if (CONFIG.SIGNUP.ENABLED && CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { 115 if (CONFIG.SIGNUP.ENABLED && CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
@@ -271,6 +278,14 @@ function checkObjectStorageConfig () {
271 'Object storage bucket prefixes should be set to different values when the same bucket is used for both types of video.' 278 'Object storage bucket prefixes should be set to different values when the same bucket is used for both types of video.'
272 ) 279 )
273 } 280 }
281
282 if (!CONFIG.OBJECT_STORAGE.UPLOAD_ACL.PUBLIC) {
283 throw new Error('object_storage.upload_acl.public must be set')
284 }
285
286 if (!CONFIG.OBJECT_STORAGE.UPLOAD_ACL.PRIVATE) {
287 throw new Error('object_storage.upload_acl.private must be set')
288 }
274 } 289 }
275} 290}
276 291
diff --git a/server/initializers/checker-before-init.ts b/server/initializers/checker-before-init.ts
index 3188903be..39713a266 100644
--- a/server/initializers/checker-before-init.ts
+++ b/server/initializers/checker-before-init.ts
@@ -11,13 +11,17 @@ const config: IConfig = require('config')
11function checkMissedConfig () { 11function checkMissedConfig () {
12 const required = [ 'listen.port', 'listen.hostname', 12 const required = [ 'listen.port', 'listen.hostname',
13 'webserver.https', 'webserver.hostname', 'webserver.port', 13 'webserver.https', 'webserver.hostname', 'webserver.port',
14 'secrets.peertube',
14 'trust_proxy', 15 'trust_proxy',
15 'database.hostname', 'database.port', 'database.username', 'database.password', 'database.pool.max', 16 'database.hostname', 'database.port', 'database.username', 'database.password', 'database.pool.max',
16 'smtp.hostname', 'smtp.port', 'smtp.username', 'smtp.password', 'smtp.tls', 'smtp.from_address', 17 'smtp.hostname', 'smtp.port', 'smtp.username', 'smtp.password', 'smtp.tls', 'smtp.from_address',
17 'email.body.signature', 'email.subject.prefix', 18 'email.body.signature', 'email.subject.prefix',
18 'storage.avatars', 'storage.videos', 'storage.logs', 'storage.previews', 'storage.thumbnails', 'storage.torrents', 'storage.cache', 19 'storage.avatars', 'storage.videos', 'storage.logs', 'storage.previews', 'storage.thumbnails', 'storage.torrents', 'storage.cache',
19 'storage.redundancy', 'storage.tmp', 'storage.streaming_playlists', 'storage.plugins', 20 'storage.redundancy', 'storage.tmp', 'storage.streaming_playlists', 'storage.plugins', 'storage.well_known',
20 'log.level', 21 'log.level', 'log.rotation.enabled', 'log.rotation.max_file_size', 'log.rotation.max_files', 'log.anonymize_ip',
22 'log.log_ping_requests', 'log.log_tracker_unknown_infohash', 'log.prettify_sql', 'log.accept_client_log',
23 'open_telemetry.metrics.enabled', 'open_telemetry.metrics.prometheus_exporter.hostname',
24 'open_telemetry.metrics.prometheus_exporter.port', 'open_telemetry.tracing.enabled', 'open_telemetry.tracing.jaeger_exporter.endpoint',
21 'user.video_quota', 'user.video_quota_daily', 25 'user.video_quota', 'user.video_quota_daily',
22 'video_channels.max_per_user', 26 'video_channels.max_per_user',
23 'csp.enabled', 'csp.report_only', 'csp.report_uri', 27 'csp.enabled', 'csp.report_only', 'csp.report_uri',
@@ -34,6 +38,7 @@ function checkMissedConfig () {
34 'import.videos.http.enabled', 'import.videos.torrent.enabled', 'import.videos.concurrency', 'import.videos.timeout', 38 'import.videos.http.enabled', 'import.videos.torrent.enabled', 'import.videos.concurrency', 'import.videos.timeout',
35 'import.video_channel_synchronization.enabled', 'import.video_channel_synchronization.max_per_user', 39 'import.video_channel_synchronization.enabled', 'import.video_channel_synchronization.max_per_user',
36 'import.video_channel_synchronization.check_interval', 'import.video_channel_synchronization.videos_limit_per_synchronization', 40 'import.video_channel_synchronization.check_interval', 'import.video_channel_synchronization.videos_limit_per_synchronization',
41 'import.video_channel_synchronization.full_sync_videos_limit',
37 'auto_blacklist.videos.of_users.enabled', 'trending.videos.interval_days', 42 'auto_blacklist.videos.of_users.enabled', 'trending.videos.interval_days',
38 'client.videos.miniature.display_author_avatar', 43 'client.videos.miniature.display_author_avatar',
39 'client.videos.miniature.prefer_author_display_name', 'client.menu.login.redirect_on_single_external_auth', 44 'client.videos.miniature.prefer_author_display_name', 'client.menu.login.redirect_on_single_external_auth',
@@ -45,6 +50,12 @@ function checkMissedConfig () {
45 'tracker.enabled', 'tracker.private', 'tracker.reject_too_many_announces', 50 'tracker.enabled', 'tracker.private', 'tracker.reject_too_many_announces',
46 'history.videos.max_age', 'views.videos.remote.max_age', 'views.videos.local_buffer_update_interval', 'views.videos.ip_view_expiration', 51 'history.videos.max_age', 'views.videos.remote.max_age', 'views.videos.local_buffer_update_interval', 'views.videos.ip_view_expiration',
47 'rates_limit.login.window', 'rates_limit.login.max', 'rates_limit.ask_send_email.window', 'rates_limit.ask_send_email.max', 52 'rates_limit.login.window', 'rates_limit.login.max', 'rates_limit.ask_send_email.window', 'rates_limit.ask_send_email.max',
53 'static_files.private_files_require_auth',
54 'object_storage.enabled', 'object_storage.endpoint', 'object_storage.region', 'object_storage.upload_acl.public',
55 'object_storage.upload_acl.private', 'object_storage.proxy.proxify_private_files', 'object_storage.credentials.access_key_id',
56 'object_storage.credentials.secret_access_key', 'object_storage.max_upload_part', 'object_storage.streaming_playlists.bucket_name',
57 'object_storage.streaming_playlists.prefix', 'object_storage.streaming_playlists.base_url', 'object_storage.videos.bucket_name',
58 'object_storage.videos.prefix', 'object_storage.videos.base_url',
48 'theme.default', 59 'theme.default',
49 'feeds.videos.count', 'feeds.comments.count', 60 'feeds.videos.count', 'feeds.comments.count',
50 'geo_ip.enabled', 'geo_ip.country.database_url', 61 'geo_ip.enabled', 'geo_ip.country.database_url',
diff --git a/server/initializers/config.ts b/server/initializers/config.ts
index 2c92bea22..c2f8b19fd 100644
--- a/server/initializers/config.ts
+++ b/server/initializers/config.ts
@@ -20,6 +20,9 @@ const CONFIG = {
20 PORT: config.get<number>('listen.port'), 20 PORT: config.get<number>('listen.port'),
21 HOSTNAME: config.get<string>('listen.hostname') 21 HOSTNAME: config.get<string>('listen.hostname')
22 }, 22 },
23 SECRETS: {
24 PEERTUBE: config.get<string>('secrets.peertube')
25 },
23 DATABASE: { 26 DATABASE: {
24 DBNAME: config.has('database.name') ? config.get<string>('database.name') : 'peertube' + config.get<string>('database.suffix'), 27 DBNAME: config.has('database.name') ? config.get<string>('database.name') : 'peertube' + config.get<string>('database.suffix'),
25 HOSTNAME: config.get<string>('database.hostname'), 28 HOSTNAME: config.get<string>('database.hostname'),
@@ -107,18 +110,28 @@ const CONFIG = {
107 TORRENTS_DIR: buildPath(config.get<string>('storage.torrents')), 110 TORRENTS_DIR: buildPath(config.get<string>('storage.torrents')),
108 CACHE_DIR: buildPath(config.get<string>('storage.cache')), 111 CACHE_DIR: buildPath(config.get<string>('storage.cache')),
109 PLUGINS_DIR: buildPath(config.get<string>('storage.plugins')), 112 PLUGINS_DIR: buildPath(config.get<string>('storage.plugins')),
110 CLIENT_OVERRIDES_DIR: buildPath(config.get<string>('storage.client_overrides')) 113 CLIENT_OVERRIDES_DIR: buildPath(config.get<string>('storage.client_overrides')),
114 WELL_KNOWN_DIR: buildPath(config.get<string>('storage.well_known'))
115 },
116 STATIC_FILES: {
117 PRIVATE_FILES_REQUIRE_AUTH: config.get<boolean>('static_files.private_files_require_auth')
111 }, 118 },
112 OBJECT_STORAGE: { 119 OBJECT_STORAGE: {
113 ENABLED: config.get<boolean>('object_storage.enabled'), 120 ENABLED: config.get<boolean>('object_storage.enabled'),
114 MAX_UPLOAD_PART: bytes.parse(config.get<string>('object_storage.max_upload_part')), 121 MAX_UPLOAD_PART: bytes.parse(config.get<string>('object_storage.max_upload_part')),
115 ENDPOINT: config.get<string>('object_storage.endpoint'), 122 ENDPOINT: config.get<string>('object_storage.endpoint'),
116 REGION: config.get<string>('object_storage.region'), 123 REGION: config.get<string>('object_storage.region'),
117 UPLOAD_ACL: config.get<string>('object_storage.upload_acl'), 124 UPLOAD_ACL: {
125 PUBLIC: config.get<string>('object_storage.upload_acl.public'),
126 PRIVATE: config.get<string>('object_storage.upload_acl.private')
127 },
118 CREDENTIALS: { 128 CREDENTIALS: {
119 ACCESS_KEY_ID: config.get<string>('object_storage.credentials.access_key_id'), 129 ACCESS_KEY_ID: config.get<string>('object_storage.credentials.access_key_id'),
120 SECRET_ACCESS_KEY: config.get<string>('object_storage.credentials.secret_access_key') 130 SECRET_ACCESS_KEY: config.get<string>('object_storage.credentials.secret_access_key')
121 }, 131 },
132 PROXY: {
133 PROXIFY_PRIVATE_FILES: config.get<boolean>('object_storage.proxy.proxify_private_files')
134 },
122 VIDEOS: { 135 VIDEOS: {
123 BUCKET_NAME: config.get<string>('object_storage.videos.bucket_name'), 136 BUCKET_NAME: config.get<string>('object_storage.videos.bucket_name'),
124 PREFIX: config.get<string>('object_storage.videos.prefix'), 137 PREFIX: config.get<string>('object_storage.videos.prefix'),
@@ -177,6 +190,7 @@ const CONFIG = {
177 ENABLED: config.get<boolean>('open_telemetry.metrics.enabled'), 190 ENABLED: config.get<boolean>('open_telemetry.metrics.enabled'),
178 191
179 PROMETHEUS_EXPORTER: { 192 PROMETHEUS_EXPORTER: {
193 HOSTNAME: config.get<string>('open_telemetry.metrics.prometheus_exporter.hostname'),
180 PORT: config.get<number>('open_telemetry.metrics.prometheus_exporter.port') 194 PORT: config.get<number>('open_telemetry.metrics.prometheus_exporter.port')
181 } 195 }
182 }, 196 },
@@ -405,6 +419,9 @@ const CONFIG = {
405 get CHECK_INTERVAL () { return parseDurationToMs(config.get<string>('import.video_channel_synchronization.check_interval')) }, 419 get CHECK_INTERVAL () { return parseDurationToMs(config.get<string>('import.video_channel_synchronization.check_interval')) },
406 get VIDEOS_LIMIT_PER_SYNCHRONIZATION () { 420 get VIDEOS_LIMIT_PER_SYNCHRONIZATION () {
407 return config.get<number>('import.video_channel_synchronization.videos_limit_per_synchronization') 421 return config.get<number>('import.video_channel_synchronization.videos_limit_per_synchronization')
422 },
423 get FULL_SYNC_VIDEOS_LIMIT () {
424 return config.get<number>('import.video_channel_synchronization.full_sync_videos_limit')
408 } 425 }
409 } 426 }
410 }, 427 },
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index eb54781b6..3908bbf05 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -1,5 +1,5 @@
1import { RepeatOptions } from 'bullmq' 1import { RepeatOptions } from 'bullmq'
2import { randomBytes } from 'crypto' 2import { Encoding, randomBytes } from 'crypto'
3import { invert } from 'lodash' 3import { invert } from 'lodash'
4import { join } from 'path' 4import { join } from 'path'
5import { randomInt, root } from '@shared/core-utils' 5import { randomInt, root } from '@shared/core-utils'
@@ -25,7 +25,7 @@ import { CONFIG, registerConfigChangedHandler } from './config'
25 25
26// --------------------------------------------------------------------------- 26// ---------------------------------------------------------------------------
27 27
28const LAST_MIGRATION_VERSION = 740 28const LAST_MIGRATION_VERSION = 745
29 29
30// --------------------------------------------------------------------------- 30// ---------------------------------------------------------------------------
31 31
@@ -116,7 +116,8 @@ const ROUTE_CACHE_LIFETIME = {
116 ACTIVITY_PUB: { 116 ACTIVITY_PUB: {
117 VIDEOS: '1 second' // 1 second, cache concurrent requests after a broadcast for example 117 VIDEOS: '1 second' // 1 second, cache concurrent requests after a broadcast for example
118 }, 118 },
119 STATS: '4 hours' 119 STATS: '4 hours',
120 WELL_KNOWN: '1 day'
120} 121}
121 122
122// --------------------------------------------------------------------------- 123// ---------------------------------------------------------------------------
@@ -636,9 +637,18 @@ let PRIVATE_RSA_KEY_SIZE = 2048
636// Password encryption 637// Password encryption
637const BCRYPT_SALT_SIZE = 10 638const BCRYPT_SALT_SIZE = 10
638 639
640const ENCRYPTION = {
641 ALGORITHM: 'aes-256-cbc',
642 IV: 16,
643 SALT: 'peertube',
644 ENCODING: 'hex' as Encoding
645}
646
639const USER_PASSWORD_RESET_LIFETIME = 60000 * 60 // 60 minutes 647const USER_PASSWORD_RESET_LIFETIME = 60000 * 60 // 60 minutes
640const USER_PASSWORD_CREATE_LIFETIME = 60000 * 60 * 24 * 7 // 7 days 648const USER_PASSWORD_CREATE_LIFETIME = 60000 * 60 * 24 * 7 // 7 days
641 649
650const TWO_FACTOR_AUTH_REQUEST_TOKEN_LIFETIME = 60000 * 10 // 10 minutes
651
642const USER_EMAIL_VERIFY_LIFETIME = 60000 * 60 // 60 minutes 652const USER_EMAIL_VERIFY_LIFETIME = 60000 * 60 // 60 minutes
643 653
644const NSFW_POLICY_TYPES: { [ id: string ]: NSFWPolicyType } = { 654const NSFW_POLICY_TYPES: { [ id: string ]: NSFWPolicyType } = {
@@ -652,10 +662,15 @@ const NSFW_POLICY_TYPES: { [ id: string ]: NSFWPolicyType } = {
652// Express static paths (router) 662// Express static paths (router)
653const STATIC_PATHS = { 663const STATIC_PATHS = {
654 THUMBNAILS: '/static/thumbnails/', 664 THUMBNAILS: '/static/thumbnails/',
665
655 WEBSEED: '/static/webseed/', 666 WEBSEED: '/static/webseed/',
667 PRIVATE_WEBSEED: '/static/webseed/private/',
668
656 REDUNDANCY: '/static/redundancy/', 669 REDUNDANCY: '/static/redundancy/',
670
657 STREAMING_PLAYLISTS: { 671 STREAMING_PLAYLISTS: {
658 HLS: '/static/streaming-playlists/hls' 672 HLS: '/static/streaming-playlists/hls',
673 PRIVATE_HLS: '/static/streaming-playlists/hls/private/'
659 } 674 }
660} 675}
661const STATIC_DOWNLOAD_PATHS = { 676const STATIC_DOWNLOAD_PATHS = {
@@ -670,6 +685,13 @@ const LAZY_STATIC_PATHS = {
670 VIDEO_CAPTIONS: '/lazy-static/video-captions/', 685 VIDEO_CAPTIONS: '/lazy-static/video-captions/',
671 TORRENTS: '/lazy-static/torrents/' 686 TORRENTS: '/lazy-static/torrents/'
672} 687}
688const OBJECT_STORAGE_PROXY_PATHS = {
689 PRIVATE_WEBSEED: '/object-storage-proxy/webseed/private/',
690
691 STREAMING_PLAYLISTS: {
692 PRIVATE_HLS: '/object-storage-proxy/streaming-playlists/hls/private/'
693 }
694}
673 695
674// Cache control 696// Cache control
675const STATIC_MAX_AGE = { 697const STATIC_MAX_AGE = {
@@ -689,7 +711,7 @@ const PREVIEWS_SIZE = {
689 height: 480, 711 height: 480,
690 minWidth: 400 712 minWidth: 400
691} 713}
692const ACTOR_IMAGES_SIZE: { [key in ActorImageType]: { width: number, height: number }[]} = { 714const ACTOR_IMAGES_SIZE: { [key in ActorImageType]: { width: number, height: number }[] } = {
693 [ActorImageType.AVATAR]: [ 715 [ActorImageType.AVATAR]: [
694 { 716 {
695 width: 120, 717 width: 120,
@@ -735,12 +757,32 @@ const LRU_CACHE = {
735 }, 757 },
736 ACTOR_IMAGE_STATIC: { 758 ACTOR_IMAGE_STATIC: {
737 MAX_SIZE: 500 759 MAX_SIZE: 500
760 },
761 STATIC_VIDEO_FILES_RIGHTS_CHECK: {
762 MAX_SIZE: 5000,
763 TTL: parseDurationToMs('10 seconds')
764 },
765 VIDEO_TOKENS: {
766 MAX_SIZE: 100_000,
767 TTL: parseDurationToMs('8 hours')
738 } 768 }
739} 769}
740 770
741const RESUMABLE_UPLOAD_DIRECTORY = join(CONFIG.STORAGE.TMP_DIR, 'resumable-uploads') 771const DIRECTORIES = {
742const HLS_STREAMING_PLAYLIST_DIRECTORY = join(CONFIG.STORAGE.STREAMING_PLAYLISTS_DIR, 'hls') 772 RESUMABLE_UPLOAD: join(CONFIG.STORAGE.TMP_DIR, 'resumable-uploads'),
743const HLS_REDUNDANCY_DIRECTORY = join(CONFIG.STORAGE.REDUNDANCY_DIR, 'hls') 773
774 HLS_STREAMING_PLAYLIST: {
775 PUBLIC: join(CONFIG.STORAGE.STREAMING_PLAYLISTS_DIR, 'hls'),
776 PRIVATE: join(CONFIG.STORAGE.STREAMING_PLAYLISTS_DIR, 'hls', 'private')
777 },
778
779 VIDEOS: {
780 PUBLIC: CONFIG.STORAGE.VIDEOS_DIR,
781 PRIVATE: join(CONFIG.STORAGE.VIDEOS_DIR, 'private')
782 },
783
784 HLS_REDUNDANCY: join(CONFIG.STORAGE.REDUNDANCY_DIR, 'hls')
785}
744 786
745const RESUMABLE_UPLOAD_SESSION_LIFETIME = SCHEDULER_INTERVALS_MS.REMOVE_DANGLING_RESUMABLE_UPLOADS 787const RESUMABLE_UPLOAD_SESSION_LIFETIME = SCHEDULER_INTERVALS_MS.REMOVE_DANGLING_RESUMABLE_UPLOADS
746 788
@@ -796,6 +838,10 @@ const REDUNDANCY = {
796} 838}
797 839
798const ACCEPT_HEADERS = [ 'html', 'application/json' ].concat(ACTIVITY_PUB.POTENTIAL_ACCEPT_HEADERS) 840const ACCEPT_HEADERS = [ 'html', 'application/json' ].concat(ACTIVITY_PUB.POTENTIAL_ACCEPT_HEADERS)
841const OTP = {
842 HEADER_NAME: 'x-peertube-otp',
843 HEADER_REQUIRED_VALUE: 'required; app'
844}
799 845
800const ASSETS_PATH = { 846const ASSETS_PATH = {
801 DEFAULT_AUDIO_BACKGROUND: join(root(), 'dist', 'server', 'assets', 'default-audio-background.jpg'), 847 DEFAULT_AUDIO_BACKGROUND: join(root(), 'dist', 'server', 'assets', 'default-audio-background.jpg'),
@@ -944,13 +990,14 @@ const VIDEO_FILTERS = {
944export { 990export {
945 WEBSERVER, 991 WEBSERVER,
946 API_VERSION, 992 API_VERSION,
993 ENCRYPTION,
947 VIDEO_LIVE, 994 VIDEO_LIVE,
948 PEERTUBE_VERSION, 995 PEERTUBE_VERSION,
949 LAZY_STATIC_PATHS, 996 LAZY_STATIC_PATHS,
997 OBJECT_STORAGE_PROXY_PATHS,
950 SEARCH_INDEX, 998 SEARCH_INDEX,
951 RESUMABLE_UPLOAD_DIRECTORY, 999 DIRECTORIES,
952 RESUMABLE_UPLOAD_SESSION_LIFETIME, 1000 RESUMABLE_UPLOAD_SESSION_LIFETIME,
953 HLS_REDUNDANCY_DIRECTORY,
954 P2P_MEDIA_LOADER_PEER_VERSION, 1001 P2P_MEDIA_LOADER_PEER_VERSION,
955 ACTOR_IMAGES_SIZE, 1002 ACTOR_IMAGES_SIZE,
956 ACCEPT_HEADERS, 1003 ACCEPT_HEADERS,
@@ -977,13 +1024,13 @@ export {
977 FOLLOW_STATES, 1024 FOLLOW_STATES,
978 DEFAULT_USER_THEME_NAME, 1025 DEFAULT_USER_THEME_NAME,
979 SERVER_ACTOR_NAME, 1026 SERVER_ACTOR_NAME,
1027 TWO_FACTOR_AUTH_REQUEST_TOKEN_LIFETIME,
980 PLUGIN_GLOBAL_CSS_FILE_NAME, 1028 PLUGIN_GLOBAL_CSS_FILE_NAME,
981 PLUGIN_GLOBAL_CSS_PATH, 1029 PLUGIN_GLOBAL_CSS_PATH,
982 PRIVATE_RSA_KEY_SIZE, 1030 PRIVATE_RSA_KEY_SIZE,
983 VIDEO_FILTERS, 1031 VIDEO_FILTERS,
984 ROUTE_CACHE_LIFETIME, 1032 ROUTE_CACHE_LIFETIME,
985 SORTABLE_COLUMNS, 1033 SORTABLE_COLUMNS,
986 HLS_STREAMING_PLAYLIST_DIRECTORY,
987 JOB_TTL, 1034 JOB_TTL,
988 DEFAULT_THEME_NAME, 1035 DEFAULT_THEME_NAME,
989 NSFW_POLICY_TYPES, 1036 NSFW_POLICY_TYPES,
@@ -1032,6 +1079,7 @@ export {
1032 PLUGIN_EXTERNAL_AUTH_TOKEN_LIFETIME, 1079 PLUGIN_EXTERNAL_AUTH_TOKEN_LIFETIME,
1033 ASSETS_PATH, 1080 ASSETS_PATH,
1034 FILES_CONTENT_HASH, 1081 FILES_CONTENT_HASH,
1082 OTP,
1035 loadLanguages, 1083 loadLanguages,
1036 buildLanguages, 1084 buildLanguages,
1037 generateContentHash 1085 generateContentHash
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts
index b02be9567..f5d8eedf1 100644
--- a/server/initializers/installer.ts
+++ b/server/initializers/installer.ts
@@ -10,7 +10,7 @@ import { ApplicationModel } from '../models/application/application'
10import { OAuthClientModel } from '../models/oauth/oauth-client' 10import { OAuthClientModel } from '../models/oauth/oauth-client'
11import { applicationExist, clientsExist, usersExist } from './checker-after-init' 11import { applicationExist, clientsExist, usersExist } from './checker-after-init'
12import { CONFIG } from './config' 12import { CONFIG } from './config'
13import { FILES_CACHE, HLS_STREAMING_PLAYLIST_DIRECTORY, LAST_MIGRATION_VERSION, RESUMABLE_UPLOAD_DIRECTORY } from './constants' 13import { DIRECTORIES, FILES_CACHE, LAST_MIGRATION_VERSION } from './constants'
14import { sequelizeTypescript } from './database' 14import { sequelizeTypescript } from './database'
15 15
16async function installApplication () { 16async function installApplication () {
@@ -92,11 +92,13 @@ function createDirectoriesIfNotExist () {
92 tasks.push(ensureDir(dir)) 92 tasks.push(ensureDir(dir))
93 } 93 }
94 94
95 // Playlist directories 95 tasks.push(ensureDir(DIRECTORIES.HLS_STREAMING_PLAYLIST.PRIVATE))
96 tasks.push(ensureDir(HLS_STREAMING_PLAYLIST_DIRECTORY)) 96 tasks.push(ensureDir(DIRECTORIES.HLS_STREAMING_PLAYLIST.PUBLIC))
97 tasks.push(ensureDir(DIRECTORIES.VIDEOS.PUBLIC))
98 tasks.push(ensureDir(DIRECTORIES.VIDEOS.PRIVATE))
97 99
98 // Resumable upload directory 100 // Resumable upload directory
99 tasks.push(ensureDir(RESUMABLE_UPLOAD_DIRECTORY)) 101 tasks.push(ensureDir(DIRECTORIES.RESUMABLE_UPLOAD))
100 102
101 return Promise.all(tasks) 103 return Promise.all(tasks)
102} 104}
diff --git a/server/initializers/migrations/0745-user-otp.ts b/server/initializers/migrations/0745-user-otp.ts
new file mode 100644
index 000000000..157308ea1
--- /dev/null
+++ b/server/initializers/migrations/0745-user-otp.ts
@@ -0,0 +1,29 @@
1import * as Sequelize from 'sequelize'
2
3async function up (utils: {
4 transaction: Sequelize.Transaction
5 queryInterface: Sequelize.QueryInterface
6 sequelize: Sequelize.Sequelize
7 db: any
8}): Promise<void> {
9 const { transaction } = utils
10
11 const data = {
12 type: Sequelize.STRING,
13 defaultValue: null,
14 allowNull: true
15 }
16 await utils.queryInterface.addColumn('user', 'otpSecret', data, { transaction })
17
18}
19
20async function down (utils: {
21 queryInterface: Sequelize.QueryInterface
22 transaction: Sequelize.Transaction
23}) {
24}
25
26export {
27 up,
28 down
29}