diff options
author | Chocobozzz <me@florianbigard.com> | 2019-02-11 11:52:34 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2019-02-11 11:52:34 +0100 |
commit | 88108880bbdba473cfe36ecbebc1c3c4f972e102 (patch) | |
tree | b242efb3b4f0d7e49d88f2d1f2063b5b3b0489c0 /server/initializers | |
parent | 53a94c7cfa8368da4cd248d65df8346905938f0c (diff) | |
parent | 9b712a2017e4ab3cf12cd6bd58278905520159d0 (diff) | |
download | PeerTube-88108880bbdba473cfe36ecbebc1c3c4f972e102.tar.gz PeerTube-88108880bbdba473cfe36ecbebc1c3c4f972e102.tar.zst PeerTube-88108880bbdba473cfe36ecbebc1c3c4f972e102.zip |
Merge branch 'develop' into pr/1217
Diffstat (limited to 'server/initializers')
23 files changed, 671 insertions, 99 deletions
diff --git a/server/initializers/checker-after-init.ts b/server/initializers/checker-after-init.ts index 72d846957..955d55206 100644 --- a/server/initializers/checker-after-init.ts +++ b/server/initializers/checker-after-init.ts | |||
@@ -10,6 +10,7 @@ import { getServerActor } from '../helpers/utils' | |||
10 | import { RecentlyAddedStrategy } from '../../shared/models/redundancy' | 10 | import { RecentlyAddedStrategy } from '../../shared/models/redundancy' |
11 | import { isArray } from '../helpers/custom-validators/misc' | 11 | import { isArray } from '../helpers/custom-validators/misc' |
12 | import { uniq } from 'lodash' | 12 | import { uniq } from 'lodash' |
13 | import { Emailer } from '../lib/emailer' | ||
13 | 14 | ||
14 | async function checkActivityPubUrls () { | 15 | async function checkActivityPubUrls () { |
15 | const actor = await getServerActor() | 16 | const actor = await getServerActor() |
@@ -32,9 +33,19 @@ async function checkActivityPubUrls () { | |||
32 | // Some checks on configuration files | 33 | // Some checks on configuration files |
33 | // Return an error message, or null if everything is okay | 34 | // Return an error message, or null if everything is okay |
34 | function checkConfig () { | 35 | function checkConfig () { |
35 | const defaultNSFWPolicy = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY | 36 | |
37 | if (!Emailer.isEnabled()) { | ||
38 | if (CONFIG.SIGNUP.ENABLED && CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { | ||
39 | return 'Emailer is disabled but you require signup email verification.' | ||
40 | } | ||
41 | |||
42 | if (CONFIG.CONTACT_FORM.ENABLED) { | ||
43 | logger.warn('Emailer is disabled so the contact form will not work.') | ||
44 | } | ||
45 | } | ||
36 | 46 | ||
37 | // NSFW policy | 47 | // NSFW policy |
48 | const defaultNSFWPolicy = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY | ||
38 | { | 49 | { |
39 | const available = [ 'do_not_list', 'blur', 'display' ] | 50 | const available = [ 'do_not_list', 'blur', 'display' ] |
40 | if (available.indexOf(defaultNSFWPolicy) === -1) { | 51 | if (available.indexOf(defaultNSFWPolicy) === -1) { |
@@ -68,6 +79,7 @@ function checkConfig () { | |||
68 | } | 79 | } |
69 | } | 80 | } |
70 | 81 | ||
82 | // Check storage directory locations | ||
71 | if (isProdInstance()) { | 83 | if (isProdInstance()) { |
72 | const configStorage = config.get('storage') | 84 | const configStorage = config.get('storage') |
73 | for (const key of Object.keys(configStorage)) { | 85 | for (const key of Object.keys(configStorage)) { |
diff --git a/server/initializers/checker-before-init.ts b/server/initializers/checker-before-init.ts index 4f46d406a..29fdb263e 100644 --- a/server/initializers/checker-before-init.ts +++ b/server/initializers/checker-before-init.ts | |||
@@ -12,13 +12,14 @@ function checkMissedConfig () { | |||
12 | 'database.hostname', 'database.port', 'database.suffix', 'database.username', 'database.password', 'database.pool.max', | 12 | 'database.hostname', 'database.port', 'database.suffix', 'database.username', 'database.password', 'database.pool.max', |
13 | 'smtp.hostname', 'smtp.port', 'smtp.username', 'smtp.password', 'smtp.tls', 'smtp.from_address', | 13 | 'smtp.hostname', 'smtp.port', 'smtp.username', 'smtp.password', 'smtp.tls', 'smtp.from_address', |
14 | 'storage.avatars', 'storage.videos', 'storage.logs', 'storage.previews', 'storage.thumbnails', 'storage.torrents', 'storage.cache', | 14 | 'storage.avatars', 'storage.videos', 'storage.logs', 'storage.previews', 'storage.thumbnails', 'storage.torrents', 'storage.cache', |
15 | 'storage.redundancy', 'storage.tmp', 'storage.playlists', | ||
15 | 'log.level', | 16 | 'log.level', |
16 | 'user.video_quota', 'user.video_quota_daily', | 17 | 'user.video_quota', 'user.video_quota_daily', |
17 | 'cache.previews.size', 'admin.email', | 18 | 'cache.previews.size', 'admin.email', 'contact_form.enabled', |
18 | 'signup.enabled', 'signup.limit', 'signup.requires_email_verification', | 19 | 'signup.enabled', 'signup.limit', 'signup.requires_email_verification', |
19 | 'signup.filters.cidr.whitelist', 'signup.filters.cidr.blacklist', | 20 | 'signup.filters.cidr.whitelist', 'signup.filters.cidr.blacklist', |
20 | 'redundancy.videos.strategies', 'redundancy.videos.check_interval', | 21 | 'redundancy.videos.strategies', 'redundancy.videos.check_interval', |
21 | 'transcoding.enabled', 'transcoding.threads', | 22 | 'transcoding.enabled', 'transcoding.threads', 'transcoding.allow_additional_extensions', |
22 | 'import.videos.http.enabled', 'import.videos.torrent.enabled', | 23 | 'import.videos.http.enabled', 'import.videos.torrent.enabled', |
23 | 'trending.videos.interval_days', | 24 | 'trending.videos.interval_days', |
24 | 'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route', | 25 | 'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route', |
@@ -77,7 +78,7 @@ async function checkFFmpeg (CONFIG: { TRANSCODING: { ENABLED: boolean } }) { | |||
77 | } | 78 | } |
78 | } | 79 | } |
79 | 80 | ||
80 | checkFFmpegEncoders() | 81 | return checkFFmpegEncoders() |
81 | } | 82 | } |
82 | 83 | ||
83 | // Optional encoders, if present, can be used to improve transcoding | 84 | // Optional encoders, if present, can be used to improve transcoding |
@@ -95,10 +96,10 @@ async function checkFFmpegEncoders (): Promise<Map<string, boolean>> { | |||
95 | supportedOptionalEncoders = new Map<string, boolean>() | 96 | supportedOptionalEncoders = new Map<string, boolean>() |
96 | 97 | ||
97 | for (const encoder of optionalEncoders) { | 98 | for (const encoder of optionalEncoders) { |
98 | supportedOptionalEncoders.set(encoder, | 99 | supportedOptionalEncoders.set(encoder, encoders[encoder] !== undefined) |
99 | encoders[encoder] !== undefined | ||
100 | ) | ||
101 | } | 100 | } |
101 | |||
102 | return supportedOptionalEncoders | ||
102 | } | 103 | } |
103 | 104 | ||
104 | // --------------------------------------------------------------------------- | 105 | // --------------------------------------------------------------------------- |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 1a3b52015..e5c4c4e63 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -3,9 +3,9 @@ import { dirname, join } from 'path' | |||
3 | import { JobType, VideoRateType, VideoState, VideosRedundancy } from '../../shared/models' | 3 | import { JobType, VideoRateType, VideoState, VideosRedundancy } from '../../shared/models' |
4 | import { ActivityPubActorType } from '../../shared/models/activitypub' | 4 | import { ActivityPubActorType } from '../../shared/models/activitypub' |
5 | import { FollowState } from '../../shared/models/actors' | 5 | import { FollowState } from '../../shared/models/actors' |
6 | import { VideoAbuseState, VideoImportState, VideoPrivacy } from '../../shared/models/videos' | 6 | import { VideoAbuseState, VideoImportState, VideoPrivacy, VideoTranscodingFPS } from '../../shared/models/videos' |
7 | // Do not use barrels, remain constants as independent as possible | 7 | // Do not use barrels, remain constants as independent as possible |
8 | import { buildPath, isTestInstance, parseDuration, root, sanitizeHost, sanitizeUrl } from '../helpers/core-utils' | 8 | import { buildPath, isTestInstance, parseDuration, parseBytes, root, sanitizeHost, sanitizeUrl } from '../helpers/core-utils' |
9 | import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type' | 9 | import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type' |
10 | import { invert } from 'lodash' | 10 | import { invert } from 'lodash' |
11 | import { CronRepeatOptions, EveryRepeatOptions } from 'bull' | 11 | import { CronRepeatOptions, EveryRepeatOptions } from 'bull' |
@@ -16,7 +16,7 @@ let config: IConfig = require('config') | |||
16 | 16 | ||
17 | // --------------------------------------------------------------------------- | 17 | // --------------------------------------------------------------------------- |
18 | 18 | ||
19 | const LAST_MIGRATION_VERSION = 275 | 19 | const LAST_MIGRATION_VERSION = 330 |
20 | 20 | ||
21 | // --------------------------------------------------------------------------- | 21 | // --------------------------------------------------------------------------- |
22 | 22 | ||
@@ -47,7 +47,12 @@ const SORTABLE_COLUMNS = { | |||
47 | VIDEOS: [ 'name', 'duration', 'createdAt', 'publishedAt', 'views', 'likes', 'trending' ], | 47 | VIDEOS: [ 'name', 'duration', 'createdAt', 'publishedAt', 'views', 'likes', 'trending' ], |
48 | 48 | ||
49 | VIDEOS_SEARCH: [ 'name', 'duration', 'createdAt', 'publishedAt', 'views', 'likes', 'match' ], | 49 | VIDEOS_SEARCH: [ 'name', 'duration', 'createdAt', 'publishedAt', 'views', 'likes', 'match' ], |
50 | VIDEO_CHANNELS_SEARCH: [ 'match', 'displayName', 'createdAt' ] | 50 | VIDEO_CHANNELS_SEARCH: [ 'match', 'displayName', 'createdAt' ], |
51 | |||
52 | ACCOUNTS_BLOCKLIST: [ 'createdAt' ], | ||
53 | SERVERS_BLOCKLIST: [ 'createdAt' ], | ||
54 | |||
55 | USER_NOTIFICATIONS: [ 'createdAt' ] | ||
51 | } | 56 | } |
52 | 57 | ||
53 | const OAUTH_LIFETIME = { | 58 | const OAUTH_LIFETIME = { |
@@ -58,6 +63,7 @@ const OAUTH_LIFETIME = { | |||
58 | const ROUTE_CACHE_LIFETIME = { | 63 | const ROUTE_CACHE_LIFETIME = { |
59 | FEEDS: '15 minutes', | 64 | FEEDS: '15 minutes', |
60 | ROBOTS: '2 hours', | 65 | ROBOTS: '2 hours', |
66 | SITEMAP: '1 day', | ||
61 | SECURITYTXT: '2 hours', | 67 | SECURITYTXT: '2 hours', |
62 | NODEINFO: '10 minutes', | 68 | NODEINFO: '10 minutes', |
63 | DNT_POLICY: '1 week', | 69 | DNT_POLICY: '1 week', |
@@ -99,7 +105,8 @@ const JOB_ATTEMPTS: { [ id in JobType ]: number } = { | |||
99 | 'video-file': 1, | 105 | 'video-file': 1, |
100 | 'video-import': 1, | 106 | 'video-import': 1, |
101 | 'email': 5, | 107 | 'email': 5, |
102 | 'videos-views': 1 | 108 | 'videos-views': 1, |
109 | 'activitypub-refresher': 1 | ||
103 | } | 110 | } |
104 | const JOB_CONCURRENCY: { [ id in JobType ]: number } = { | 111 | const JOB_CONCURRENCY: { [ id in JobType ]: number } = { |
105 | 'activitypub-http-broadcast': 1, | 112 | 'activitypub-http-broadcast': 1, |
@@ -110,7 +117,8 @@ const JOB_CONCURRENCY: { [ id in JobType ]: number } = { | |||
110 | 'video-file': 1, | 117 | 'video-file': 1, |
111 | 'video-import': 1, | 118 | 'video-import': 1, |
112 | 'email': 5, | 119 | 'email': 5, |
113 | 'videos-views': 1 | 120 | 'videos-views': 1, |
121 | 'activitypub-refresher': 1 | ||
114 | } | 122 | } |
115 | const JOB_TTL: { [ id in JobType ]: number } = { | 123 | const JOB_TTL: { [ id in JobType ]: number } = { |
116 | 'activitypub-http-broadcast': 60000 * 10, // 10 minutes | 124 | 'activitypub-http-broadcast': 60000 * 10, // 10 minutes |
@@ -121,11 +129,12 @@ const JOB_TTL: { [ id in JobType ]: number } = { | |||
121 | 'video-file': 1000 * 3600 * 48, // 2 days, transcoding could be long | 129 | 'video-file': 1000 * 3600 * 48, // 2 days, transcoding could be long |
122 | 'video-import': 1000 * 3600 * 2, // hours | 130 | 'video-import': 1000 * 3600 * 2, // hours |
123 | 'email': 60000 * 10, // 10 minutes | 131 | 'email': 60000 * 10, // 10 minutes |
124 | 'videos-views': undefined // Unlimited | 132 | 'videos-views': undefined, // Unlimited |
133 | 'activitypub-refresher': 60000 * 10 // 10 minutes | ||
125 | } | 134 | } |
126 | const REPEAT_JOBS: { [ id: string ]: EveryRepeatOptions | CronRepeatOptions } = { | 135 | const REPEAT_JOBS: { [ id: string ]: EveryRepeatOptions | CronRepeatOptions } = { |
127 | 'videos-views': { | 136 | 'videos-views': { |
128 | cron: '1 * * * *' // At 1 minutes past the hour | 137 | cron: '1 * * * *' // At 1 minute past the hour |
129 | } | 138 | } |
130 | } | 139 | } |
131 | 140 | ||
@@ -137,7 +146,7 @@ const VIDEO_IMPORT_TIMEOUT = 1000 * 3600 // 1 hour | |||
137 | 146 | ||
138 | // 1 hour | 147 | // 1 hour |
139 | let SCHEDULER_INTERVALS_MS = { | 148 | let SCHEDULER_INTERVALS_MS = { |
140 | badActorFollow: 60000 * 60, // 1 hour | 149 | actorFollowScores: 60000 * 60, // 1 hour |
141 | removeOldJobs: 60000 * 60, // 1 hour | 150 | removeOldJobs: 60000 * 60, // 1 hour |
142 | updateVideos: 60000, // 1 minute | 151 | updateVideos: 60000, // 1 minute |
143 | youtubeDLUpdate: 60000 * 60 * 24 // 1 day | 152 | youtubeDLUpdate: 60000 * 60 * 24 // 1 day |
@@ -179,9 +188,12 @@ const CONFIG = { | |||
179 | FROM_ADDRESS: config.get<string>('smtp.from_address') | 188 | FROM_ADDRESS: config.get<string>('smtp.from_address') |
180 | }, | 189 | }, |
181 | STORAGE: { | 190 | STORAGE: { |
191 | TMP_DIR: buildPath(config.get<string>('storage.tmp')), | ||
182 | AVATARS_DIR: buildPath(config.get<string>('storage.avatars')), | 192 | AVATARS_DIR: buildPath(config.get<string>('storage.avatars')), |
183 | LOG_DIR: buildPath(config.get<string>('storage.logs')), | 193 | LOG_DIR: buildPath(config.get<string>('storage.logs')), |
184 | VIDEOS_DIR: buildPath(config.get<string>('storage.videos')), | 194 | VIDEOS_DIR: buildPath(config.get<string>('storage.videos')), |
195 | PLAYLISTS_DIR: buildPath(config.get<string>('storage.playlists')), | ||
196 | REDUNDANCY_DIR: buildPath(config.get<string>('storage.redundancy')), | ||
185 | THUMBNAILS_DIR: buildPath(config.get<string>('storage.thumbnails')), | 197 | THUMBNAILS_DIR: buildPath(config.get<string>('storage.thumbnails')), |
186 | PREVIEWS_DIR: buildPath(config.get<string>('storage.previews')), | 198 | PREVIEWS_DIR: buildPath(config.get<string>('storage.previews')), |
187 | CAPTIONS_DIR: buildPath(config.get<string>('storage.captions')), | 199 | CAPTIONS_DIR: buildPath(config.get<string>('storage.captions')), |
@@ -220,6 +232,9 @@ const CONFIG = { | |||
220 | ADMIN: { | 232 | ADMIN: { |
221 | get EMAIL () { return config.get<string>('admin.email') } | 233 | get EMAIL () { return config.get<string>('admin.email') } |
222 | }, | 234 | }, |
235 | CONTACT_FORM: { | ||
236 | get ENABLED () { return config.get<boolean>('contact_form.enabled') } | ||
237 | }, | ||
223 | SIGNUP: { | 238 | SIGNUP: { |
224 | get ENABLED () { return config.get<boolean>('signup.enabled') }, | 239 | get ENABLED () { return config.get<boolean>('signup.enabled') }, |
225 | get LIMIT () { return config.get<number>('signup.limit') }, | 240 | get LIMIT () { return config.get<number>('signup.limit') }, |
@@ -232,11 +247,12 @@ const CONFIG = { | |||
232 | } | 247 | } |
233 | }, | 248 | }, |
234 | USER: { | 249 | USER: { |
235 | get VIDEO_QUOTA () { return config.get<number>('user.video_quota') }, | 250 | get VIDEO_QUOTA () { return parseBytes(config.get<number>('user.video_quota')) }, |
236 | get VIDEO_QUOTA_DAILY () { return config.get<number>('user.video_quota_daily') } | 251 | get VIDEO_QUOTA_DAILY () { return parseBytes(config.get<number>('user.video_quota_daily')) } |
237 | }, | 252 | }, |
238 | TRANSCODING: { | 253 | TRANSCODING: { |
239 | get ENABLED () { return config.get<boolean>('transcoding.enabled') }, | 254 | get ENABLED () { return config.get<boolean>('transcoding.enabled') }, |
255 | get ALLOW_ADDITIONAL_EXTENSIONS () { return config.get<boolean>('transcoding.allow_additional_extensions') }, | ||
240 | get THREADS () { return config.get<number>('transcoding.threads') }, | 256 | get THREADS () { return config.get<number>('transcoding.threads') }, |
241 | RESOLUTIONS: { | 257 | RESOLUTIONS: { |
242 | get '240p' () { return config.get<boolean>('transcoding.resolutions.240p') }, | 258 | get '240p' () { return config.get<boolean>('transcoding.resolutions.240p') }, |
@@ -244,6 +260,9 @@ const CONFIG = { | |||
244 | get '480p' () { return config.get<boolean>('transcoding.resolutions.480p') }, | 260 | get '480p' () { return config.get<boolean>('transcoding.resolutions.480p') }, |
245 | get '720p' () { return config.get<boolean>('transcoding.resolutions.720p') }, | 261 | get '720p' () { return config.get<boolean>('transcoding.resolutions.720p') }, |
246 | get '1080p' () { return config.get<boolean>('transcoding.resolutions.1080p') } | 262 | get '1080p' () { return config.get<boolean>('transcoding.resolutions.1080p') } |
263 | }, | ||
264 | HLS: { | ||
265 | get ENABLED () { return config.get<boolean>('transcoding.hls.enabled') } | ||
247 | } | 266 | } |
248 | }, | 267 | }, |
249 | IMPORT: { | 268 | IMPORT: { |
@@ -280,6 +299,7 @@ const CONFIG = { | |||
280 | get SECURITYTXT_CONTACT () { return config.get<string>('admin.email') } | 299 | get SECURITYTXT_CONTACT () { return config.get<string>('admin.email') } |
281 | }, | 300 | }, |
282 | SERVICES: { | 301 | SERVICES: { |
302 | get 'CSP-LOGGER' () { return config.get<string>('services.csp-logger') }, | ||
283 | TWITTER: { | 303 | TWITTER: { |
284 | get USERNAME () { return config.get<string>('services.twitter.username') }, | 304 | get USERNAME () { return config.get<string>('services.twitter.username') }, |
285 | get WHITELISTED () { return config.get<boolean>('services.twitter.whitelisted') } | 305 | get WHITELISTED () { return config.get<boolean>('services.twitter.whitelisted') } |
@@ -289,27 +309,27 @@ const CONFIG = { | |||
289 | 309 | ||
290 | // --------------------------------------------------------------------------- | 310 | // --------------------------------------------------------------------------- |
291 | 311 | ||
292 | const CONSTRAINTS_FIELDS = { | 312 | let CONSTRAINTS_FIELDS = { |
293 | USERS: { | 313 | USERS: { |
294 | NAME: { min: 3, max: 120 }, // Length | 314 | NAME: { min: 1, max: 120 }, // Length |
295 | DESCRIPTION: { min: 3, max: 250 }, // Length | 315 | DESCRIPTION: { min: 3, max: 1000 }, // Length |
296 | USERNAME: { min: 3, max: 20 }, // Length | 316 | USERNAME: { min: 1, max: 50 }, // Length |
297 | PASSWORD: { min: 6, max: 255 }, // Length | 317 | PASSWORD: { min: 6, max: 255 }, // Length |
298 | VIDEO_QUOTA: { min: -1 }, | 318 | VIDEO_QUOTA: { min: -1 }, |
299 | VIDEO_QUOTA_DAILY: { min: -1 }, | 319 | VIDEO_QUOTA_DAILY: { min: -1 }, |
300 | BLOCKED_REASON: { min: 3, max: 250 } // Length | 320 | BLOCKED_REASON: { min: 3, max: 250 } // Length |
301 | }, | 321 | }, |
302 | VIDEO_ABUSES: { | 322 | VIDEO_ABUSES: { |
303 | REASON: { min: 2, max: 300 }, // Length | 323 | REASON: { min: 2, max: 3000 }, // Length |
304 | MODERATION_COMMENT: { min: 2, max: 300 } // Length | 324 | MODERATION_COMMENT: { min: 2, max: 3000 } // Length |
305 | }, | 325 | }, |
306 | VIDEO_BLACKLIST: { | 326 | VIDEO_BLACKLIST: { |
307 | REASON: { min: 2, max: 300 } // Length | 327 | REASON: { min: 2, max: 300 } // Length |
308 | }, | 328 | }, |
309 | VIDEO_CHANNELS: { | 329 | VIDEO_CHANNELS: { |
310 | NAME: { min: 3, max: 120 }, // Length | 330 | NAME: { min: 1, max: 120 }, // Length |
311 | DESCRIPTION: { min: 3, max: 500 }, // Length | 331 | DESCRIPTION: { min: 3, max: 1000 }, // Length |
312 | SUPPORT: { min: 3, max: 500 }, // Length | 332 | SUPPORT: { min: 3, max: 1000 }, // Length |
313 | URL: { min: 3, max: 2000 } // Length | 333 | URL: { min: 3, max: 2000 } // Length |
314 | }, | 334 | }, |
315 | VIDEO_CAPTIONS: { | 335 | VIDEO_CAPTIONS: { |
@@ -333,19 +353,22 @@ const CONSTRAINTS_FIELDS = { | |||
333 | VIDEOS_REDUNDANCY: { | 353 | VIDEOS_REDUNDANCY: { |
334 | URL: { min: 3, max: 2000 } // Length | 354 | URL: { min: 3, max: 2000 } // Length |
335 | }, | 355 | }, |
356 | VIDEO_RATES: { | ||
357 | URL: { min: 3, max: 2000 } // Length | ||
358 | }, | ||
336 | VIDEOS: { | 359 | VIDEOS: { |
337 | NAME: { min: 3, max: 120 }, // Length | 360 | NAME: { min: 3, max: 120 }, // Length |
338 | LANGUAGE: { min: 1, max: 10 }, // Length | 361 | LANGUAGE: { min: 1, max: 10 }, // Length |
339 | TRUNCATED_DESCRIPTION: { min: 3, max: 250 }, // Length | 362 | TRUNCATED_DESCRIPTION: { min: 3, max: 250 }, // Length |
340 | DESCRIPTION: { min: 3, max: 10000 }, // Length | 363 | DESCRIPTION: { min: 3, max: 10000 }, // Length |
341 | SUPPORT: { min: 3, max: 500 }, // Length | 364 | SUPPORT: { min: 3, max: 1000 }, // Length |
342 | IMAGE: { | 365 | IMAGE: { |
343 | EXTNAME: [ '.jpg', '.jpeg' ], | 366 | EXTNAME: [ '.jpg', '.jpeg' ], |
344 | FILE_SIZE: { | 367 | FILE_SIZE: { |
345 | max: 2 * 1024 * 1024 // 2MB | 368 | max: 2 * 1024 * 1024 // 2MB |
346 | } | 369 | } |
347 | }, | 370 | }, |
348 | EXTNAME: [ '.mp4', '.ogv', '.webm' ], | 371 | EXTNAME: buildVideosExtname(), |
349 | INFO_HASH: { min: 40, max: 40 }, // Length, info hash is 20 bytes length but we represent it in hexadecimal so 20 * 2 | 372 | INFO_HASH: { min: 40, max: 40 }, // Length, info hash is 20 bytes length but we represent it in hexadecimal so 20 * 2 |
350 | DURATION: { min: 0 }, // Number | 373 | DURATION: { min: 0 }, // Number |
351 | TAGS: { min: 0, max: 5 }, // Number of total tags | 374 | TAGS: { min: 0, max: 5 }, // Number of total tags |
@@ -378,6 +401,10 @@ const CONSTRAINTS_FIELDS = { | |||
378 | }, | 401 | }, |
379 | VIDEO_SHARE: { | 402 | VIDEO_SHARE: { |
380 | URL: { min: 3, max: 2000 } // Length | 403 | URL: { min: 3, max: 2000 } // Length |
404 | }, | ||
405 | CONTACT_FORM: { | ||
406 | FROM_NAME: { min: 1, max: 120 }, // Length | ||
407 | BODY: { min: 3, max: 5000 } // Length | ||
381 | } | 408 | } |
382 | } | 409 | } |
383 | 410 | ||
@@ -393,7 +420,9 @@ const RATES_LIMIT = { | |||
393 | } | 420 | } |
394 | 421 | ||
395 | let VIDEO_VIEW_LIFETIME = 60000 * 60 // 1 hour | 422 | let VIDEO_VIEW_LIFETIME = 60000 * 60 // 1 hour |
396 | const VIDEO_TRANSCODING_FPS = { | 423 | let CONTACT_FORM_LIFETIME = 60000 * 60 // 1 hour |
424 | |||
425 | const VIDEO_TRANSCODING_FPS: VideoTranscodingFPS = { | ||
397 | MIN: 10, | 426 | MIN: 10, |
398 | AVERAGE: 30, | 427 | AVERAGE: 30, |
399 | MAX: 60, | 428 | MAX: 60, |
@@ -421,7 +450,7 @@ const VIDEO_CATEGORIES = { | |||
421 | 8: 'People', | 450 | 8: 'People', |
422 | 9: 'Comedy', | 451 | 9: 'Comedy', |
423 | 10: 'Entertainment', | 452 | 10: 'Entertainment', |
424 | 11: 'News', | 453 | 11: 'News & Politics', |
425 | 12: 'How To', | 454 | 12: 'How To', |
426 | 13: 'Education', | 455 | 13: 'Education', |
427 | 14: 'Activism', | 456 | 14: 'Activism', |
@@ -468,27 +497,31 @@ const VIDEO_ABUSE_STATES = { | |||
468 | [VideoAbuseState.ACCEPTED]: 'Accepted' | 497 | [VideoAbuseState.ACCEPTED]: 'Accepted' |
469 | } | 498 | } |
470 | 499 | ||
471 | const VIDEO_MIMETYPE_EXT = { | 500 | const MIMETYPES = { |
472 | 'video/webm': '.webm', | 501 | VIDEO: { |
473 | 'video/ogg': '.ogv', | 502 | MIMETYPE_EXT: buildVideoMimetypeExt(), |
474 | 'video/mp4': '.mp4' | 503 | EXT_MIMETYPE: null as { [ id: string ]: string } |
475 | } | 504 | }, |
476 | const VIDEO_EXT_MIMETYPE = invert(VIDEO_MIMETYPE_EXT) | 505 | IMAGE: { |
477 | 506 | MIMETYPE_EXT: { | |
478 | const IMAGE_MIMETYPE_EXT = { | 507 | 'image/png': '.png', |
479 | 'image/png': '.png', | 508 | 'image/jpg': '.jpg', |
480 | 'image/jpg': '.jpg', | 509 | 'image/jpeg': '.jpg' |
481 | 'image/jpeg': '.jpg' | 510 | } |
482 | } | 511 | }, |
483 | 512 | VIDEO_CAPTIONS: { | |
484 | const VIDEO_CAPTIONS_MIMETYPE_EXT = { | 513 | MIMETYPE_EXT: { |
485 | 'text/vtt': '.vtt', | 514 | 'text/vtt': '.vtt', |
486 | 'application/x-subrip': '.srt' | 515 | 'application/x-subrip': '.srt' |
487 | } | 516 | } |
488 | 517 | }, | |
489 | const TORRENT_MIMETYPE_EXT = { | 518 | TORRENT: { |
490 | 'application/x-bittorrent': '.torrent' | 519 | MIMETYPE_EXT: { |
520 | 'application/x-bittorrent': '.torrent' | ||
521 | } | ||
522 | } | ||
491 | } | 523 | } |
524 | MIMETYPES.VIDEO.EXT_MIMETYPE = invert(MIMETYPES.VIDEO.MIMETYPE_EXT) | ||
492 | 525 | ||
493 | // --------------------------------------------------------------------------- | 526 | // --------------------------------------------------------------------------- |
494 | 527 | ||
@@ -514,7 +547,7 @@ const ACTIVITY_PUB = { | |||
514 | COLLECTION_ITEMS_PER_PAGE: 10, | 547 | COLLECTION_ITEMS_PER_PAGE: 10, |
515 | FETCH_PAGE_LIMIT: 100, | 548 | FETCH_PAGE_LIMIT: 100, |
516 | URL_MIME_TYPES: { | 549 | URL_MIME_TYPES: { |
517 | VIDEO: Object.keys(VIDEO_MIMETYPE_EXT), | 550 | VIDEO: Object.keys(MIMETYPES.VIDEO.MIMETYPE_EXT), |
518 | TORRENT: [ 'application/x-bittorrent' ], | 551 | TORRENT: [ 'application/x-bittorrent' ], |
519 | MAGNET: [ 'application/x-bittorrent;x-scheme-handler/magnet' ] | 552 | MAGNET: [ 'application/x-bittorrent;x-scheme-handler/magnet' ] |
520 | }, | 553 | }, |
@@ -529,9 +562,15 @@ const ACTIVITY_PUB_ACTOR_TYPES: { [ id: string ]: ActivityPubActorType } = { | |||
529 | APPLICATION: 'Application' | 562 | APPLICATION: 'Application' |
530 | } | 563 | } |
531 | 564 | ||
565 | const HTTP_SIGNATURE = { | ||
566 | HEADER_NAME: 'signature', | ||
567 | ALGORITHM: 'rsa-sha256', | ||
568 | HEADERS_TO_SIGN: [ '(request-target)', 'host', 'date', 'digest' ] | ||
569 | } | ||
570 | |||
532 | // --------------------------------------------------------------------------- | 571 | // --------------------------------------------------------------------------- |
533 | 572 | ||
534 | const PRIVATE_RSA_KEY_SIZE = 2048 | 573 | let PRIVATE_RSA_KEY_SIZE = 2048 |
535 | 574 | ||
536 | // Password encryption | 575 | // Password encryption |
537 | const BCRYPT_SALT_SIZE = 10 | 576 | const BCRYPT_SALT_SIZE = 10 |
@@ -554,6 +593,10 @@ const STATIC_PATHS = { | |||
554 | THUMBNAILS: '/static/thumbnails/', | 593 | THUMBNAILS: '/static/thumbnails/', |
555 | TORRENTS: '/static/torrents/', | 594 | TORRENTS: '/static/torrents/', |
556 | WEBSEED: '/static/webseed/', | 595 | WEBSEED: '/static/webseed/', |
596 | REDUNDANCY: '/static/redundancy/', | ||
597 | PLAYLISTS: { | ||
598 | HLS: '/static/playlists/hls' | ||
599 | }, | ||
557 | AVATARS: '/static/avatars/', | 600 | AVATARS: '/static/avatars/', |
558 | VIDEO_CAPTIONS: '/static/video-captions/' | 601 | VIDEO_CAPTIONS: '/static/video-captions/' |
559 | } | 602 | } |
@@ -596,6 +639,9 @@ const CACHE = { | |||
596 | } | 639 | } |
597 | } | 640 | } |
598 | 641 | ||
642 | const HLS_PLAYLIST_DIRECTORY = join(CONFIG.STORAGE.PLAYLISTS_DIR, 'hls') | ||
643 | const HLS_REDUNDANCY_DIRECTORY = join(CONFIG.STORAGE.REDUNDANCY_DIR, 'hls') | ||
644 | |||
599 | const MEMOIZE_TTL = { | 645 | const MEMOIZE_TTL = { |
600 | OVERVIEWS_SAMPLE: 1000 * 3600 * 4 // 4 hours | 646 | OVERVIEWS_SAMPLE: 1000 * 3600 * 4 // 4 hours |
601 | } | 647 | } |
@@ -635,6 +681,8 @@ const TRACKER_RATE_LIMITS = { | |||
635 | 681 | ||
636 | // Special constants for a test instance | 682 | // Special constants for a test instance |
637 | if (isTestInstance() === true) { | 683 | if (isTestInstance() === true) { |
684 | PRIVATE_RSA_KEY_SIZE = 1024 | ||
685 | |||
638 | ACTOR_FOLLOW_SCORE.BASE = 20 | 686 | ACTOR_FOLLOW_SCORE.BASE = 20 |
639 | 687 | ||
640 | REMOTE_SCHEME.HTTP = 'http' | 688 | REMOTE_SCHEME.HTTP = 'http' |
@@ -648,7 +696,7 @@ if (isTestInstance() === true) { | |||
648 | 696 | ||
649 | CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max = 100 * 1024 // 100KB | 697 | CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max = 100 * 1024 // 100KB |
650 | 698 | ||
651 | SCHEDULER_INTERVALS_MS.badActorFollow = 10000 | 699 | SCHEDULER_INTERVALS_MS.actorFollowScores = 1000 |
652 | SCHEDULER_INTERVALS_MS.removeOldJobs = 10000 | 700 | SCHEDULER_INTERVALS_MS.removeOldJobs = 10000 |
653 | SCHEDULER_INTERVALS_MS.updateVideos = 5000 | 701 | SCHEDULER_INTERVALS_MS.updateVideos = 5000 |
654 | REPEAT_JOBS['videos-views'] = { every: 5000 } | 702 | REPEAT_JOBS['videos-views'] = { every: 5000 } |
@@ -656,21 +704,24 @@ if (isTestInstance() === true) { | |||
656 | REDUNDANCY.VIDEOS.RANDOMIZED_FACTOR = 1 | 704 | REDUNDANCY.VIDEOS.RANDOMIZED_FACTOR = 1 |
657 | 705 | ||
658 | VIDEO_VIEW_LIFETIME = 1000 // 1 second | 706 | VIDEO_VIEW_LIFETIME = 1000 // 1 second |
707 | CONTACT_FORM_LIFETIME = 1000 // 1 second | ||
659 | 708 | ||
660 | JOB_ATTEMPTS['email'] = 1 | 709 | JOB_ATTEMPTS['email'] = 1 |
661 | 710 | ||
662 | CACHE.VIDEO_CAPTIONS.MAX_AGE = 3000 | 711 | CACHE.VIDEO_CAPTIONS.MAX_AGE = 3000 |
663 | MEMOIZE_TTL.OVERVIEWS_SAMPLE = 1 | 712 | MEMOIZE_TTL.OVERVIEWS_SAMPLE = 1 |
664 | ROUTE_CACHE_LIFETIME.OVERVIEWS.VIDEOS = '0ms' | 713 | ROUTE_CACHE_LIFETIME.OVERVIEWS.VIDEOS = '0ms' |
714 | |||
715 | RATES_LIMIT.LOGIN.MAX = 20 | ||
665 | } | 716 | } |
666 | 717 | ||
667 | updateWebserverConfig() | 718 | updateWebserverUrls() |
668 | 719 | ||
669 | // --------------------------------------------------------------------------- | 720 | // --------------------------------------------------------------------------- |
670 | 721 | ||
671 | export { | 722 | export { |
672 | API_VERSION, | 723 | API_VERSION, |
673 | VIDEO_CAPTIONS_MIMETYPE_EXT, | 724 | HLS_REDUNDANCY_DIRECTORY, |
674 | AVATARS_SIZE, | 725 | AVATARS_SIZE, |
675 | ACCEPT_HEADERS, | 726 | ACCEPT_HEADERS, |
676 | BCRYPT_SALT_SIZE, | 727 | BCRYPT_SALT_SIZE, |
@@ -695,10 +746,10 @@ export { | |||
695 | PRIVATE_RSA_KEY_SIZE, | 746 | PRIVATE_RSA_KEY_SIZE, |
696 | ROUTE_CACHE_LIFETIME, | 747 | ROUTE_CACHE_LIFETIME, |
697 | SORTABLE_COLUMNS, | 748 | SORTABLE_COLUMNS, |
749 | HLS_PLAYLIST_DIRECTORY, | ||
698 | FEEDS, | 750 | FEEDS, |
699 | JOB_TTL, | 751 | JOB_TTL, |
700 | NSFW_POLICY_TYPES, | 752 | NSFW_POLICY_TYPES, |
701 | TORRENT_MIMETYPE_EXT, | ||
702 | STATIC_MAX_AGE, | 753 | STATIC_MAX_AGE, |
703 | STATIC_PATHS, | 754 | STATIC_PATHS, |
704 | VIDEO_IMPORT_TIMEOUT, | 755 | VIDEO_IMPORT_TIMEOUT, |
@@ -711,7 +762,6 @@ export { | |||
711 | VIDEO_LICENCES, | 762 | VIDEO_LICENCES, |
712 | VIDEO_STATES, | 763 | VIDEO_STATES, |
713 | VIDEO_RATE_TYPES, | 764 | VIDEO_RATE_TYPES, |
714 | VIDEO_MIMETYPE_EXT, | ||
715 | VIDEO_TRANSCODING_FPS, | 765 | VIDEO_TRANSCODING_FPS, |
716 | FFMPEG_NICE, | 766 | FFMPEG_NICE, |
717 | VIDEO_ABUSE_STATES, | 767 | VIDEO_ABUSE_STATES, |
@@ -719,17 +769,18 @@ export { | |||
719 | USER_PASSWORD_RESET_LIFETIME, | 769 | USER_PASSWORD_RESET_LIFETIME, |
720 | MEMOIZE_TTL, | 770 | MEMOIZE_TTL, |
721 | USER_EMAIL_VERIFY_LIFETIME, | 771 | USER_EMAIL_VERIFY_LIFETIME, |
722 | IMAGE_MIMETYPE_EXT, | ||
723 | OVERVIEWS, | 772 | OVERVIEWS, |
724 | SCHEDULER_INTERVALS_MS, | 773 | SCHEDULER_INTERVALS_MS, |
725 | REPEAT_JOBS, | 774 | REPEAT_JOBS, |
726 | STATIC_DOWNLOAD_PATHS, | 775 | STATIC_DOWNLOAD_PATHS, |
727 | RATES_LIMIT, | 776 | RATES_LIMIT, |
728 | VIDEO_EXT_MIMETYPE, | 777 | MIMETYPES, |
729 | CRAWL_REQUEST_CONCURRENCY, | 778 | CRAWL_REQUEST_CONCURRENCY, |
730 | JOB_COMPLETED_LIFETIME, | 779 | JOB_COMPLETED_LIFETIME, |
780 | HTTP_SIGNATURE, | ||
731 | VIDEO_IMPORT_STATES, | 781 | VIDEO_IMPORT_STATES, |
732 | VIDEO_VIEW_LIFETIME, | 782 | VIDEO_VIEW_LIFETIME, |
783 | CONTACT_FORM_LIFETIME, | ||
733 | buildLanguages | 784 | buildLanguages |
734 | } | 785 | } |
735 | 786 | ||
@@ -746,16 +797,50 @@ function getLocalConfigFilePath () { | |||
746 | return join(dirname(configSources[ 0 ].name), filename + '.json') | 797 | return join(dirname(configSources[ 0 ].name), filename + '.json') |
747 | } | 798 | } |
748 | 799 | ||
749 | function updateWebserverConfig () { | 800 | function buildVideoMimetypeExt () { |
801 | const data = { | ||
802 | 'video/webm': '.webm', | ||
803 | 'video/ogg': '.ogv', | ||
804 | 'video/mp4': '.mp4' | ||
805 | } | ||
806 | |||
807 | if (CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS) { | ||
808 | Object.assign(data, { | ||
809 | 'video/quicktime': '.mov', | ||
810 | 'video/x-msvideo': '.avi', | ||
811 | 'video/x-flv': '.flv', | ||
812 | 'video/x-matroska': '.mkv', | ||
813 | 'application/octet-stream': '.mkv', | ||
814 | 'video/avi': '.avi' | ||
815 | }) | ||
816 | } | ||
817 | |||
818 | return data | ||
819 | } | ||
820 | |||
821 | function updateWebserverUrls () { | ||
750 | CONFIG.WEBSERVER.URL = sanitizeUrl(CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT) | 822 | CONFIG.WEBSERVER.URL = sanitizeUrl(CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT) |
751 | CONFIG.WEBSERVER.HOST = sanitizeHost(CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT, REMOTE_SCHEME.HTTP) | 823 | CONFIG.WEBSERVER.HOST = sanitizeHost(CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT, REMOTE_SCHEME.HTTP) |
752 | } | 824 | } |
753 | 825 | ||
826 | function updateWebserverConfig () { | ||
827 | CONSTRAINTS_FIELDS.VIDEOS.EXTNAME = buildVideosExtname() | ||
828 | |||
829 | MIMETYPES.VIDEO.MIMETYPE_EXT = buildVideoMimetypeExt() | ||
830 | MIMETYPES.VIDEO.EXT_MIMETYPE = invert(MIMETYPES.VIDEO.MIMETYPE_EXT) | ||
831 | } | ||
832 | |||
833 | function buildVideosExtname () { | ||
834 | return CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS | ||
835 | ? [ '.mp4', '.ogv', '.webm', '.mkv', '.mov', '.avi', '.flv' ] | ||
836 | : [ '.mp4', '.ogv', '.webm' ] | ||
837 | } | ||
838 | |||
754 | function buildVideosRedundancy (objs: any[]): VideosRedundancy[] { | 839 | function buildVideosRedundancy (objs: any[]): VideosRedundancy[] { |
755 | if (!objs) return [] | 840 | if (!objs) return [] |
756 | 841 | ||
757 | return objs.map(obj => { | 842 | return objs.map(obj => { |
758 | return Object.assign(obj, { | 843 | return Object.assign({}, obj, { |
759 | minLifetime: parseDuration(obj.min_lifetime), | 844 | minLifetime: parseDuration(obj.min_lifetime), |
760 | size: bytes.parse(obj.size), | 845 | size: bytes.parse(obj.size), |
761 | minViews: obj.min_views | 846 | minViews: obj.min_views |
@@ -832,4 +917,5 @@ export function reloadConfig () { | |||
832 | config = require('config') | 917 | config = require('config') |
833 | 918 | ||
834 | updateWebserverConfig() | 919 | updateWebserverConfig() |
920 | updateWebserverUrls() | ||
835 | } | 921 | } |
diff --git a/server/initializers/database.ts b/server/initializers/database.ts index 482c03b31..fe296142d 100644 --- a/server/initializers/database.ts +++ b/server/initializers/database.ts | |||
@@ -29,6 +29,11 @@ import { VideoViewModel } from '../models/video/video-views' | |||
29 | import { VideoChangeOwnershipModel } from '../models/video/video-change-ownership' | 29 | import { VideoChangeOwnershipModel } from '../models/video/video-change-ownership' |
30 | import { VideoRedundancyModel } from '../models/redundancy/video-redundancy' | 30 | import { VideoRedundancyModel } from '../models/redundancy/video-redundancy' |
31 | import { UserVideoHistoryModel } from '../models/account/user-video-history' | 31 | import { UserVideoHistoryModel } from '../models/account/user-video-history' |
32 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | ||
33 | import { ServerBlocklistModel } from '../models/server/server-blocklist' | ||
34 | import { UserNotificationModel } from '../models/account/user-notification' | ||
35 | import { UserNotificationSettingModel } from '../models/account/user-notification-setting' | ||
36 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' | ||
32 | 37 | ||
33 | require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string | 38 | require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string |
34 | 39 | ||
@@ -91,7 +96,12 @@ async function initDatabaseModels (silent: boolean) { | |||
91 | VideoImportModel, | 96 | VideoImportModel, |
92 | VideoViewModel, | 97 | VideoViewModel, |
93 | VideoRedundancyModel, | 98 | VideoRedundancyModel, |
94 | UserVideoHistoryModel | 99 | UserVideoHistoryModel, |
100 | AccountBlocklistModel, | ||
101 | ServerBlocklistModel, | ||
102 | UserNotificationModel, | ||
103 | UserNotificationSettingModel, | ||
104 | VideoStreamingPlaylistModel | ||
95 | ]) | 105 | ]) |
96 | 106 | ||
97 | // Check extensions exist in the database | 107 | // Check extensions exist in the database |
@@ -115,25 +125,27 @@ export { | |||
115 | // --------------------------------------------------------------------------- | 125 | // --------------------------------------------------------------------------- |
116 | 126 | ||
117 | async function checkPostgresExtensions () { | 127 | async function checkPostgresExtensions () { |
118 | const extensions = [ | 128 | const promises = [ |
119 | 'pg_trgm', | 129 | checkPostgresExtension('pg_trgm'), |
120 | 'unaccent' | 130 | checkPostgresExtension('unaccent') |
121 | ] | 131 | ] |
122 | 132 | ||
123 | for (const extension of extensions) { | 133 | return Promise.all(promises) |
124 | const query = `SELECT true AS enabled FROM pg_available_extensions WHERE name = '${extension}' AND installed_version IS NOT NULL;` | 134 | } |
125 | const [ res ] = await sequelizeTypescript.query(query, { raw: true }) | 135 | |
136 | async function checkPostgresExtension (extension: string) { | ||
137 | const query = `SELECT true AS enabled FROM pg_available_extensions WHERE name = '${extension}' AND installed_version IS NOT NULL;` | ||
138 | const [ res ] = await sequelizeTypescript.query(query, { raw: true }) | ||
126 | 139 | ||
127 | if (!res || res.length === 0 || res[ 0 ][ 'enabled' ] !== true) { | 140 | if (!res || res.length === 0 || res[ 0 ][ 'enabled' ] !== true) { |
128 | // Try to create the extension ourself | 141 | // Try to create the extension ourself |
129 | try { | 142 | try { |
130 | await sequelizeTypescript.query(`CREATE EXTENSION ${extension};`, { raw: true }) | 143 | await sequelizeTypescript.query(`CREATE EXTENSION ${extension};`, { raw: true }) |
131 | 144 | ||
132 | } catch { | 145 | } catch { |
133 | const errorMessage = `You need to enable ${extension} extension in PostgreSQL. ` + | 146 | const errorMessage = `You need to enable ${extension} extension in PostgreSQL. ` + |
134 | `You can do so by running 'CREATE EXTENSION ${extension};' as a PostgreSQL super user in ${CONFIG.DATABASE.DBNAME} database.` | 147 | `You can do so by running 'CREATE EXTENSION ${extension};' as a PostgreSQL super user in ${CONFIG.DATABASE.DBNAME} database.` |
135 | throw new Error(errorMessage) | 148 | throw new Error(errorMessage) |
136 | } | ||
137 | } | 149 | } |
138 | } | 150 | } |
139 | } | 151 | } |
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts index c952ad46c..2b22e16fe 100644 --- a/server/initializers/installer.ts +++ b/server/initializers/installer.ts | |||
@@ -6,18 +6,27 @@ import { UserModel } from '../models/account/user' | |||
6 | import { ApplicationModel } from '../models/application/application' | 6 | import { ApplicationModel } from '../models/application/application' |
7 | import { OAuthClientModel } from '../models/oauth/oauth-client' | 7 | import { OAuthClientModel } from '../models/oauth/oauth-client' |
8 | import { applicationExist, clientsExist, usersExist } from './checker-after-init' | 8 | import { applicationExist, clientsExist, usersExist } from './checker-after-init' |
9 | import { CACHE, CONFIG, LAST_MIGRATION_VERSION } from './constants' | 9 | import { CACHE, CONFIG, HLS_PLAYLIST_DIRECTORY, LAST_MIGRATION_VERSION } from './constants' |
10 | import { sequelizeTypescript } from './database' | 10 | import { sequelizeTypescript } from './database' |
11 | import { remove, ensureDir } from 'fs-extra' | 11 | import { remove, ensureDir } from 'fs-extra' |
12 | 12 | ||
13 | async function installApplication () { | 13 | async function installApplication () { |
14 | try { | 14 | try { |
15 | await sequelizeTypescript.sync() | 15 | await Promise.all([ |
16 | await removeCacheDirectories() | 16 | // Database related |
17 | await createDirectoriesIfNotExist() | 17 | sequelizeTypescript.sync() |
18 | await createApplicationIfNotExist() | 18 | .then(() => { |
19 | await createOAuthClientIfNotExist() | 19 | return Promise.all([ |
20 | await createOAuthAdminIfNotExist() | 20 | createApplicationIfNotExist(), |
21 | createOAuthClientIfNotExist(), | ||
22 | createOAuthAdminIfNotExist() | ||
23 | ]) | ||
24 | }), | ||
25 | |||
26 | // Directories | ||
27 | removeCacheDirectories() | ||
28 | .then(() => createDirectoriesIfNotExist()) | ||
29 | ]) | ||
21 | } catch (err) { | 30 | } catch (err) { |
22 | logger.error('Cannot install application.', { err }) | 31 | logger.error('Cannot install application.', { err }) |
23 | process.exit(-1) | 32 | process.exit(-1) |
@@ -64,6 +73,9 @@ function createDirectoriesIfNotExist () { | |||
64 | tasks.push(ensureDir(dir)) | 73 | tasks.push(ensureDir(dir)) |
65 | } | 74 | } |
66 | 75 | ||
76 | // Playlist directories | ||
77 | tasks.push(ensureDir(HLS_PLAYLIST_DIRECTORY)) | ||
78 | |||
67 | return Promise.all(tasks) | 79 | return Promise.all(tasks) |
68 | } | 80 | } |
69 | 81 | ||
diff --git a/server/initializers/migrations/0120-video-null.ts b/server/initializers/migrations/0120-video-null.ts index 63f3984dd..6d253f04f 100644 --- a/server/initializers/migrations/0120-video-null.ts +++ b/server/initializers/migrations/0120-video-null.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { CONSTRAINTS_FIELDS } from '../constants' | ||
3 | 2 | ||
4 | async function up (utils: { | 3 | async function up (utils: { |
5 | transaction: Sequelize.Transaction, | 4 | transaction: Sequelize.Transaction, |
@@ -28,7 +27,7 @@ async function up (utils: { | |||
28 | 27 | ||
29 | { | 28 | { |
30 | const data = { | 29 | const data = { |
31 | type: Sequelize.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max), | 30 | type: Sequelize.STRING(10000), |
32 | allowNull: true, | 31 | allowNull: true, |
33 | defaultValue: null | 32 | defaultValue: null |
34 | } | 33 | } |
diff --git a/server/initializers/migrations/0195-support.ts b/server/initializers/migrations/0195-support.ts index 8722a5f22..3b9eabe79 100644 --- a/server/initializers/migrations/0195-support.ts +++ b/server/initializers/migrations/0195-support.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { CONSTRAINTS_FIELDS } from '../index' | ||
3 | 2 | ||
4 | async function up (utils: { | 3 | async function up (utils: { |
5 | transaction: Sequelize.Transaction, | 4 | transaction: Sequelize.Transaction, |
@@ -8,7 +7,7 @@ async function up (utils: { | |||
8 | }): Promise<void> { | 7 | }): Promise<void> { |
9 | { | 8 | { |
10 | const data = { | 9 | const data = { |
11 | type: Sequelize.STRING(CONSTRAINTS_FIELDS.VIDEOS.SUPPORT.max), | 10 | type: Sequelize.STRING(500), |
12 | allowNull: true, | 11 | allowNull: true, |
13 | defaultValue: null | 12 | defaultValue: null |
14 | } | 13 | } |
@@ -17,7 +16,7 @@ async function up (utils: { | |||
17 | 16 | ||
18 | { | 17 | { |
19 | const data = { | 18 | const data = { |
20 | type: Sequelize.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.SUPPORT.max), | 19 | type: Sequelize.STRING(500), |
21 | allowNull: true, | 20 | allowNull: true, |
22 | defaultValue: null | 21 | defaultValue: null |
23 | } | 22 | } |
@@ -26,7 +25,7 @@ async function up (utils: { | |||
26 | 25 | ||
27 | { | 26 | { |
28 | const data = { | 27 | const data = { |
29 | type: Sequelize.STRING(CONSTRAINTS_FIELDS.USERS.DESCRIPTION.max), | 28 | type: Sequelize.STRING(250), |
30 | allowNull: true, | 29 | allowNull: true, |
31 | defaultValue: null | 30 | defaultValue: null |
32 | } | 31 | } |
@@ -35,7 +34,7 @@ async function up (utils: { | |||
35 | 34 | ||
36 | { | 35 | { |
37 | const data = { | 36 | const data = { |
38 | type: Sequelize.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max), | 37 | type: Sequelize.STRING(10000), |
39 | allowNull: true, | 38 | allowNull: true, |
40 | defaultValue: null | 39 | defaultValue: null |
41 | } | 40 | } |
diff --git a/server/initializers/migrations/0245-user-blocked.ts b/server/initializers/migrations/0245-user-blocked.ts index 5a04ecd2b..19c7d5b9c 100644 --- a/server/initializers/migrations/0245-user-blocked.ts +++ b/server/initializers/migrations/0245-user-blocked.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { CONSTRAINTS_FIELDS } from '../constants' | ||
3 | 2 | ||
4 | async function up (utils: { | 3 | async function up (utils: { |
5 | transaction: Sequelize.Transaction | 4 | transaction: Sequelize.Transaction |
@@ -31,7 +30,7 @@ async function up (utils: { | |||
31 | 30 | ||
32 | { | 31 | { |
33 | const data = { | 32 | const data = { |
34 | type: Sequelize.STRING(CONSTRAINTS_FIELDS.USERS.BLOCKED_REASON.max), | 33 | type: Sequelize.STRING(250), |
35 | allowNull: true, | 34 | allowNull: true, |
36 | defaultValue: null | 35 | defaultValue: null |
37 | } | 36 | } |
diff --git a/server/initializers/migrations/0250-video-abuse-state.ts b/server/initializers/migrations/0250-video-abuse-state.ts index acb668ae1..50de25182 100644 --- a/server/initializers/migrations/0250-video-abuse-state.ts +++ b/server/initializers/migrations/0250-video-abuse-state.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { CONSTRAINTS_FIELDS } from '../constants' | ||
3 | import { VideoAbuseState } from '../../../shared/models/videos' | 2 | import { VideoAbuseState } from '../../../shared/models/videos' |
4 | 3 | ||
5 | async function up (utils: { | 4 | async function up (utils: { |
@@ -32,7 +31,7 @@ async function up (utils: { | |||
32 | 31 | ||
33 | { | 32 | { |
34 | const data = { | 33 | const data = { |
35 | type: Sequelize.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.MODERATION_COMMENT.max), | 34 | type: Sequelize.STRING(300), |
36 | allowNull: true, | 35 | allowNull: true, |
37 | defaultValue: null | 36 | defaultValue: null |
38 | } | 37 | } |
diff --git a/server/initializers/migrations/0255-video-blacklist-reason.ts b/server/initializers/migrations/0255-video-blacklist-reason.ts index a380e620e..69d6efb9e 100644 --- a/server/initializers/migrations/0255-video-blacklist-reason.ts +++ b/server/initializers/migrations/0255-video-blacklist-reason.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { CONSTRAINTS_FIELDS } from '../constants' | ||
3 | import { VideoAbuseState } from '../../../shared/models/videos' | 2 | import { VideoAbuseState } from '../../../shared/models/videos' |
4 | 3 | ||
5 | async function up (utils: { | 4 | async function up (utils: { |
@@ -10,7 +9,7 @@ async function up (utils: { | |||
10 | 9 | ||
11 | { | 10 | { |
12 | const data = { | 11 | const data = { |
13 | type: Sequelize.STRING(CONSTRAINTS_FIELDS.VIDEO_BLACKLIST.REASON.max), | 12 | type: Sequelize.STRING(300), |
14 | allowNull: true, | 13 | allowNull: true, |
15 | defaultValue: null | 14 | defaultValue: null |
16 | } | 15 | } |
diff --git a/server/initializers/migrations/0260-upload-quota-daily.ts b/server/initializers/migrations/0260-upload-quota-daily.ts index d25154ba6..cbbe391ef 100644 --- a/server/initializers/migrations/0260-upload-quota-daily.ts +++ b/server/initializers/migrations/0260-upload-quota-daily.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { CONSTRAINTS_FIELDS } from '../constants' | ||
3 | 2 | ||
4 | async function up (utils: { | 3 | async function up (utils: { |
5 | transaction: Sequelize.Transaction | 4 | transaction: Sequelize.Transaction |
diff --git a/server/initializers/migrations/0275-video-file-unique.ts b/server/initializers/migrations/0275-video-file-unique.ts index fd89188c0..e321ecb04 100644 --- a/server/initializers/migrations/0275-video-file-unique.ts +++ b/server/initializers/migrations/0275-video-file-unique.ts | |||
@@ -5,6 +5,12 @@ async function up (utils: { | |||
5 | queryInterface: Sequelize.QueryInterface | 5 | queryInterface: Sequelize.QueryInterface |
6 | sequelize: Sequelize.Sequelize | 6 | sequelize: Sequelize.Sequelize |
7 | }): Promise<any> { | 7 | }): Promise<any> { |
8 | // Delete duplicated keys | ||
9 | { | ||
10 | const query = 'DELETE FROM "server" s1 USING "server" s2 WHERE s1.id < s2.id AND s1."host" = s2."host"' | ||
11 | await utils.sequelize.query(query) | ||
12 | } | ||
13 | |||
8 | { | 14 | { |
9 | const query = 'DELETE FROM "videoFile" vf1 USING "videoFile" vf2 WHERE vf1.id < vf2.id ' + | 15 | const query = 'DELETE FROM "videoFile" vf1 USING "videoFile" vf2 WHERE vf1.id < vf2.id ' + |
10 | 'AND vf1."videoId" = vf2."videoId" AND vf1.resolution = vf2.resolution AND vf1.fps IS NULL' | 16 | 'AND vf1."videoId" = vf2."videoId" AND vf1.resolution = vf2.resolution AND vf1.fps IS NULL' |
diff --git a/server/initializers/migrations/0280-webtorrent-policy-user.ts b/server/initializers/migrations/0280-webtorrent-policy-user.ts new file mode 100644 index 000000000..e6488356a --- /dev/null +++ b/server/initializers/migrations/0280-webtorrent-policy-user.ts | |||
@@ -0,0 +1,28 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction | ||
5 | queryInterface: Sequelize.QueryInterface | ||
6 | sequelize: Sequelize.Sequelize | ||
7 | }): Promise<any> { | ||
8 | { | ||
9 | const data = { | ||
10 | type: Sequelize.BOOLEAN, | ||
11 | allowNull: false, | ||
12 | defaultValue: true | ||
13 | } | ||
14 | |||
15 | await utils.queryInterface.addColumn('user', 'webTorrentEnabled', data) | ||
16 | } | ||
17 | |||
18 | } | ||
19 | |||
20 | async function down (utils: { | ||
21 | transaction: Sequelize.Transaction | ||
22 | queryInterface: Sequelize.QueryInterface | ||
23 | sequelize: Sequelize.Sequelize | ||
24 | }): Promise<any> { | ||
25 | await utils.queryInterface.removeColumn('user', 'webTorrentEnabled') | ||
26 | } | ||
27 | |||
28 | export { up, down } | ||
diff --git a/server/initializers/migrations/0285-description-support.ts b/server/initializers/migrations/0285-description-support.ts new file mode 100644 index 000000000..85ef4ef39 --- /dev/null +++ b/server/initializers/migrations/0285-description-support.ts | |||
@@ -0,0 +1,53 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize, | ||
7 | db: any | ||
8 | }): Promise<void> { | ||
9 | { | ||
10 | const data = { | ||
11 | type: Sequelize.STRING(1000), | ||
12 | allowNull: true, | ||
13 | defaultValue: null | ||
14 | } | ||
15 | await utils.queryInterface.changeColumn('video', 'support', data) | ||
16 | } | ||
17 | |||
18 | { | ||
19 | const data = { | ||
20 | type: Sequelize.STRING(1000), | ||
21 | allowNull: true, | ||
22 | defaultValue: null | ||
23 | } | ||
24 | await utils.queryInterface.changeColumn('videoChannel', 'support', data) | ||
25 | } | ||
26 | |||
27 | { | ||
28 | const data = { | ||
29 | type: Sequelize.STRING(1000), | ||
30 | allowNull: true, | ||
31 | defaultValue: null | ||
32 | } | ||
33 | await utils.queryInterface.changeColumn('videoChannel', 'description', data) | ||
34 | } | ||
35 | |||
36 | { | ||
37 | const data = { | ||
38 | type: Sequelize.STRING(1000), | ||
39 | allowNull: true, | ||
40 | defaultValue: null | ||
41 | } | ||
42 | await utils.queryInterface.changeColumn('account', 'description', data) | ||
43 | } | ||
44 | } | ||
45 | |||
46 | function down (options) { | ||
47 | throw new Error('Not implemented.') | ||
48 | } | ||
49 | |||
50 | export { | ||
51 | up, | ||
52 | down | ||
53 | } | ||
diff --git a/server/initializers/migrations/0290-account-video-rate-url.ts b/server/initializers/migrations/0290-account-video-rate-url.ts new file mode 100644 index 000000000..bdabf2929 --- /dev/null +++ b/server/initializers/migrations/0290-account-video-rate-url.ts | |||
@@ -0,0 +1,46 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize, | ||
7 | db: any | ||
8 | }): Promise<void> { | ||
9 | { | ||
10 | const data = { | ||
11 | type: Sequelize.STRING(2000), | ||
12 | allowNull: true | ||
13 | } | ||
14 | |||
15 | await utils.queryInterface.addColumn('accountVideoRate', 'url', data) | ||
16 | } | ||
17 | |||
18 | { | ||
19 | const builtUrlQuery = `SELECT "actor"."url" || '/' || "accountVideoRate"."type" || 's/' || "videoId" ` + | ||
20 | 'FROM "accountVideoRate" ' + | ||
21 | 'INNER JOIN account ON account.id = "accountVideoRate"."accountId" ' + | ||
22 | 'INNER JOIN actor ON actor.id = account."actorId" ' + | ||
23 | 'WHERE "base".id = "accountVideoRate".id' | ||
24 | |||
25 | const query = 'UPDATE "accountVideoRate" base SET "url" = (' + builtUrlQuery + ') WHERE "url" IS NULL' | ||
26 | await utils.sequelize.query(query) | ||
27 | } | ||
28 | |||
29 | { | ||
30 | const data = { | ||
31 | type: Sequelize.STRING(2000), | ||
32 | allowNull: false, | ||
33 | defaultValue: null | ||
34 | } | ||
35 | await utils.queryInterface.changeColumn('accountVideoRate', 'url', data) | ||
36 | } | ||
37 | } | ||
38 | |||
39 | function down (options) { | ||
40 | throw new Error('Not implemented.') | ||
41 | } | ||
42 | |||
43 | export { | ||
44 | up, | ||
45 | down | ||
46 | } | ||
diff --git a/server/initializers/migrations/0295-video-file-extname.ts b/server/initializers/migrations/0295-video-file-extname.ts new file mode 100644 index 000000000..dbf249f66 --- /dev/null +++ b/server/initializers/migrations/0295-video-file-extname.ts | |||
@@ -0,0 +1,49 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize, | ||
7 | db: any | ||
8 | }): Promise<void> { | ||
9 | { | ||
10 | await utils.queryInterface.renameColumn('videoFile', 'extname', 'extname_old') | ||
11 | } | ||
12 | |||
13 | { | ||
14 | const data = { | ||
15 | type: Sequelize.STRING, | ||
16 | defaultValue: null, | ||
17 | allowNull: true | ||
18 | } | ||
19 | |||
20 | await utils.queryInterface.addColumn('videoFile', 'extname', data) | ||
21 | } | ||
22 | |||
23 | { | ||
24 | const query = 'UPDATE "videoFile" SET "extname" = "extname_old"::text' | ||
25 | await utils.sequelize.query(query) | ||
26 | } | ||
27 | |||
28 | { | ||
29 | const data = { | ||
30 | type: Sequelize.STRING, | ||
31 | defaultValue: null, | ||
32 | allowNull: false | ||
33 | } | ||
34 | await utils.queryInterface.changeColumn('videoFile', 'extname', data) | ||
35 | } | ||
36 | |||
37 | { | ||
38 | await utils.queryInterface.removeColumn('videoFile', 'extname_old') | ||
39 | } | ||
40 | } | ||
41 | |||
42 | function down (options) { | ||
43 | throw new Error('Not implemented.') | ||
44 | } | ||
45 | |||
46 | export { | ||
47 | up, | ||
48 | down | ||
49 | } | ||
diff --git a/server/initializers/migrations/0300-user-videos-history-enabled.ts b/server/initializers/migrations/0300-user-videos-history-enabled.ts new file mode 100644 index 000000000..aa5fc21fb --- /dev/null +++ b/server/initializers/migrations/0300-user-videos-history-enabled.ts | |||
@@ -0,0 +1,27 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize, | ||
7 | db: any | ||
8 | }): Promise<void> { | ||
9 | { | ||
10 | const data = { | ||
11 | type: Sequelize.BOOLEAN, | ||
12 | allowNull: false, | ||
13 | defaultValue: true | ||
14 | } | ||
15 | |||
16 | await utils.queryInterface.addColumn('user', 'videosHistoryEnabled', data) | ||
17 | } | ||
18 | } | ||
19 | |||
20 | function down (options) { | ||
21 | throw new Error('Not implemented.') | ||
22 | } | ||
23 | |||
24 | export { | ||
25 | up, | ||
26 | down | ||
27 | } | ||
diff --git a/server/initializers/migrations/0305-fix-unfederated-videos.ts b/server/initializers/migrations/0305-fix-unfederated-videos.ts new file mode 100644 index 000000000..be206601f --- /dev/null +++ b/server/initializers/migrations/0305-fix-unfederated-videos.ts | |||
@@ -0,0 +1,52 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize, | ||
7 | db: any | ||
8 | }): Promise<void> { | ||
9 | { | ||
10 | const query = `INSERT INTO "videoShare" (url, "actorId", "videoId", "createdAt", "updatedAt") ` + | ||
11 | `(` + | ||
12 | `SELECT ` + | ||
13 | `video.url || '/announces/' || "videoChannel"."actorId" as url, ` + | ||
14 | `"videoChannel"."actorId" AS "actorId", ` + | ||
15 | `"video"."id" AS "videoId", ` + | ||
16 | `NOW() AS "createdAt", ` + | ||
17 | `NOW() AS "updatedAt" ` + | ||
18 | `FROM video ` + | ||
19 | `INNER JOIN "videoChannel" ON "video"."channelId" = "videoChannel"."id" ` + | ||
20 | `WHERE "video"."remote" = false AND "video"."privacy" != 3 AND "video"."state" = 1` + | ||
21 | `) ` + | ||
22 | `ON CONFLICT DO NOTHING` | ||
23 | |||
24 | await utils.sequelize.query(query) | ||
25 | } | ||
26 | |||
27 | { | ||
28 | const query = `INSERT INTO "videoShare" (url, "actorId", "videoId", "createdAt", "updatedAt") ` + | ||
29 | `(` + | ||
30 | `SELECT ` + | ||
31 | `video.url || '/announces/' || (SELECT id FROM actor WHERE "preferredUsername" = 'peertube' ORDER BY id ASC LIMIT 1) as url, ` + | ||
32 | `(SELECT id FROM actor WHERE "preferredUsername" = 'peertube' ORDER BY id ASC LIMIT 1) AS "actorId", ` + | ||
33 | `"video"."id" AS "videoId", ` + | ||
34 | `NOW() AS "createdAt", ` + | ||
35 | `NOW() AS "updatedAt" ` + | ||
36 | `FROM video ` + | ||
37 | `WHERE "video"."remote" = false AND "video"."privacy" != 3 AND "video"."state" = 1` + | ||
38 | `) ` + | ||
39 | `ON CONFLICT DO NOTHING` | ||
40 | |||
41 | await utils.sequelize.query(query) | ||
42 | } | ||
43 | } | ||
44 | |||
45 | function down (options) { | ||
46 | throw new Error('Not implemented.') | ||
47 | } | ||
48 | |||
49 | export { | ||
50 | up, | ||
51 | down | ||
52 | } | ||
diff --git a/server/initializers/migrations/0310-drop-unused-video-indexes.ts b/server/initializers/migrations/0310-drop-unused-video-indexes.ts new file mode 100644 index 000000000..d51f430c0 --- /dev/null +++ b/server/initializers/migrations/0310-drop-unused-video-indexes.ts | |||
@@ -0,0 +1,32 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize, | ||
7 | db: any | ||
8 | }): Promise<void> { | ||
9 | const indexNames = [ | ||
10 | 'video_category', | ||
11 | 'video_licence', | ||
12 | 'video_nsfw', | ||
13 | 'video_language', | ||
14 | 'video_wait_transcoding', | ||
15 | 'video_state', | ||
16 | 'video_remote', | ||
17 | 'video_likes' | ||
18 | ] | ||
19 | |||
20 | for (const indexName of indexNames) { | ||
21 | await utils.sequelize.query('DROP INDEX IF EXISTS "' + indexName + '";') | ||
22 | } | ||
23 | } | ||
24 | |||
25 | function down (options) { | ||
26 | throw new Error('Not implemented.') | ||
27 | } | ||
28 | |||
29 | export { | ||
30 | up, | ||
31 | down | ||
32 | } | ||
diff --git a/server/initializers/migrations/0315-user-notifications.ts b/server/initializers/migrations/0315-user-notifications.ts new file mode 100644 index 000000000..8284c58a0 --- /dev/null +++ b/server/initializers/migrations/0315-user-notifications.ts | |||
@@ -0,0 +1,47 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize | ||
7 | }): Promise<void> { | ||
8 | |||
9 | { | ||
10 | const query = ` | ||
11 | CREATE TABLE IF NOT EXISTS "userNotificationSetting" ("id" SERIAL, | ||
12 | "newVideoFromSubscription" INTEGER NOT NULL DEFAULT NULL, | ||
13 | "newCommentOnMyVideo" INTEGER NOT NULL DEFAULT NULL, | ||
14 | "videoAbuseAsModerator" INTEGER NOT NULL DEFAULT NULL, | ||
15 | "blacklistOnMyVideo" INTEGER NOT NULL DEFAULT NULL, | ||
16 | "myVideoPublished" INTEGER NOT NULL DEFAULT NULL, | ||
17 | "myVideoImportFinished" INTEGER NOT NULL DEFAULT NULL, | ||
18 | "newUserRegistration" INTEGER NOT NULL DEFAULT NULL, | ||
19 | "newFollow" INTEGER NOT NULL DEFAULT NULL, | ||
20 | "commentMention" INTEGER NOT NULL DEFAULT NULL, | ||
21 | "userId" INTEGER REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE, | ||
22 | "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, | ||
23 | "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, | ||
24 | PRIMARY KEY ("id")) | ||
25 | ` | ||
26 | await utils.sequelize.query(query) | ||
27 | } | ||
28 | |||
29 | { | ||
30 | const query = 'INSERT INTO "userNotificationSetting" ' + | ||
31 | '("newVideoFromSubscription", "newCommentOnMyVideo", "videoAbuseAsModerator", "blacklistOnMyVideo", ' + | ||
32 | '"myVideoPublished", "myVideoImportFinished", "newUserRegistration", "newFollow", "commentMention", ' + | ||
33 | '"userId", "createdAt", "updatedAt") ' + | ||
34 | '(SELECT 1, 1, 3, 3, 1, 1, 1, 1, 1, id, NOW(), NOW() FROM "user")' | ||
35 | |||
36 | await utils.sequelize.query(query) | ||
37 | } | ||
38 | } | ||
39 | |||
40 | function down (options) { | ||
41 | throw new Error('Not implemented.') | ||
42 | } | ||
43 | |||
44 | export { | ||
45 | up, | ||
46 | down | ||
47 | } | ||
diff --git a/server/initializers/migrations/0320-blacklist-unfederate.ts b/server/initializers/migrations/0320-blacklist-unfederate.ts new file mode 100644 index 000000000..6fb7bbb90 --- /dev/null +++ b/server/initializers/migrations/0320-blacklist-unfederate.ts | |||
@@ -0,0 +1,27 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize | ||
7 | }): Promise<void> { | ||
8 | |||
9 | { | ||
10 | const data = { | ||
11 | type: Sequelize.BOOLEAN, | ||
12 | allowNull: false, | ||
13 | defaultValue: false | ||
14 | } | ||
15 | |||
16 | await utils.queryInterface.addColumn('videoBlacklist', 'unfederated', data) | ||
17 | } | ||
18 | } | ||
19 | |||
20 | function down (options) { | ||
21 | throw new Error('Not implemented.') | ||
22 | } | ||
23 | |||
24 | export { | ||
25 | up, | ||
26 | down | ||
27 | } | ||
diff --git a/server/initializers/migrations/0325-video-abuse-fields.ts b/server/initializers/migrations/0325-video-abuse-fields.ts new file mode 100644 index 000000000..fca6d666f --- /dev/null +++ b/server/initializers/migrations/0325-video-abuse-fields.ts | |||
@@ -0,0 +1,37 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize | ||
7 | }): Promise<void> { | ||
8 | |||
9 | { | ||
10 | const data = { | ||
11 | type: Sequelize.STRING(3000), | ||
12 | allowNull: false, | ||
13 | defaultValue: null | ||
14 | } | ||
15 | |||
16 | await utils.queryInterface.changeColumn('videoAbuse', 'reason', data) | ||
17 | } | ||
18 | |||
19 | { | ||
20 | const data = { | ||
21 | type: Sequelize.STRING(3000), | ||
22 | allowNull: true, | ||
23 | defaultValue: null | ||
24 | } | ||
25 | |||
26 | await utils.queryInterface.changeColumn('videoAbuse', 'moderationComment', data) | ||
27 | } | ||
28 | } | ||
29 | |||
30 | function down (options) { | ||
31 | throw new Error('Not implemented.') | ||
32 | } | ||
33 | |||
34 | export { | ||
35 | up, | ||
36 | down | ||
37 | } | ||
diff --git a/server/initializers/migrations/0330-video-streaming-playlist.ts b/server/initializers/migrations/0330-video-streaming-playlist.ts new file mode 100644 index 000000000..c85a762ab --- /dev/null +++ b/server/initializers/migrations/0330-video-streaming-playlist.ts | |||
@@ -0,0 +1,51 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize | ||
7 | }): Promise<void> { | ||
8 | |||
9 | { | ||
10 | const query = ` | ||
11 | CREATE TABLE IF NOT EXISTS "videoStreamingPlaylist" | ||
12 | ( | ||
13 | "id" SERIAL, | ||
14 | "type" INTEGER NOT NULL, | ||
15 | "playlistUrl" VARCHAR(2000) NOT NULL, | ||
16 | "p2pMediaLoaderInfohashes" VARCHAR(255)[] NOT NULL, | ||
17 | "segmentsSha256Url" VARCHAR(255) NOT NULL, | ||
18 | "videoId" INTEGER NOT NULL REFERENCES "video" ("id") ON DELETE CASCADE ON UPDATE CASCADE, | ||
19 | "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, | ||
20 | "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, | ||
21 | PRIMARY KEY ("id") | ||
22 | );` | ||
23 | await utils.sequelize.query(query) | ||
24 | } | ||
25 | |||
26 | { | ||
27 | const data = { | ||
28 | type: Sequelize.INTEGER, | ||
29 | allowNull: true, | ||
30 | defaultValue: null | ||
31 | } | ||
32 | |||
33 | await utils.queryInterface.changeColumn('videoRedundancy', 'videoFileId', data) | ||
34 | } | ||
35 | |||
36 | { | ||
37 | const query = 'ALTER TABLE "videoRedundancy" ADD COLUMN "videoStreamingPlaylistId" INTEGER NULL ' + | ||
38 | 'REFERENCES "videoStreamingPlaylist" ("id") ON DELETE CASCADE ON UPDATE CASCADE' | ||
39 | |||
40 | await utils.sequelize.query(query) | ||
41 | } | ||
42 | } | ||
43 | |||
44 | function down (options) { | ||
45 | throw new Error('Not implemented.') | ||
46 | } | ||
47 | |||
48 | export { | ||
49 | up, | ||
50 | down | ||
51 | } | ||