aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--package.json4
-rw-r--r--server/controllers/api/videos/import.ts3
-rw-r--r--server/controllers/api/videos/index.ts6
-rw-r--r--server/helpers/database-utils.ts5
-rw-r--r--server/initializers/database.ts13
-rw-r--r--server/initializers/migrations/0080-video-channels.ts2
-rw-r--r--server/initializers/migrations/0100-activitypub.ts10
-rw-r--r--server/initializers/migrations/0135-video-channel-actor.ts3
-rw-r--r--server/initializers/migrations/0345-video-playlists.ts4
-rw-r--r--server/initializers/migrator.ts8
-rw-r--r--server/lib/activitypub/cache-file.ts4
-rw-r--r--server/lib/activitypub/playlist.ts6
-rw-r--r--server/lib/activitypub/process/process-follow.ts4
-rw-r--r--server/lib/activitypub/process/process-update.ts8
-rw-r--r--server/lib/activitypub/video-comments.ts3
-rw-r--r--server/lib/activitypub/video-rates.ts5
-rw-r--r--server/lib/activitypub/videos.ts12
-rw-r--r--server/lib/user.ts3
-rw-r--r--server/lib/video-comment.ts2
-rw-r--r--server/models/account/account-video-rate.ts17
-rw-r--r--server/models/account/account.ts16
-rw-r--r--server/models/account/user-notification.ts23
-rw-r--r--server/models/account/user-video-history.ts2
-rw-r--r--server/models/account/user.ts18
-rw-r--r--server/models/activitypub/actor-follow.ts39
-rw-r--r--server/models/activitypub/actor.ts8
-rw-r--r--server/models/migrations.ts16
-rw-r--r--server/models/oauth/oauth-client.ts4
-rw-r--r--server/models/oauth/oauth-token.ts2
-rw-r--r--server/models/redundancy/video-redundancy.ts2
-rw-r--r--server/models/utils.ts48
-rw-r--r--server/models/video/schedule-video-update.ts8
-rw-r--r--server/models/video/tag.ts8
-rw-r--r--server/models/video/video-abuse.ts2
-rw-r--r--server/models/video/video-blacklist.ts21
-rw-r--r--server/models/video/video-caption.ts10
-rw-r--r--server/models/video/video-change-ownership.ts2
-rw-r--r--server/models/video/video-channel.ts17
-rw-r--r--server/models/video/video-comment.ts47
-rw-r--r--server/models/video/video-import.ts4
-rw-r--r--server/models/video/video-playlist-element.ts20
-rw-r--r--server/models/video/video-playlist.ts28
-rw-r--r--server/models/video/video-streaming-playlist.ts18
-rw-r--r--server/models/video/video.ts169
-rw-r--r--shared/extra-utils/miscs/sql.ts17
-rw-r--r--yarn.lock139
46 files changed, 389 insertions, 421 deletions
diff --git a/package.json b/package.json
index 6b3986278..20cbd555a 100644
--- a/package.json
+++ b/package.json
@@ -142,8 +142,8 @@
142 "reflect-metadata": "^0.1.12", 142 "reflect-metadata": "^0.1.12",
143 "request": "^2.81.0", 143 "request": "^2.81.0",
144 "scripty": "^1.5.0", 144 "scripty": "^1.5.0",
145 "sequelize": "4.42.0", 145 "sequelize": "5.6.1",
146 "sequelize-typescript": "0.6.7", 146 "sequelize-typescript": "^1.0.0-beta.1",
147 "sharp": "^0.22.0", 147 "sharp": "^0.22.0",
148 "sitemap": "^2.1.0", 148 "sitemap": "^2.1.0",
149 "socket.io": "^2.2.0", 149 "socket.io": "^2.2.0",
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts
index f9a24a0c2..a4ec41d44 100644
--- a/server/controllers/api/videos/import.ts
+++ b/server/controllers/api/videos/import.ts
@@ -15,7 +15,6 @@ import { VideoImportModel } from '../../../models/video/video-import'
15import { JobQueue } from '../../../lib/job-queue/job-queue' 15import { JobQueue } from '../../../lib/job-queue/job-queue'
16import { join } from 'path' 16import { join } from 'path'
17import { isArray } from '../../../helpers/custom-validators/misc' 17import { isArray } from '../../../helpers/custom-validators/misc'
18import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model'
19import { VideoChannelModel } from '../../../models/video/video-channel' 18import { VideoChannelModel } from '../../../models/video/video-channel'
20import * as Bluebird from 'bluebird' 19import * as Bluebird from 'bluebird'
21import * as parseTorrent from 'parse-torrent' 20import * as parseTorrent from 'parse-torrent'
@@ -228,7 +227,7 @@ function insertIntoDB (parameters: {
228 previewModel: ThumbnailModel, 227 previewModel: ThumbnailModel,
229 videoChannel: VideoChannelModel, 228 videoChannel: VideoChannelModel,
230 tags: string[], 229 tags: string[],
231 videoImportAttributes: FilteredModelAttributes<VideoImportModel> 230 videoImportAttributes: Partial<VideoImportModel>
232}): Bluebird<VideoImportModel> { 231}): Bluebird<VideoImportModel> {
233 let { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes } = parameters 232 let { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes } = parameters
234 233
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 24721a17f..ad2fe958c 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -332,15 +332,15 @@ async function updateVideo (req: express.Request, res: express.Response) {
332 if (videoInfoToUpdate.downloadEnabled !== undefined) videoInstance.set('downloadEnabled', videoInfoToUpdate.downloadEnabled) 332 if (videoInfoToUpdate.downloadEnabled !== undefined) videoInstance.set('downloadEnabled', videoInfoToUpdate.downloadEnabled)
333 333
334 if (videoInfoToUpdate.originallyPublishedAt !== undefined && videoInfoToUpdate.originallyPublishedAt !== null) { 334 if (videoInfoToUpdate.originallyPublishedAt !== undefined && videoInfoToUpdate.originallyPublishedAt !== null) {
335 videoInstance.set('originallyPublishedAt', videoInfoToUpdate.originallyPublishedAt) 335 videoInstance.originallyPublishedAt = new Date(videoInfoToUpdate.originallyPublishedAt)
336 } 336 }
337 337
338 if (videoInfoToUpdate.privacy !== undefined) { 338 if (videoInfoToUpdate.privacy !== undefined) {
339 const newPrivacy = parseInt(videoInfoToUpdate.privacy.toString(), 10) 339 const newPrivacy = parseInt(videoInfoToUpdate.privacy.toString(), 10)
340 videoInstance.set('privacy', newPrivacy) 340 videoInstance.privacy = newPrivacy
341 341
342 if (wasPrivateVideo === true && newPrivacy !== VideoPrivacy.PRIVATE) { 342 if (wasPrivateVideo === true && newPrivacy !== VideoPrivacy.PRIVATE) {
343 videoInstance.set('publishedAt', new Date()) 343 videoInstance.publishedAt = new Date()
344 } 344 }
345 } 345 }
346 346
diff --git a/server/helpers/database-utils.ts b/server/helpers/database-utils.ts
index 1005d2cf1..39c74b2fd 100644
--- a/server/helpers/database-utils.ts
+++ b/server/helpers/database-utils.ts
@@ -62,14 +62,13 @@ function updateInstanceWithAnother <T extends Model<T>> (instanceToUpdate: Model
62 const obj = baseInstance.toJSON() 62 const obj = baseInstance.toJSON()
63 63
64 for (const key of Object.keys(obj)) { 64 for (const key of Object.keys(obj)) {
65 instanceToUpdate.set(key, obj[key]) 65 instanceToUpdate[key] = obj[key]
66 } 66 }
67} 67}
68 68
69function resetSequelizeInstance (instance: Model<any>, savedFields: object) { 69function resetSequelizeInstance (instance: Model<any>, savedFields: object) {
70 Object.keys(savedFields).forEach(key => { 70 Object.keys(savedFields).forEach(key => {
71 const value = savedFields[key] 71 instance[key] = savedFields[key]
72 instance.set(key, value)
73 }) 72 })
74} 73}
75 74
diff --git a/server/initializers/database.ts b/server/initializers/database.ts
index 8f237eb23..d1744d21f 100644
--- a/server/initializers/database.ts
+++ b/server/initializers/database.ts
@@ -37,6 +37,7 @@ import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-pla
37import { VideoPlaylistModel } from '../models/video/video-playlist' 37import { VideoPlaylistModel } from '../models/video/video-playlist'
38import { VideoPlaylistElementModel } from '../models/video/video-playlist-element' 38import { VideoPlaylistElementModel } from '../models/video/video-playlist-element'
39import { ThumbnailModel } from '../models/video/thumbnail' 39import { ThumbnailModel } from '../models/video/thumbnail'
40import { QueryTypes, Transaction } from 'sequelize'
40 41
41require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string 42require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string
42 43
@@ -58,8 +59,7 @@ const sequelizeTypescript = new SequelizeTypescript({
58 max: poolMax 59 max: poolMax
59 }, 60 },
60 benchmark: isTestInstance(), 61 benchmark: isTestInstance(),
61 isolationLevel: SequelizeTypescript.Transaction.ISOLATION_LEVELS.SERIALIZABLE, 62 isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE,
62 operatorsAliases: false,
63 logging: (message: string, benchmark: number) => { 63 logging: (message: string, benchmark: number) => {
64 if (process.env.NODE_DB_LOG === 'false') return 64 if (process.env.NODE_DB_LOG === 'false') return
65 65
@@ -141,10 +141,15 @@ async function checkPostgresExtensions () {
141 141
142async function checkPostgresExtension (extension: string) { 142async function checkPostgresExtension (extension: string) {
143 const query = `SELECT true AS enabled FROM pg_available_extensions WHERE name = '${extension}' AND installed_version IS NOT NULL;` 143 const query = `SELECT true AS enabled FROM pg_available_extensions WHERE name = '${extension}' AND installed_version IS NOT NULL;`
144 const [ res ] = await sequelizeTypescript.query(query, { raw: true }) 144 const options = {
145 type: QueryTypes.SELECT as QueryTypes.SELECT,
146 raw: true
147 }
148
149 const res = await sequelizeTypescript.query<{ enabled: boolean }>(query, options)
145 150
146 if (!res || res.length === 0 || res[ 0 ][ 'enabled' ] !== true) { 151 if (!res || res.length === 0 || res[ 0 ][ 'enabled' ] !== true) {
147 // Try to create the extension ourself 152 // Try to create the extension ourselves
148 try { 153 try {
149 await sequelizeTypescript.query(`CREATE EXTENSION ${extension};`, { raw: true }) 154 await sequelizeTypescript.query(`CREATE EXTENSION ${extension};`, { raw: true })
150 155
diff --git a/server/initializers/migrations/0080-video-channels.ts b/server/initializers/migrations/0080-video-channels.ts
index 0e17a4ccd..5512bdcf1 100644
--- a/server/initializers/migrations/0080-video-channels.ts
+++ b/server/initializers/migrations/0080-video-channels.ts
@@ -69,7 +69,7 @@ async function up (utils: {
69 const options = { 69 const options = {
70 type: Sequelize.QueryTypes.SELECT 70 type: Sequelize.QueryTypes.SELECT
71 } 71 }
72 const rawVideos = await utils.sequelize.query(query, options) 72 const rawVideos = await utils.sequelize.query(query, options) as any
73 73
74 for (const rawVideo of rawVideos) { 74 for (const rawVideo of rawVideos) {
75 const videoChannel = await utils.db.VideoChannel.findOne({ where: { authorId: rawVideo.authorId } }) 75 const videoChannel = await utils.db.VideoChannel.findOne({ where: { authorId: rawVideo.authorId } })
diff --git a/server/initializers/migrations/0100-activitypub.ts b/server/initializers/migrations/0100-activitypub.ts
index a7ebd804c..2880a97d9 100644
--- a/server/initializers/migrations/0100-activitypub.ts
+++ b/server/initializers/migrations/0100-activitypub.ts
@@ -21,7 +21,7 @@ async function up (utils: {
21 const options = { 21 const options = {
22 type: Sequelize.QueryTypes.SELECT 22 type: Sequelize.QueryTypes.SELECT
23 } 23 }
24 const res = await utils.sequelize.query(query, options) 24 const res = await utils.sequelize.query(query, options) as any
25 25
26 if (!res[0] || res[0].total !== 0) { 26 if (!res[0] || res[0].total !== 0) {
27 throw new Error('You need to quit friends.') 27 throw new Error('You need to quit friends.')
@@ -68,8 +68,8 @@ async function up (utils: {
68 const accountCreated = await createLocalAccountWithoutKeys(SERVER_ACTOR_NAME, null, applicationInstance.id, undefined) 68 const accountCreated = await createLocalAccountWithoutKeys(SERVER_ACTOR_NAME, null, applicationInstance.id, undefined)
69 69
70 const { publicKey, privateKey } = await createPrivateAndPublicKeys() 70 const { publicKey, privateKey } = await createPrivateAndPublicKeys()
71 accountCreated.set('publicKey', publicKey) 71 accountCreated.Actor.publicKey = publicKey
72 accountCreated.set('privateKey', privateKey) 72 accountCreated.Actor.privateKey = privateKey
73 73
74 await accountCreated.save() 74 await accountCreated.save()
75 } 75 }
@@ -86,8 +86,8 @@ async function up (utils: {
86 const account = await createLocalAccountWithoutKeys(user.username, user.id, null, undefined) 86 const account = await createLocalAccountWithoutKeys(user.username, user.id, null, undefined)
87 87
88 const { publicKey, privateKey } = await createPrivateAndPublicKeys() 88 const { publicKey, privateKey } = await createPrivateAndPublicKeys()
89 account.set('publicKey', publicKey) 89 account.Actor.publicKey = publicKey
90 account.set('privateKey', privateKey) 90 account.Actor.privateKey = privateKey
91 await account.save() 91 await account.save()
92 } 92 }
93 93
diff --git a/server/initializers/migrations/0135-video-channel-actor.ts b/server/initializers/migrations/0135-video-channel-actor.ts
index 033f43b68..5ace0f4d2 100644
--- a/server/initializers/migrations/0135-video-channel-actor.ts
+++ b/server/initializers/migrations/0135-video-channel-actor.ts
@@ -239,7 +239,8 @@ async function up (utils: {
239 239
240 { 240 {
241 const query = 'SELECT * FROM "actor" WHERE "serverId" IS NULL AND "publicKey" IS NULL' 241 const query = 'SELECT * FROM "actor" WHERE "serverId" IS NULL AND "publicKey" IS NULL'
242 const [ res ] = await utils.sequelize.query(query) 242 const options = { type: Sequelize.QueryTypes.SELECT as Sequelize.QueryTypes.SELECT }
243 const [ res ] = await utils.sequelize.query(query, options)
243 244
244 for (const actor of res) { 245 for (const actor of res) {
245 const { privateKey, publicKey } = await createPrivateAndPublicKeys() 246 const { privateKey, publicKey } = await createPrivateAndPublicKeys()
diff --git a/server/initializers/migrations/0345-video-playlists.ts b/server/initializers/migrations/0345-video-playlists.ts
index 6953f5553..de69f5b9e 100644
--- a/server/initializers/migrations/0345-video-playlists.ts
+++ b/server/initializers/migrations/0345-video-playlists.ts
@@ -51,7 +51,9 @@ CREATE TABLE IF NOT EXISTS "videoPlaylistElement"
51 51
52 { 52 {
53 const userQuery = 'SELECT "username" FROM "user";' 53 const userQuery = 'SELECT "username" FROM "user";'
54 const userResult = await utils.sequelize.query(userQuery, { transaction, type: Sequelize.QueryTypes.SELECT }) 54
55 const options = { transaction, type: Sequelize.QueryTypes.SELECT as Sequelize.QueryTypes.SELECT }
56 const userResult = await utils.sequelize.query<{ username: string }>(userQuery, options)
55 const usernames = userResult.map(r => r.username) 57 const usernames = userResult.map(r => r.username)
56 58
57 for (const username of usernames) { 59 for (const username of usernames) {
diff --git a/server/initializers/migrator.ts b/server/initializers/migrator.ts
index adc2f9fb3..1cb0116b7 100644
--- a/server/initializers/migrator.ts
+++ b/server/initializers/migrator.ts
@@ -3,6 +3,7 @@ import { logger } from '../helpers/logger'
3import { LAST_MIGRATION_VERSION } from './constants' 3import { LAST_MIGRATION_VERSION } from './constants'
4import { sequelizeTypescript } from './database' 4import { sequelizeTypescript } from './database'
5import { readdir } from 'fs-extra' 5import { readdir } from 'fs-extra'
6import { QueryTypes } from 'sequelize'
6 7
7async function migrate () { 8async function migrate () {
8 const tables = await sequelizeTypescript.getQueryInterface().showAllTables() 9 const tables = await sequelizeTypescript.getQueryInterface().showAllTables()
@@ -13,7 +14,12 @@ async function migrate () {
13 14
14 let actualVersion: number | null = null 15 let actualVersion: number | null = null
15 16
16 const [ rows ] = await sequelizeTypescript.query('SELECT "migrationVersion" FROM "application"') 17 const query = 'SELECT "migrationVersion" FROM "application"'
18 const options = {
19 type: QueryTypes.SELECT as QueryTypes.SELECT
20 }
21
22 const rows = await sequelizeTypescript.query<{ migrationVersion: number }>(query, options)
17 if (rows && rows[0] && rows[0].migrationVersion) { 23 if (rows && rows[0] && rows[0].migrationVersion) {
18 actualVersion = rows[0].migrationVersion 24 actualVersion = rows[0].migrationVersion
19 } 25 }
diff --git a/server/lib/activitypub/cache-file.ts b/server/lib/activitypub/cache-file.ts
index 597003135..de5cc54ac 100644
--- a/server/lib/activitypub/cache-file.ts
+++ b/server/lib/activitypub/cache-file.ts
@@ -68,8 +68,8 @@ function updateCacheFile (
68 68
69 const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor) 69 const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor)
70 70
71 redundancyModel.set('expires', attributes.expiresOn) 71 redundancyModel.expiresOn = attributes.expiresOn
72 redundancyModel.set('fileUrl', attributes.fileUrl) 72 redundancyModel.fileUrl = attributes.fileUrl
73 73
74 return redundancyModel.save({ transaction: t }) 74 return redundancyModel.save({ transaction: t })
75} 75}
diff --git a/server/lib/activitypub/playlist.ts b/server/lib/activitypub/playlist.ts
index 341e469f3..721c19603 100644
--- a/server/lib/activitypub/playlist.ts
+++ b/server/lib/activitypub/playlist.ts
@@ -14,7 +14,6 @@ import { getOrCreateVideoAndAccountAndChannel } from './videos'
14import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist' 14import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist'
15import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element' 15import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element'
16import { VideoModel } from '../../models/video/video' 16import { VideoModel } from '../../models/video/video'
17import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model'
18import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' 17import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
19import { sequelizeTypescript } from '../../initializers/database' 18import { sequelizeTypescript } from '../../initializers/database'
20import { createPlaylistThumbnailFromUrl } from '../thumbnail' 19import { createPlaylistThumbnailFromUrl } from '../thumbnail'
@@ -87,7 +86,8 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAc
87 } 86 }
88 } 87 }
89 88
90 const [ playlist ] = await VideoPlaylistModel.upsert<VideoPlaylistModel>(playlistAttributes, { returning: true }) 89 // FIXME: sequelize typings
90 const [ playlist ] = (await VideoPlaylistModel.upsert<VideoPlaylistModel>(playlistAttributes, { returning: true }) as any)
91 91
92 let accItems: string[] = [] 92 let accItems: string[] = []
93 await crawlCollectionPage<string>(playlistObject.id, items => { 93 await crawlCollectionPage<string>(playlistObject.id, items => {
@@ -156,7 +156,7 @@ export {
156// --------------------------------------------------------------------------- 156// ---------------------------------------------------------------------------
157 157
158async function resetVideoPlaylistElements (elementUrls: string[], playlist: VideoPlaylistModel) { 158async function resetVideoPlaylistElements (elementUrls: string[], playlist: VideoPlaylistModel) {
159 const elementsToCreate: FilteredModelAttributes<VideoPlaylistElementModel>[] = [] 159 const elementsToCreate: object[] = [] // FIXME: sequelize typings
160 160
161 await Bluebird.map(elementUrls, async elementUrl => { 161 await Bluebird.map(elementUrls, async elementUrl => {
162 try { 162 try {
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts
index ac3dd6ac4..ed16ba172 100644
--- a/server/lib/activitypub/process/process-follow.ts
+++ b/server/lib/activitypub/process/process-follow.ts
@@ -37,7 +37,9 @@ async function processFollow (actor: ActorModel, targetActorURL: string) {
37 if (isFollowingInstance && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) { 37 if (isFollowingInstance && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
38 logger.info('Rejecting %s because instance followers are disabled.', targetActor.url) 38 logger.info('Rejecting %s because instance followers are disabled.', targetActor.url)
39 39
40 return sendReject(actor, targetActor) 40 await sendReject(actor, targetActor)
41
42 return { actorFollow: undefined }
41 } 43 }
42 44
43 const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({ 45 const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts
index 0b96ba352..54a9234bb 100644
--- a/server/lib/activitypub/process/process-update.ts
+++ b/server/lib/activitypub/process/process-update.ts
@@ -120,9 +120,11 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate)
120 120
121 await actor.save({ transaction: t }) 121 await actor.save({ transaction: t })
122 122
123 accountOrChannelInstance.set('name', actorAttributesToUpdate.name || actorAttributesToUpdate.preferredUsername) 123 accountOrChannelInstance.name = actorAttributesToUpdate.name || actorAttributesToUpdate.preferredUsername
124 accountOrChannelInstance.set('description', actorAttributesToUpdate.summary) 124 accountOrChannelInstance.description = actorAttributesToUpdate.summary
125 accountOrChannelInstance.set('support', actorAttributesToUpdate.support) 125
126 if (accountOrChannelInstance instanceof VideoChannelModel) accountOrChannelInstance.support = actorAttributesToUpdate.support
127
126 await accountOrChannelInstance.save({ transaction: t }) 128 await accountOrChannelInstance.save({ transaction: t })
127 }) 129 })
128 130
diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts
index 18f44d50e..cb67bf9a4 100644
--- a/server/lib/activitypub/video-comments.ts
+++ b/server/lib/activitypub/video-comments.ts
@@ -73,7 +73,8 @@ async function addVideoComment (videoInstance: VideoModel, commentUrl: string) {
73 const entry = await videoCommentActivityObjectToDBAttributes(videoInstance, actor, body) 73 const entry = await videoCommentActivityObjectToDBAttributes(videoInstance, actor, body)
74 if (!entry) return { created: false } 74 if (!entry) return { created: false }
75 75
76 const [ comment, created ] = await VideoCommentModel.upsert<VideoCommentModel>(entry, { returning: true }) 76 // FIXME: sequelize typings
77 const [ comment, created ] = (await VideoCommentModel.upsert<VideoCommentModel>(entry, { returning: true }) as any)
77 comment.Account = actor.Account 78 comment.Account = actor.Account
78 comment.Video = videoInstance 79 comment.Video = videoInstance
79 80
diff --git a/server/lib/activitypub/video-rates.ts b/server/lib/activitypub/video-rates.ts
index 7809c58b8..cda5b2981 100644
--- a/server/lib/activitypub/video-rates.ts
+++ b/server/lib/activitypub/video-rates.ts
@@ -56,7 +56,10 @@ async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRa
56 logger.info('Adding %d %s to video %s.', rateCounts, rate, video.uuid) 56 logger.info('Adding %d %s to video %s.', rateCounts, rate, video.uuid)
57 57
58 // This is "likes" and "dislikes" 58 // This is "likes" and "dislikes"
59 if (rateCounts !== 0) await video.increment(rate + 's', { by: rateCounts }) 59 if (rateCounts !== 0) {
60 const field = rate === 'like' ? 'likes' : 'dislikes'
61 await video.increment(field, { by: rateCounts })
62 }
60 63
61 return 64 return
62} 65}
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index 16c37a55f..5a56942a9 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -6,7 +6,7 @@ import {
6 ActivityPlaylistSegmentHashesObject, 6 ActivityPlaylistSegmentHashesObject,
7 ActivityPlaylistUrlObject, 7 ActivityPlaylistUrlObject,
8 ActivityUrlObject, 8 ActivityUrlObject,
9 ActivityVideoUrlObject, VideoCreate, 9 ActivityVideoUrlObject,
10 VideoState 10 VideoState
11} from '../../../shared/index' 11} from '../../../shared/index'
12import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' 12import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
@@ -45,7 +45,6 @@ import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
45import { Notifier } from '../notifier' 45import { Notifier } from '../notifier'
46import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' 46import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist'
47import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' 47import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
48import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model'
49import { AccountVideoRateModel } from '../../models/account/account-video-rate' 48import { AccountVideoRateModel } from '../../models/account/account-video-rate'
50import { VideoShareModel } from '../../models/video/video-share' 49import { VideoShareModel } from '../../models/video/video-share'
51import { VideoCommentModel } from '../../models/video/video-comment' 50import { VideoCommentModel } from '../../models/video/video-comment'
@@ -312,7 +311,7 @@ async function updateVideoFromAP (options: {
312 311
313 // Update or add other one 312 // Update or add other one
314 const upsertTasks = videoFileAttributes.map(a => { 313 const upsertTasks = videoFileAttributes.map(a => {
315 return VideoFileModel.upsert<VideoFileModel>(a, { returning: true, transaction: t }) 314 return (VideoFileModel.upsert<VideoFileModel>(a, { returning: true, transaction: t }) as any) // FIXME: sequelize typings
316 .then(([ file ]) => file) 315 .then(([ file ]) => file)
317 }) 316 })
318 317
@@ -335,7 +334,8 @@ async function updateVideoFromAP (options: {
335 334
336 // Update or add other one 335 // Update or add other one
337 const upsertTasks = streamingPlaylistAttributes.map(a => { 336 const upsertTasks = streamingPlaylistAttributes.map(a => {
338 return VideoStreamingPlaylistModel.upsert<VideoStreamingPlaylistModel>(a, { returning: true, transaction: t }) 337 // FIXME: sequelize typings
338 return (VideoStreamingPlaylistModel.upsert<VideoStreamingPlaylistModel>(a, { returning: true, transaction: t }) as any)
339 .then(([ streamingPlaylist ]) => streamingPlaylist) 339 .then(([ streamingPlaylist ]) => streamingPlaylist)
340 }) 340 })
341 341
@@ -594,7 +594,7 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid
594 throw new Error('Cannot find video files for ' + video.url) 594 throw new Error('Cannot find video files for ' + video.url)
595 } 595 }
596 596
597 const attributes: FilteredModelAttributes<VideoFileModel>[] = [] 597 const attributes: object[] = [] // FIXME: add typings
598 for (const fileUrl of fileUrls) { 598 for (const fileUrl of fileUrls) {
599 // Fetch associated magnet uri 599 // Fetch associated magnet uri
600 const magnet = videoObject.url.find(u => { 600 const magnet = videoObject.url.find(u => {
@@ -629,7 +629,7 @@ function streamingPlaylistActivityUrlToDBAttributes (video: VideoModel, videoObj
629 const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[] 629 const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[]
630 if (playlistUrls.length === 0) return [] 630 if (playlistUrls.length === 0) return []
631 631
632 const attributes: FilteredModelAttributes<VideoStreamingPlaylistModel>[] = [] 632 const attributes: object[] = [] // FIXME: add typings
633 for (const playlistUrlObject of playlistUrls) { 633 for (const playlistUrlObject of playlistUrls) {
634 const segmentsSha256UrlObject = playlistUrlObject.tag 634 const segmentsSha256UrlObject = playlistUrlObject.tag
635 .find(t => { 635 .find(t => {
diff --git a/server/lib/user.ts b/server/lib/user.ts
index ce0d60518..7badb3e72 100644
--- a/server/lib/user.ts
+++ b/server/lib/user.ts
@@ -7,7 +7,6 @@ import { UserModel } from '../models/account/user'
7import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' 7import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub'
8import { createVideoChannel } from './video-channel' 8import { createVideoChannel } from './video-channel'
9import { VideoChannelModel } from '../models/video/video-channel' 9import { VideoChannelModel } from '../models/video/video-channel'
10import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model'
11import { ActorModel } from '../models/activitypub/actor' 10import { ActorModel } from '../models/activitypub/actor'
12import { UserNotificationSettingModel } from '../models/account/user-notification-setting' 11import { UserNotificationSettingModel } from '../models/account/user-notification-setting'
13import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' 12import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users'
@@ -73,7 +72,7 @@ async function createLocalAccountWithoutKeys (
73 userId, 72 userId,
74 applicationId, 73 applicationId,
75 actorId: actorInstanceCreated.id 74 actorId: actorInstanceCreated.id
76 } as FilteredModelAttributes<AccountModel>) 75 })
77 76
78 const accountInstanceCreated = await accountInstance.save({ transaction: t }) 77 const accountInstanceCreated = await accountInstance.save({ transaction: t })
79 accountInstanceCreated.Actor = actorInstanceCreated 78 accountInstanceCreated.Actor = actorInstanceCreated
diff --git a/server/lib/video-comment.ts b/server/lib/video-comment.ts
index 59bce7520..bfe22d225 100644
--- a/server/lib/video-comment.ts
+++ b/server/lib/video-comment.ts
@@ -28,7 +28,7 @@ async function createVideoComment (obj: {
28 videoId: obj.video.id, 28 videoId: obj.video.id,
29 accountId: obj.account.id, 29 accountId: obj.account.id,
30 url: 'fake url' 30 url: 'fake url'
31 }, { transaction: t, validate: false }) 31 }, { transaction: t, validate: false } as any) // FIXME: sequelize typings
32 32
33 comment.set('url', getVideoCommentActivityPubUrl(obj.video, comment)) 33 comment.set('url', getVideoCommentActivityPubUrl(obj.video, comment))
34 34
diff --git a/server/models/account/account-video-rate.ts b/server/models/account/account-video-rate.ts
index 78a897a65..59f586b54 100644
--- a/server/models/account/account-video-rate.ts
+++ b/server/models/account/account-video-rate.ts
@@ -1,16 +1,15 @@
1import { values } from 'lodash' 1import { values } from 'lodash'
2import { Transaction, Op } from 'sequelize' 2import { FindOptions, Op, Transaction } from 'sequelize'
3import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 3import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
4import { IFindOptions } from 'sequelize-typescript/lib/interfaces/IFindOptions'
5import { VideoRateType } from '../../../shared/models/videos' 4import { VideoRateType } from '../../../shared/models/videos'
6import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants' 5import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants'
7import { VideoModel } from '../video/video' 6import { VideoModel } from '../video/video'
8import { AccountModel } from './account' 7import { AccountModel } from './account'
9import { ActorModel } from '../activitypub/actor' 8import { ActorModel } from '../activitypub/actor'
10import { throwIfNotValid, getSort } from '../utils' 9import { getSort, throwIfNotValid } from '../utils'
11import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 10import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
12import { AccountVideoRate } from '../../../shared' 11import { AccountVideoRate } from '../../../shared'
13import { VideoChannelModel, ScopeNames as VideoChannelScopeNames } from '../video/video-channel' 12import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from '../video/video-channel'
14 13
15/* 14/*
16 Account rates per video. 15 Account rates per video.
@@ -40,7 +39,7 @@ import { VideoChannelModel, ScopeNames as VideoChannelScopeNames } from '../vide
40export class AccountVideoRateModel extends Model<AccountVideoRateModel> { 39export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
41 40
42 @AllowNull(false) 41 @AllowNull(false)
43 @Column(DataType.ENUM(values(VIDEO_RATE_TYPES))) 42 @Column(DataType.ENUM(...values(VIDEO_RATE_TYPES)))
44 type: VideoRateType 43 type: VideoRateType
45 44
46 @AllowNull(false) 45 @AllowNull(false)
@@ -79,7 +78,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
79 Account: AccountModel 78 Account: AccountModel
80 79
81 static load (accountId: number, videoId: number, transaction?: Transaction) { 80 static load (accountId: number, videoId: number, transaction?: Transaction) {
82 const options: IFindOptions<AccountVideoRateModel> = { 81 const options: FindOptions = {
83 where: { 82 where: {
84 accountId, 83 accountId,
85 videoId 84 videoId
@@ -97,7 +96,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
97 type?: string, 96 type?: string,
98 accountId: number 97 accountId: number
99 }) { 98 }) {
100 const query: IFindOptions<AccountVideoRateModel> = { 99 const query: FindOptions = {
101 offset: options.start, 100 offset: options.start,
102 limit: options.count, 101 limit: options.count,
103 order: getSort(options.sort), 102 order: getSort(options.sort),
@@ -123,7 +122,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
123 } 122 }
124 123
125 static loadLocalAndPopulateVideo (rateType: VideoRateType, accountName: string, videoId: number, transaction?: Transaction) { 124 static loadLocalAndPopulateVideo (rateType: VideoRateType, accountName: string, videoId: number, transaction?: Transaction) {
126 const options: IFindOptions<AccountVideoRateModel> = { 125 const options: FindOptions = {
127 where: { 126 where: {
128 videoId, 127 videoId,
129 type: rateType 128 type: rateType
@@ -155,7 +154,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
155 } 154 }
156 155
157 static loadByUrl (url: string, transaction: Transaction) { 156 static loadByUrl (url: string, transaction: Transaction) {
158 const options: IFindOptions<AccountVideoRateModel> = { 157 const options: FindOptions = {
159 where: { 158 where: {
160 url 159 url
161 } 160 }
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index 6f425024e..bf2ed0a61 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -1,4 +1,3 @@
1import * as Sequelize from 'sequelize'
2import { 1import {
3 AllowNull, 2 AllowNull,
4 BeforeDestroy, 3 BeforeDestroy,
@@ -28,6 +27,7 @@ import { UserModel } from './user'
28import { AvatarModel } from '../avatar/avatar' 27import { AvatarModel } from '../avatar/avatar'
29import { VideoPlaylistModel } from '../video/video-playlist' 28import { VideoPlaylistModel } from '../video/video-playlist'
30import { WEBSERVER } from '../../initializers/constants' 29import { WEBSERVER } from '../../initializers/constants'
30import { Op, Transaction, WhereOptions } from 'sequelize'
31 31
32export enum ScopeNames { 32export enum ScopeNames {
33 SUMMARY = 'SUMMARY' 33 SUMMARY = 'SUMMARY'
@@ -42,7 +42,7 @@ export enum ScopeNames {
42 ] 42 ]
43}) 43})
44@Scopes({ 44@Scopes({
45 [ ScopeNames.SUMMARY ]: (whereActor?: Sequelize.WhereOptions<ActorModel>) => { 45 [ ScopeNames.SUMMARY ]: (whereActor?: WhereOptions) => {
46 return { 46 return {
47 attributes: [ 'id', 'name' ], 47 attributes: [ 'id', 'name' ],
48 include: [ 48 include: [
@@ -90,7 +90,7 @@ export class AccountModel extends Model<AccountModel> {
90 90
91 @AllowNull(true) 91 @AllowNull(true)
92 @Default(null) 92 @Default(null)
93 @Is('AccountDescription', value => throwIfNotValid(value, isAccountDescriptionValid, 'description')) 93 @Is('AccountDescription', value => throwIfNotValid(value, isAccountDescriptionValid, 'description', true))
94 @Column 94 @Column
95 description: string 95 description: string
96 96
@@ -176,7 +176,7 @@ export class AccountModel extends Model<AccountModel> {
176 return undefined 176 return undefined
177 } 177 }
178 178
179 static load (id: number, transaction?: Sequelize.Transaction) { 179 static load (id: number, transaction?: Transaction) {
180 return AccountModel.findByPk(id, { transaction }) 180 return AccountModel.findByPk(id, { transaction })
181 } 181 }
182 182
@@ -207,15 +207,15 @@ export class AccountModel extends Model<AccountModel> {
207 static loadLocalByName (name: string) { 207 static loadLocalByName (name: string) {
208 const query = { 208 const query = {
209 where: { 209 where: {
210 [ Sequelize.Op.or ]: [ 210 [ Op.or ]: [
211 { 211 {
212 userId: { 212 userId: {
213 [ Sequelize.Op.ne ]: null 213 [ Op.ne ]: null
214 } 214 }
215 }, 215 },
216 { 216 {
217 applicationId: { 217 applicationId: {
218 [ Sequelize.Op.ne ]: null 218 [ Op.ne ]: null
219 } 219 }
220 } 220 }
221 ] 221 ]
@@ -259,7 +259,7 @@ export class AccountModel extends Model<AccountModel> {
259 return AccountModel.findOne(query) 259 return AccountModel.findOne(query)
260 } 260 }
261 261
262 static loadByUrl (url: string, transaction?: Sequelize.Transaction) { 262 static loadByUrl (url: string, transaction?: Transaction) {
263 const query = { 263 const query = {
264 include: [ 264 include: [
265 { 265 {
diff --git a/server/models/account/user-notification.ts b/server/models/account/user-notification.ts
index 33480f3b5..08388f268 100644
--- a/server/models/account/user-notification.ts
+++ b/server/models/account/user-notification.ts
@@ -1,17 +1,4 @@
1import { 1import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
2 AllowNull,
3 BelongsTo,
4 Column,
5 CreatedAt,
6 Default,
7 ForeignKey,
8 IFindOptions,
9 Is,
10 Model,
11 Scopes,
12 Table,
13 UpdatedAt
14} from 'sequelize-typescript'
15import { UserNotification, UserNotificationType } from '../../../shared' 2import { UserNotification, UserNotificationType } from '../../../shared'
16import { getSort, throwIfNotValid } from '../utils' 3import { getSort, throwIfNotValid } from '../utils'
17import { isBooleanValid } from '../../helpers/custom-validators/misc' 4import { isBooleanValid } from '../../helpers/custom-validators/misc'
@@ -19,7 +6,7 @@ import { isUserNotificationTypeValid } from '../../helpers/custom-validators/use
19import { UserModel } from './user' 6import { UserModel } from './user'
20import { VideoModel } from '../video/video' 7import { VideoModel } from '../video/video'
21import { VideoCommentModel } from '../video/video-comment' 8import { VideoCommentModel } from '../video/video-comment'
22import { Op } from 'sequelize' 9import { FindOptions, Op } from 'sequelize'
23import { VideoChannelModel } from '../video/video-channel' 10import { VideoChannelModel } from '../video/video-channel'
24import { AccountModel } from './account' 11import { AccountModel } from './account'
25import { VideoAbuseModel } from '../video/video-abuse' 12import { VideoAbuseModel } from '../video/video-abuse'
@@ -160,7 +147,7 @@ function buildAccountInclude (required: boolean, withActor = false) {
160 }, 147 },
161 148
162 buildAccountInclude(false, true) 149 buildAccountInclude(false, true)
163 ] 150 ] as any // FIXME: sequelize typings
164 } 151 }
165}) 152})
166@Table({ 153@Table({
@@ -225,7 +212,7 @@ function buildAccountInclude (required: boolean, withActor = false) {
225 } 212 }
226 } 213 }
227 } 214 }
228 ] 215 ] as any // FIXME: sequelize typings
229}) 216})
230export class UserNotificationModel extends Model<UserNotificationModel> { 217export class UserNotificationModel extends Model<UserNotificationModel> {
231 218
@@ -344,7 +331,7 @@ export class UserNotificationModel extends Model<UserNotificationModel> {
344 ActorFollow: ActorFollowModel 331 ActorFollow: ActorFollowModel
345 332
346 static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) { 333 static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) {
347 const query: IFindOptions<UserNotificationModel> = { 334 const query: FindOptions = {
348 offset: start, 335 offset: start,
349 limit: count, 336 limit: count,
350 order: getSort(sort), 337 order: getSort(sort),
diff --git a/server/models/account/user-video-history.ts b/server/models/account/user-video-history.ts
index 49d2def81..a862fc45f 100644
--- a/server/models/account/user-video-history.ts
+++ b/server/models/account/user-video-history.ts
@@ -76,7 +76,7 @@ export class UserVideoHistoryModel extends Model<UserVideoHistoryModel> {
76 } 76 }
77 77
78 if (beforeDate) { 78 if (beforeDate) {
79 query.where.updatedAt = { 79 query.where['updatedAt'] = {
80 [Op.lt]: beforeDate 80 [Op.lt]: beforeDate
81 } 81 }
82 } 82 }
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index b66458351..8bd0397dd 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -80,7 +80,7 @@ enum ScopeNames {
80 model: () => UserNotificationSettingModel, 80 model: () => UserNotificationSettingModel,
81 required: true 81 required: true
82 } 82 }
83 ] 83 ] as any // FIXME: sequelize typings
84 } 84 }
85}) 85})
86@Table({ 86@Table({
@@ -115,13 +115,13 @@ export class UserModel extends Model<UserModel> {
115 115
116 @AllowNull(true) 116 @AllowNull(true)
117 @Default(null) 117 @Default(null)
118 @Is('UserEmailVerified', value => throwIfNotValid(value, isUserEmailVerifiedValid, 'email verified boolean')) 118 @Is('UserEmailVerified', value => throwIfNotValid(value, isUserEmailVerifiedValid, 'email verified boolean', true))
119 @Column 119 @Column
120 emailVerified: boolean 120 emailVerified: boolean
121 121
122 @AllowNull(false) 122 @AllowNull(false)
123 @Is('UserNSFWPolicy', value => throwIfNotValid(value, isUserNSFWPolicyValid, 'NSFW policy')) 123 @Is('UserNSFWPolicy', value => throwIfNotValid(value, isUserNSFWPolicyValid, 'NSFW policy'))
124 @Column(DataType.ENUM(values(NSFW_POLICY_TYPES))) 124 @Column(DataType.ENUM(...values(NSFW_POLICY_TYPES)))
125 nsfwPolicy: NSFWPolicyType 125 nsfwPolicy: NSFWPolicyType
126 126
127 @AllowNull(false) 127 @AllowNull(false)
@@ -156,7 +156,7 @@ export class UserModel extends Model<UserModel> {
156 156
157 @AllowNull(true) 157 @AllowNull(true)
158 @Default(null) 158 @Default(null)
159 @Is('UserBlockedReason', value => throwIfNotValid(value, isUserBlockedReasonValid, 'blocked reason')) 159 @Is('UserBlockedReason', value => throwIfNotValid(value, isUserBlockedReasonValid, 'blocked reason', true))
160 @Column 160 @Column
161 blockedReason: string 161 blockedReason: string
162 162
@@ -556,10 +556,10 @@ export class UserModel extends Model<UserModel> {
556 notificationSettings: this.NotificationSetting ? this.NotificationSetting.toFormattedJSON() : undefined, 556 notificationSettings: this.NotificationSetting ? this.NotificationSetting.toFormattedJSON() : undefined,
557 videoChannels: [], 557 videoChannels: [],
558 videoQuotaUsed: videoQuotaUsed !== undefined 558 videoQuotaUsed: videoQuotaUsed !== undefined
559 ? parseInt(videoQuotaUsed, 10) 559 ? parseInt(videoQuotaUsed + '', 10)
560 : undefined, 560 : undefined,
561 videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined 561 videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined
562 ? parseInt(videoQuotaUsedDaily, 10) 562 ? parseInt(videoQuotaUsedDaily + '', 10)
563 : undefined 563 : undefined
564 } 564 }
565 565
@@ -619,14 +619,14 @@ export class UserModel extends Model<UserModel> {
619 private static getTotalRawQuery (query: string, userId: number) { 619 private static getTotalRawQuery (query: string, userId: number) {
620 const options = { 620 const options = {
621 bind: { userId }, 621 bind: { userId },
622 type: Sequelize.QueryTypes.SELECT 622 type: Sequelize.QueryTypes.SELECT as Sequelize.QueryTypes.SELECT
623 } 623 }
624 624
625 return UserModel.sequelize.query(query, options) 625 return UserModel.sequelize.query<{ total: number }>(query, options)
626 .then(([ { total } ]) => { 626 .then(([ { total } ]) => {
627 if (total === null) return 0 627 if (total === null) return 0
628 628
629 return parseInt(total, 10) 629 return parseInt(total + '', 10)
630 }) 630 })
631 } 631 }
632} 632}
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/activitypub/actor-follow.ts
index f9b4f57f3..b0461b981 100644
--- a/server/models/activitypub/actor-follow.ts
+++ b/server/models/activitypub/actor-follow.ts
@@ -1,6 +1,5 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import { values } from 'lodash' 2import { values } from 'lodash'
3import * as Sequelize from 'sequelize'
4import { 3import {
5 AfterCreate, 4 AfterCreate,
6 AfterDestroy, 5 AfterDestroy,
@@ -27,8 +26,8 @@ import { ServerModel } from '../server/server'
27import { getSort } from '../utils' 26import { getSort } from '../utils'
28import { ActorModel, unusedActorAttributesForAPI } from './actor' 27import { ActorModel, unusedActorAttributesForAPI } from './actor'
29import { VideoChannelModel } from '../video/video-channel' 28import { VideoChannelModel } from '../video/video-channel'
30import { IIncludeOptions } from '../../../node_modules/sequelize-typescript/lib/interfaces/IIncludeOptions'
31import { AccountModel } from '../account/account' 29import { AccountModel } from '../account/account'
30import { IncludeOptions, Op, Transaction, QueryTypes } from 'sequelize'
32 31
33@Table({ 32@Table({
34 tableName: 'actorFollow', 33 tableName: 'actorFollow',
@@ -51,7 +50,7 @@ import { AccountModel } from '../account/account'
51export class ActorFollowModel extends Model<ActorFollowModel> { 50export class ActorFollowModel extends Model<ActorFollowModel> {
52 51
53 @AllowNull(false) 52 @AllowNull(false)
54 @Column(DataType.ENUM(values(FOLLOW_STATES))) 53 @Column(DataType.ENUM(...values(FOLLOW_STATES)))
55 state: FollowState 54 state: FollowState
56 55
57 @AllowNull(false) 56 @AllowNull(false)
@@ -126,7 +125,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
126 if (numberOfActorFollowsRemoved) logger.info('Removed bad %d actor follows.', numberOfActorFollowsRemoved) 125 if (numberOfActorFollowsRemoved) logger.info('Removed bad %d actor follows.', numberOfActorFollowsRemoved)
127 } 126 }
128 127
129 static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Sequelize.Transaction) { 128 static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction) {
130 const query = { 129 const query = {
131 where: { 130 where: {
132 actorId, 131 actorId,
@@ -150,8 +149,8 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
150 return ActorFollowModel.findOne(query) 149 return ActorFollowModel.findOne(query)
151 } 150 }
152 151
153 static loadByActorAndTargetNameAndHostForAPI (actorId: number, targetName: string, targetHost: string, t?: Sequelize.Transaction) { 152 static loadByActorAndTargetNameAndHostForAPI (actorId: number, targetName: string, targetHost: string, t?: Transaction) {
154 const actorFollowingPartInclude: IIncludeOptions = { 153 const actorFollowingPartInclude: IncludeOptions = {
155 model: ActorModel, 154 model: ActorModel,
156 required: true, 155 required: true,
157 as: 'ActorFollowing', 156 as: 'ActorFollowing',
@@ -208,7 +207,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
208 .map(t => { 207 .map(t => {
209 if (t.host) { 208 if (t.host) {
210 return { 209 return {
211 [ Sequelize.Op.and ]: [ 210 [ Op.and ]: [
212 { 211 {
213 '$preferredUsername$': t.name 212 '$preferredUsername$': t.name
214 }, 213 },
@@ -220,7 +219,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
220 } 219 }
221 220
222 return { 221 return {
223 [ Sequelize.Op.and ]: [ 222 [ Op.and ]: [
224 { 223 {
225 '$preferredUsername$': t.name 224 '$preferredUsername$': t.name
226 }, 225 },
@@ -234,9 +233,9 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
234 const query = { 233 const query = {
235 attributes: [], 234 attributes: [],
236 where: { 235 where: {
237 [ Sequelize.Op.and ]: [ 236 [ Op.and ]: [
238 { 237 {
239 [ Sequelize.Op.or ]: whereTab 238 [ Op.or ]: whereTab
240 }, 239 },
241 { 240 {
242 actorId 241 actorId
@@ -288,7 +287,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
288 required: true, 287 required: true,
289 where: search ? { 288 where: search ? {
290 host: { 289 host: {
291 [Sequelize.Op.iLike]: '%' + search + '%' 290 [Op.iLike]: '%' + search + '%'
292 } 291 }
293 } : undefined 292 } : undefined
294 } 293 }
@@ -323,7 +322,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
323 required: true, 322 required: true,
324 where: search ? { 323 where: search ? {
325 host: { 324 host: {
326 [ Sequelize.Op.iLike ]: '%' + search + '%' 325 [ Op.iLike ]: '%' + search + '%'
327 } 326 }
328 } : undefined 327 } : undefined
329 } 328 }
@@ -406,11 +405,11 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
406 }) 405 })
407 } 406 }
408 407
409 static listAcceptedFollowerUrlsForAP (actorIds: number[], t: Sequelize.Transaction, start?: number, count?: number) { 408 static listAcceptedFollowerUrlsForAP (actorIds: number[], t: Transaction, start?: number, count?: number) {
410 return ActorFollowModel.createListAcceptedFollowForApiQuery('followers', actorIds, t, start, count) 409 return ActorFollowModel.createListAcceptedFollowForApiQuery('followers', actorIds, t, start, count)
411 } 410 }
412 411
413 static listAcceptedFollowerSharedInboxUrls (actorIds: number[], t: Sequelize.Transaction) { 412 static listAcceptedFollowerSharedInboxUrls (actorIds: number[], t: Transaction) {
414 return ActorFollowModel.createListAcceptedFollowForApiQuery( 413 return ActorFollowModel.createListAcceptedFollowForApiQuery(
415 'followers', 414 'followers',
416 actorIds, 415 actorIds,
@@ -422,7 +421,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
422 ) 421 )
423 } 422 }
424 423
425 static listAcceptedFollowingUrlsForApi (actorIds: number[], t: Sequelize.Transaction, start?: number, count?: number) { 424 static listAcceptedFollowingUrlsForApi (actorIds: number[], t: Transaction, start?: number, count?: number) {
426 return ActorFollowModel.createListAcceptedFollowForApiQuery('following', actorIds, t, start, count) 425 return ActorFollowModel.createListAcceptedFollowForApiQuery('following', actorIds, t, start, count)
427 } 426 }
428 427
@@ -447,7 +446,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
447 } 446 }
448 } 447 }
449 448
450 static updateFollowScore (inboxUrl: string, value: number, t?: Sequelize.Transaction) { 449 static updateFollowScore (inboxUrl: string, value: number, t?: Transaction) {
451 const query = `UPDATE "actorFollow" SET "score" = LEAST("score" + ${value}, ${ACTOR_FOLLOW_SCORE.MAX}) ` + 450 const query = `UPDATE "actorFollow" SET "score" = LEAST("score" + ${value}, ${ACTOR_FOLLOW_SCORE.MAX}) ` +
452 'WHERE id IN (' + 451 'WHERE id IN (' +
453 'SELECT "actorFollow"."id" FROM "actorFollow" ' + 452 'SELECT "actorFollow"."id" FROM "actorFollow" ' +
@@ -456,7 +455,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
456 ')' 455 ')'
457 456
458 const options = { 457 const options = {
459 type: Sequelize.QueryTypes.BULKUPDATE, 458 type: QueryTypes.BULKUPDATE,
460 transaction: t 459 transaction: t
461 } 460 }
462 461
@@ -466,7 +465,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
466 private static async createListAcceptedFollowForApiQuery ( 465 private static async createListAcceptedFollowForApiQuery (
467 type: 'followers' | 'following', 466 type: 'followers' | 'following',
468 actorIds: number[], 467 actorIds: number[],
469 t: Sequelize.Transaction, 468 t: Transaction,
470 start?: number, 469 start?: number,
471 count?: number, 470 count?: number,
472 columnUrl = 'url', 471 columnUrl = 'url',
@@ -502,7 +501,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
502 501
503 const options = { 502 const options = {
504 bind: { actorIds }, 503 bind: { actorIds },
505 type: Sequelize.QueryTypes.SELECT, 504 type: QueryTypes.SELECT,
506 transaction: t 505 transaction: t
507 } 506 }
508 tasks.push(ActorFollowModel.sequelize.query(query, options)) 507 tasks.push(ActorFollowModel.sequelize.query(query, options))
@@ -521,7 +520,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
521 const query = { 520 const query = {
522 where: { 521 where: {
523 score: { 522 score: {
524 [Sequelize.Op.lte]: 0 523 [Op.lte]: 0
525 } 524 }
526 }, 525 },
527 logging: false 526 logging: false
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts
index e8f603031..1ebee8df5 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/activitypub/actor.ts
@@ -93,7 +93,7 @@ export const unusedActorAttributesForAPI = [
93 model: () => AvatarModel, 93 model: () => AvatarModel,
94 required: false 94 required: false
95 } 95 }
96 ] 96 ] as any // FIXME: sequelize typings
97 } 97 }
98}) 98})
99@Table({ 99@Table({
@@ -131,7 +131,7 @@ export const unusedActorAttributesForAPI = [
131export class ActorModel extends Model<ActorModel> { 131export class ActorModel extends Model<ActorModel> {
132 132
133 @AllowNull(false) 133 @AllowNull(false)
134 @Column(DataType.ENUM(values(ACTIVITY_PUB_ACTOR_TYPES))) 134 @Column({ type: DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES)) }) // FIXME: sequelize typings
135 type: ActivityPubActorType 135 type: ActivityPubActorType
136 136
137 @AllowNull(false) 137 @AllowNull(false)
@@ -151,12 +151,12 @@ export class ActorModel extends Model<ActorModel> {
151 url: string 151 url: string
152 152
153 @AllowNull(true) 153 @AllowNull(true)
154 @Is('ActorPublicKey', value => throwIfNotValid(value, isActorPublicKeyValid, 'public key')) 154 @Is('ActorPublicKey', value => throwIfNotValid(value, isActorPublicKeyValid, 'public key', true))
155 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.PUBLIC_KEY.max)) 155 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.PUBLIC_KEY.max))
156 publicKey: string 156 publicKey: string
157 157
158 @AllowNull(true) 158 @AllowNull(true)
159 @Is('ActorPublicKey', value => throwIfNotValid(value, isActorPrivateKeyValid, 'private key')) 159 @Is('ActorPublicKey', value => throwIfNotValid(value, isActorPrivateKeyValid, 'private key', true))
160 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.PRIVATE_KEY.max)) 160 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.PRIVATE_KEY.max))
161 privateKey: string 161 privateKey: string
162 162
diff --git a/server/models/migrations.ts b/server/models/migrations.ts
index 24badb166..6c11332a1 100644
--- a/server/models/migrations.ts
+++ b/server/models/migrations.ts
@@ -1,24 +1,24 @@
1import * as Sequelize from 'sequelize' 1import { ModelAttributeColumnOptions } from 'sequelize'
2 2
3declare namespace Migration { 3declare namespace Migration {
4 interface Boolean extends Sequelize.DefineAttributeColumnOptions { 4 interface Boolean extends ModelAttributeColumnOptions {
5 defaultValue: boolean | null 5 defaultValue: boolean | null
6 } 6 }
7 7
8 interface String extends Sequelize.DefineAttributeColumnOptions { 8 interface String extends ModelAttributeColumnOptions {
9 defaultValue: string | null 9 defaultValue: string | null
10 } 10 }
11 11
12 interface Integer extends Sequelize.DefineAttributeColumnOptions { 12 interface Integer extends ModelAttributeColumnOptions {
13 defaultValue: number | null 13 defaultValue: number | null
14 } 14 }
15 15
16 interface BigInteger extends Sequelize.DefineAttributeColumnOptions { 16 interface BigInteger extends ModelAttributeColumnOptions {
17 defaultValue: Sequelize.DataTypeBigInt | number | null 17 defaultValue: number | null
18 } 18 }
19 19
20 interface UUID extends Sequelize.DefineAttributeColumnOptions { 20 interface UUID extends ModelAttributeColumnOptions {
21 defaultValue: Sequelize.DataTypeUUIDv4 | null 21 defaultValue: null
22 } 22 }
23} 23}
24 24
diff --git a/server/models/oauth/oauth-client.ts b/server/models/oauth/oauth-client.ts
index 42c59bb79..b4a841edd 100644
--- a/server/models/oauth/oauth-client.ts
+++ b/server/models/oauth/oauth-client.ts
@@ -24,10 +24,10 @@ export class OAuthClientModel extends Model<OAuthClientModel> {
24 @Column 24 @Column
25 clientSecret: string 25 clientSecret: string
26 26
27 @Column(DataType.ARRAY(DataType.STRING)) 27 @Column({ type: DataType.ARRAY(DataType.STRING) }) // FIXME: sequelize typings
28 grants: string[] 28 grants: string[]
29 29
30 @Column(DataType.ARRAY(DataType.STRING)) 30 @Column({ type: DataType.ARRAY(DataType.STRING) }) // FIXME: sequelize typings
31 redirectUris: string[] 31 redirectUris: string[]
32 32
33 @CreatedAt 33 @CreatedAt
diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts
index 08d892da4..3f41ee63b 100644
--- a/server/models/oauth/oauth-token.ts
+++ b/server/models/oauth/oauth-token.ts
@@ -55,7 +55,7 @@ enum ScopeNames {
55 } 55 }
56 ] 56 ]
57 } 57 }
58 ] 58 ] as any // FIXME: sequelize typings
59 } 59 }
60}) 60})
61@Table({ 61@Table({
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts
index 2b8e44223..cbeaa662b 100644
--- a/server/models/redundancy/video-redundancy.ts
+++ b/server/models/redundancy/video-redundancy.ts
@@ -58,7 +58,7 @@ export enum ScopeNames {
58 } 58 }
59 ] 59 ]
60 } 60 }
61 ] 61 ] as any // FIXME: sequelize typings
62 } 62 }
63}) 63})
64 64
diff --git a/server/models/utils.ts b/server/models/utils.ts
index a478bc62b..98170a00e 100644
--- a/server/models/utils.ts
+++ b/server/models/utils.ts
@@ -1,26 +1,29 @@
1import { Sequelize } from 'sequelize-typescript' 1import { Sequelize } from 'sequelize-typescript'
2import * as validator from 'validator' 2import * as validator from 'validator'
3import { OrderItem } from 'sequelize'
4import { Col } from 'sequelize/types/lib/utils'
3 5
4type SortType = { sortModel: any, sortValue: string } 6type SortType = { sortModel: any, sortValue: string }
5 7
6// Translate for example "-name" to [ [ 'name', 'DESC' ], [ 'id', 'ASC' ] ] 8// Translate for example "-name" to [ [ 'name', 'DESC' ], [ 'id', 'ASC' ] ]
7function getSort (value: string, lastSort: string[] = [ 'id', 'ASC' ]) { 9function getSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderItem[] {
8 let { direction, field } = buildDirectionAndField(value) 10 const { direction, field } = buildDirectionAndField(value)
11
12 let finalField: string | Col
9 13
10 if (field.toLowerCase() === 'match') { // Search 14 if (field.toLowerCase() === 'match') { // Search
11 field = Sequelize.col('similarity') 15 finalField = Sequelize.col('similarity')
16 } else {
17 finalField = field
12 } 18 }
13 19
14 return [ [ field, direction ], lastSort ] 20 return [ [ finalField, direction ], lastSort ]
15} 21}
16 22
17function getVideoSort (value: string, lastSort: string[] = [ 'id', 'ASC' ]) { 23function getVideoSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderItem[] {
18 let { direction, field } = buildDirectionAndField(value) 24 const { direction, field } = buildDirectionAndField(value)
19 25
20 // Alias 26 if (field.toLowerCase() === 'trending') { // Sort by aggregation
21 if (field.toLowerCase() === 'match') { // Search
22 field = Sequelize.col('similarity')
23 } else if (field.toLowerCase() === 'trending') { // Sort by aggregation
24 return [ 27 return [
25 [ Sequelize.fn('COALESCE', Sequelize.fn('SUM', Sequelize.col('VideoViews.views')), '0'), direction ], 28 [ Sequelize.fn('COALESCE', Sequelize.fn('SUM', Sequelize.col('VideoViews.views')), '0'), direction ],
26 29
@@ -30,15 +33,24 @@ function getVideoSort (value: string, lastSort: string[] = [ 'id', 'ASC' ]) {
30 ] 33 ]
31 } 34 }
32 35
33 const firstSort = typeof field === 'string' ? 36 let finalField: string | Col
34 field.split('.').concat([ direction ]) : 37
35 [ field, direction ] 38 // Alias
39 if (field.toLowerCase() === 'match') { // Search
40 finalField = Sequelize.col('similarity')
41 } else {
42 finalField = field
43 }
44
45 const firstSort = typeof finalField === 'string'
46 ? finalField.split('.').concat([ direction ]) as any // FIXME: sequelize typings
47 : [ finalField, direction ]
36 48
37 return [ firstSort, lastSort ] 49 return [ firstSort, lastSort ]
38} 50}
39 51
40function getSortOnModel (model: any, value: string, lastSort: string[] = [ 'id', 'ASC' ]) { 52function getSortOnModel (model: any, value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderItem[] {
41 let [ firstSort ] = getSort(value) 53 const [ firstSort ] = getSort(value)
42 54
43 if (model) return [ [ model, firstSort[0], firstSort[1] ], lastSort ] 55 if (model) return [ [ model, firstSort[0], firstSort[1] ], lastSort ]
44 return [ firstSort, lastSort ] 56 return [ firstSort, lastSort ]
@@ -52,7 +64,9 @@ function isOutdated (model: { createdAt: Date, updatedAt: Date }, refreshInterva
52 return (now - createdAtTime) > refreshInterval && (now - updatedAtTime) > refreshInterval 64 return (now - createdAtTime) > refreshInterval && (now - updatedAtTime) > refreshInterval
53} 65}
54 66
55function throwIfNotValid (value: any, validator: (value: any) => boolean, fieldName = 'value') { 67function throwIfNotValid (value: any, validator: (value: any) => boolean, fieldName = 'value', nullable = false) {
68 if (nullable && (value === null || value === undefined)) return
69
56 if (validator(value) === false) { 70 if (validator(value) === false) {
57 throw new Error(`"${value}" is not a valid ${fieldName}.`) 71 throw new Error(`"${value}" is not a valid ${fieldName}.`)
58 } 72 }
@@ -131,7 +145,7 @@ function searchTrigramNormalizeCol (col: string) {
131} 145}
132 146
133function buildDirectionAndField (value: string) { 147function buildDirectionAndField (value: string) {
134 let field: any 148 let field: string
135 let direction: 'ASC' | 'DESC' 149 let direction: 'ASC' | 'DESC'
136 150
137 if (value.substring(0, 1) === '-') { 151 if (value.substring(0, 1) === '-') {
diff --git a/server/models/video/schedule-video-update.ts b/server/models/video/schedule-video-update.ts
index abddc1111..b9e9ed9a0 100644
--- a/server/models/video/schedule-video-update.ts
+++ b/server/models/video/schedule-video-update.ts
@@ -1,7 +1,7 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Sequelize, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { ScopeNames as VideoScopeNames, VideoModel } from './video' 2import { ScopeNames as VideoScopeNames, VideoModel } from './video'
3import { VideoPrivacy } from '../../../shared/models/videos' 3import { VideoPrivacy } from '../../../shared/models/videos'
4import { Transaction } from 'sequelize' 4import { Op, Transaction } from 'sequelize'
5 5
6@Table({ 6@Table({
7 tableName: 'scheduleVideoUpdate', 7 tableName: 'scheduleVideoUpdate',
@@ -51,7 +51,7 @@ export class ScheduleVideoUpdateModel extends Model<ScheduleVideoUpdateModel> {
51 attributes: [ 'id' ], 51 attributes: [ 'id' ],
52 where: { 52 where: {
53 updateAt: { 53 updateAt: {
54 [Sequelize.Op.lte]: new Date() 54 [Op.lte]: new Date()
55 } 55 }
56 } 56 }
57 } 57 }
@@ -64,7 +64,7 @@ export class ScheduleVideoUpdateModel extends Model<ScheduleVideoUpdateModel> {
64 const query = { 64 const query = {
65 where: { 65 where: {
66 updateAt: { 66 updateAt: {
67 [Sequelize.Op.lte]: new Date() 67 [Op.lte]: new Date()
68 } 68 }
69 }, 69 },
70 include: [ 70 include: [
diff --git a/server/models/video/tag.ts b/server/models/video/tag.ts
index b39621eaf..048b47613 100644
--- a/server/models/video/tag.ts
+++ b/server/models/video/tag.ts
@@ -1,5 +1,5 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import * as Sequelize from 'sequelize' 2import { QueryTypes, Transaction } from 'sequelize'
3import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 3import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
4import { isVideoTagValid } from '../../helpers/custom-validators/videos' 4import { isVideoTagValid } from '../../helpers/custom-validators/videos'
5import { throwIfNotValid } from '../utils' 5import { throwIfNotValid } from '../utils'
@@ -37,7 +37,7 @@ export class TagModel extends Model<TagModel> {
37 }) 37 })
38 Videos: VideoModel[] 38 Videos: VideoModel[]
39 39
40 static findOrCreateTags (tags: string[], transaction: Sequelize.Transaction) { 40 static findOrCreateTags (tags: string[], transaction: Transaction) {
41 if (tags === null) return [] 41 if (tags === null) return []
42 42
43 const tasks: Bluebird<TagModel>[] = [] 43 const tasks: Bluebird<TagModel>[] = []
@@ -72,10 +72,10 @@ export class TagModel extends Model<TagModel> {
72 72
73 const options = { 73 const options = {
74 bind: { threshold, count, videoPrivacy: VideoPrivacy.PUBLIC, videoState: VideoState.PUBLISHED }, 74 bind: { threshold, count, videoPrivacy: VideoPrivacy.PUBLIC, videoState: VideoState.PUBLISHED },
75 type: Sequelize.QueryTypes.SELECT 75 type: QueryTypes.SELECT as QueryTypes.SELECT
76 } 76 }
77 77
78 return TagModel.sequelize.query(query, options) 78 return TagModel.sequelize.query<{ name }>(query, options)
79 .then(data => data.map(d => d.name)) 79 .then(data => data.map(d => d.name))
80 } 80 }
81} 81}
diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts
index eacd651cc..1ac7919b3 100644
--- a/server/models/video/video-abuse.ts
+++ b/server/models/video/video-abuse.ts
@@ -39,7 +39,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
39 39
40 @AllowNull(true) 40 @AllowNull(true)
41 @Default(null) 41 @Default(null)
42 @Is('VideoAbuseModerationComment', value => throwIfNotValid(value, isVideoAbuseModerationCommentValid, 'moderationComment')) 42 @Is('VideoAbuseModerationComment', value => throwIfNotValid(value, isVideoAbuseModerationCommentValid, 'moderationComment', true))
43 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.MODERATION_COMMENT.max)) 43 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.MODERATION_COMMENT.max))
44 moderationComment: string 44 moderationComment: string
45 45
diff --git a/server/models/video/video-blacklist.ts b/server/models/video/video-blacklist.ts
index 2619b4950..d9fe9dfc9 100644
--- a/server/models/video/video-blacklist.ts
+++ b/server/models/video/video-blacklist.ts
@@ -1,22 +1,11 @@
1import { 1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2 AllowNull,
3 BelongsTo,
4 Column,
5 CreatedAt,
6 DataType,
7 Default,
8 ForeignKey,
9 Is, Model,
10 Table,
11 UpdatedAt,
12 IFindOptions
13} from 'sequelize-typescript'
14import { getSortOnModel, SortType, throwIfNotValid } from '../utils' 2import { getSortOnModel, SortType, throwIfNotValid } from '../utils'
15import { VideoModel } from './video' 3import { VideoModel } from './video'
16import { VideoChannelModel, ScopeNames as VideoChannelScopeNames } from './video-channel' 4import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from './video-channel'
17import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' 5import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist'
18import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' 6import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos'
19import { CONSTRAINTS_FIELDS } from '../../initializers/constants' 7import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
8import { FindOptions } from 'sequelize'
20 9
21@Table({ 10@Table({
22 tableName: 'videoBlacklist', 11 tableName: 'videoBlacklist',
@@ -30,7 +19,7 @@ import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
30export class VideoBlacklistModel extends Model<VideoBlacklistModel> { 19export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
31 20
32 @AllowNull(true) 21 @AllowNull(true)
33 @Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason')) 22 @Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason', true))
34 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_BLACKLIST.REASON.max)) 23 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_BLACKLIST.REASON.max))
35 reason: string 24 reason: string
36 25
@@ -63,7 +52,7 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
63 Video: VideoModel 52 Video: VideoModel
64 53
65 static listForApi (start: number, count: number, sort: SortType, type?: VideoBlacklistType) { 54 static listForApi (start: number, count: number, sort: SortType, type?: VideoBlacklistType) {
66 const query: IFindOptions<VideoBlacklistModel> = { 55 const query: FindOptions = {
67 offset: start, 56 offset: start,
68 limit: count, 57 limit: count,
69 order: getSortOnModel(sort.sortModel, sort.sortValue), 58 order: getSortOnModel(sort.sortModel, sort.sortValue),
diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts
index f2dbbfde8..45c60e26b 100644
--- a/server/models/video/video-caption.ts
+++ b/server/models/video/video-caption.ts
@@ -1,4 +1,4 @@
1import * as Sequelize from 'sequelize' 1import { OrderItem, Transaction } from 'sequelize'
2import { 2import {
3 AllowNull, 3 AllowNull,
4 BeforeDestroy, 4 BeforeDestroy,
@@ -115,19 +115,19 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> {
115 return VideoCaptionModel.findOne(query) 115 return VideoCaptionModel.findOne(query)
116 } 116 }
117 117
118 static insertOrReplaceLanguage (videoId: number, language: string, transaction: Sequelize.Transaction) { 118 static insertOrReplaceLanguage (videoId: number, language: string, transaction: Transaction) {
119 const values = { 119 const values = {
120 videoId, 120 videoId,
121 language 121 language
122 } 122 }
123 123
124 return VideoCaptionModel.upsert<VideoCaptionModel>(values, { transaction, returning: true }) 124 return (VideoCaptionModel.upsert<VideoCaptionModel>(values, { transaction, returning: true }) as any) // FIXME: typings
125 .then(([ caption ]) => caption) 125 .then(([ caption ]) => caption)
126 } 126 }
127 127
128 static listVideoCaptions (videoId: number) { 128 static listVideoCaptions (videoId: number) {
129 const query = { 129 const query = {
130 order: [ [ 'language', 'ASC' ] ], 130 order: [ [ 'language', 'ASC' ] ] as OrderItem[],
131 where: { 131 where: {
132 videoId 132 videoId
133 } 133 }
@@ -140,7 +140,7 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> {
140 return VIDEO_LANGUAGES[language] || 'Unknown' 140 return VIDEO_LANGUAGES[language] || 'Unknown'
141 } 141 }
142 142
143 static deleteAllCaptionsOfRemoteVideo (videoId: number, transaction: Sequelize.Transaction) { 143 static deleteAllCaptionsOfRemoteVideo (videoId: number, transaction: Transaction) {
144 const query = { 144 const query = {
145 where: { 145 where: {
146 videoId 146 videoId
diff --git a/server/models/video/video-change-ownership.ts b/server/models/video/video-change-ownership.ts
index 85e688c4f..a4f4d53f1 100644
--- a/server/models/video/video-change-ownership.ts
+++ b/server/models/video/video-change-ownership.ts
@@ -43,7 +43,7 @@ enum ScopeNames {
43 { model: () => VideoFileModel } 43 { model: () => VideoFileModel }
44 ] 44 ]
45 } 45 }
46 ] 46 ] as any // FIXME: sequelize typings
47 } 47 }
48}) 48})
49export class VideoChangeOwnershipModel extends Model<VideoChangeOwnershipModel> { 49export class VideoChangeOwnershipModel extends Model<VideoChangeOwnershipModel> {
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 5b5075344..901006dea 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -9,7 +9,6 @@ import {
9 DefaultScope, 9 DefaultScope,
10 ForeignKey, 10 ForeignKey,
11 HasMany, 11 HasMany,
12 IFindOptions,
13 Is, 12 Is,
14 Model, 13 Model,
15 Scopes, 14 Scopes,
@@ -31,12 +30,12 @@ import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttr
31import { VideoModel } from './video' 30import { VideoModel } from './video'
32import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' 31import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants'
33import { ServerModel } from '../server/server' 32import { ServerModel } from '../server/server'
34import { DefineIndexesOptions } from 'sequelize' 33import { FindOptions, ModelIndexesOptions, Op } from 'sequelize'
35import { AvatarModel } from '../avatar/avatar' 34import { AvatarModel } from '../avatar/avatar'
36import { VideoPlaylistModel } from './video-playlist' 35import { VideoPlaylistModel } from './video-playlist'
37 36
38// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation 37// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
39const indexes: DefineIndexesOptions[] = [ 38const indexes: ModelIndexesOptions[] = [
40 buildTrigramSearchIndex('video_channel_name_trigram', 'name'), 39 buildTrigramSearchIndex('video_channel_name_trigram', 'name'),
41 40
42 { 41 {
@@ -69,7 +68,7 @@ type AvailableForListOptions = {
69}) 68})
70@Scopes({ 69@Scopes({
71 [ScopeNames.SUMMARY]: (withAccount = false) => { 70 [ScopeNames.SUMMARY]: (withAccount = false) => {
72 const base: IFindOptions<VideoChannelModel> = { 71 const base: FindOptions = {
73 attributes: [ 'name', 'description', 'id', 'actorId' ], 72 attributes: [ 'name', 'description', 'id', 'actorId' ],
74 include: [ 73 include: [
75 { 74 {
@@ -112,13 +111,13 @@ type AvailableForListOptions = {
112 }, 111 },
113 model: ActorModel, 112 model: ActorModel,
114 where: { 113 where: {
115 [Sequelize.Op.or]: [ 114 [Op.or]: [
116 { 115 {
117 serverId: null 116 serverId: null
118 }, 117 },
119 { 118 {
120 serverId: { 119 serverId: {
121 [ Sequelize.Op.in ]: Sequelize.literal(inQueryInstanceFollow) 120 [ Op.in ]: Sequelize.literal(inQueryInstanceFollow)
122 } 121 }
123 } 122 }
124 ] 123 ]
@@ -172,13 +171,13 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
172 171
173 @AllowNull(true) 172 @AllowNull(true)
174 @Default(null) 173 @Default(null)
175 @Is('VideoChannelDescription', value => throwIfNotValid(value, isVideoChannelDescriptionValid, 'description')) 174 @Is('VideoChannelDescription', value => throwIfNotValid(value, isVideoChannelDescriptionValid, 'description', true))
176 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.DESCRIPTION.max)) 175 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.DESCRIPTION.max))
177 description: string 176 description: string
178 177
179 @AllowNull(true) 178 @AllowNull(true)
180 @Default(null) 179 @Default(null)
181 @Is('VideoChannelSupport', value => throwIfNotValid(value, isVideoChannelSupportValid, 'support')) 180 @Is('VideoChannelSupport', value => throwIfNotValid(value, isVideoChannelSupportValid, 'support', true))
182 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.SUPPORT.max)) 181 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.SUPPORT.max))
183 support: string 182 support: string
184 183
@@ -313,7 +312,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
313 limit: options.count, 312 limit: options.count,
314 order: getSort(options.sort), 313 order: getSort(options.sort),
315 where: { 314 where: {
316 [Sequelize.Op.or]: [ 315 [Op.or]: [
317 Sequelize.literal( 316 Sequelize.literal(
318 'lower(immutable_unaccent("VideoChannelModel"."name")) % lower(immutable_unaccent(' + escapedSearch + '))' 317 'lower(immutable_unaccent("VideoChannelModel"."name")) % lower(immutable_unaccent(' + escapedSearch + '))'
319 ), 318 ),
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index cb5f1cbbe..5f7cd3671 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -1,4 +1,3 @@
1import * as Sequelize from 'sequelize'
2import { 1import {
3 AllowNull, 2 AllowNull,
4 BeforeDestroy, 3 BeforeDestroy,
@@ -7,7 +6,6 @@ import {
7 CreatedAt, 6 CreatedAt,
8 DataType, 7 DataType,
9 ForeignKey, 8 ForeignKey,
10 IFindOptions,
11 Is, 9 Is,
12 Model, 10 Model,
13 Scopes, 11 Scopes,
@@ -32,6 +30,7 @@ import { UserModel } from '../account/user'
32import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' 30import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor'
33import { regexpCapture } from '../../helpers/regexp' 31import { regexpCapture } from '../../helpers/regexp'
34import { uniq } from 'lodash' 32import { uniq } from 'lodash'
33import { FindOptions, Op, Order, Sequelize, Transaction } from 'sequelize'
35 34
36enum ScopeNames { 35enum ScopeNames {
37 WITH_ACCOUNT = 'WITH_ACCOUNT', 36 WITH_ACCOUNT = 'WITH_ACCOUNT',
@@ -86,7 +85,7 @@ enum ScopeNames {
86 } 85 }
87 ] 86 ]
88 } 87 }
89 ] 88 ] as any // FIXME: sequelize typings
90 }, 89 },
91 [ScopeNames.WITH_IN_REPLY_TO]: { 90 [ScopeNames.WITH_IN_REPLY_TO]: {
92 include: [ 91 include: [
@@ -120,7 +119,7 @@ enum ScopeNames {
120 } 119 }
121 ] 120 ]
122 } 121 }
123 ] 122 ] as any // FIXME: sequelize typings
124 } 123 }
125}) 124})
126@Table({ 125@Table({
@@ -244,8 +243,8 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
244 } 243 }
245 } 244 }
246 245
247 static loadById (id: number, t?: Sequelize.Transaction) { 246 static loadById (id: number, t?: Transaction) {
248 const query: IFindOptions<VideoCommentModel> = { 247 const query: FindOptions = {
249 where: { 248 where: {
250 id 249 id
251 } 250 }
@@ -256,8 +255,8 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
256 return VideoCommentModel.findOne(query) 255 return VideoCommentModel.findOne(query)
257 } 256 }
258 257
259 static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Sequelize.Transaction) { 258 static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Transaction) {
260 const query: IFindOptions<VideoCommentModel> = { 259 const query: FindOptions = {
261 where: { 260 where: {
262 id 261 id
263 } 262 }
@@ -270,8 +269,8 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
270 .findOne(query) 269 .findOne(query)
271 } 270 }
272 271
273 static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) { 272 static loadByUrlAndPopulateAccount (url: string, t?: Transaction) {
274 const query: IFindOptions<VideoCommentModel> = { 273 const query: FindOptions = {
275 where: { 274 where: {
276 url 275 url
277 } 276 }
@@ -282,8 +281,8 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
282 return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT ]).findOne(query) 281 return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT ]).findOne(query)
283 } 282 }
284 283
285 static loadByUrlAndPopulateReplyAndVideo (url: string, t?: Sequelize.Transaction) { 284 static loadByUrlAndPopulateReplyAndVideo (url: string, t?: Transaction) {
286 const query: IFindOptions<VideoCommentModel> = { 285 const query: FindOptions = {
287 where: { 286 where: {
288 url 287 url
289 } 288 }
@@ -307,7 +306,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
307 videoId, 306 videoId,
308 inReplyToCommentId: null, 307 inReplyToCommentId: null,
309 accountId: { 308 accountId: {
310 [Sequelize.Op.notIn]: Sequelize.literal( 309 [Op.notIn]: Sequelize.literal(
311 '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')' 310 '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')'
312 ) 311 )
313 } 312 }
@@ -336,15 +335,15 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
336 const userAccountId = user ? user.Account.id : undefined 335 const userAccountId = user ? user.Account.id : undefined
337 336
338 const query = { 337 const query = {
339 order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ], 338 order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ] as Order,
340 where: { 339 where: {
341 videoId, 340 videoId,
342 [ Sequelize.Op.or ]: [ 341 [ Op.or ]: [
343 { id: threadId }, 342 { id: threadId },
344 { originCommentId: threadId } 343 { originCommentId: threadId }
345 ], 344 ],
346 accountId: { 345 accountId: {
347 [Sequelize.Op.notIn]: Sequelize.literal( 346 [Op.notIn]: Sequelize.literal(
348 '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')' 347 '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')'
349 ) 348 )
350 } 349 }
@@ -366,12 +365,12 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
366 }) 365 })
367 } 366 }
368 367
369 static listThreadParentComments (comment: VideoCommentModel, t: Sequelize.Transaction, order: 'ASC' | 'DESC' = 'ASC') { 368 static listThreadParentComments (comment: VideoCommentModel, t: Transaction, order: 'ASC' | 'DESC' = 'ASC') {
370 const query = { 369 const query = {
371 order: [ [ 'createdAt', order ] ], 370 order: [ [ 'createdAt', order ] ] as Order,
372 where: { 371 where: {
373 id: { 372 id: {
374 [ Sequelize.Op.in ]: Sequelize.literal('(' + 373 [ Op.in ]: Sequelize.literal('(' +
375 'WITH RECURSIVE children (id, "inReplyToCommentId") AS ( ' + 374 'WITH RECURSIVE children (id, "inReplyToCommentId") AS ( ' +
376 `SELECT id, "inReplyToCommentId" FROM "videoComment" WHERE id = ${comment.id} ` + 375 `SELECT id, "inReplyToCommentId" FROM "videoComment" WHERE id = ${comment.id} ` +
377 'UNION ' + 376 'UNION ' +
@@ -380,7 +379,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
380 ') ' + 379 ') ' +
381 'SELECT id FROM children' + 380 'SELECT id FROM children' +
382 ')'), 381 ')'),
383 [ Sequelize.Op.ne ]: comment.id 382 [ Op.ne ]: comment.id
384 } 383 }
385 }, 384 },
386 transaction: t 385 transaction: t
@@ -391,9 +390,9 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
391 .findAll(query) 390 .findAll(query)
392 } 391 }
393 392
394 static listAndCountByVideoId (videoId: number, start: number, count: number, t?: Sequelize.Transaction, order: 'ASC' | 'DESC' = 'ASC') { 393 static listAndCountByVideoId (videoId: number, start: number, count: number, t?: Transaction, order: 'ASC' | 'DESC' = 'ASC') {
395 const query = { 394 const query = {
396 order: [ [ 'createdAt', order ] ], 395 order: [ [ 'createdAt', order ] ] as Order,
397 offset: start, 396 offset: start,
398 limit: count, 397 limit: count,
399 where: { 398 where: {
@@ -407,7 +406,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
407 406
408 static listForFeed (start: number, count: number, videoId?: number) { 407 static listForFeed (start: number, count: number, videoId?: number) {
409 const query = { 408 const query = {
410 order: [ [ 'createdAt', 'DESC' ] ], 409 order: [ [ 'createdAt', 'DESC' ] ] as Order,
411 offset: start, 410 offset: start,
412 limit: count, 411 limit: count,
413 where: {}, 412 where: {},
@@ -457,7 +456,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
457 const query = { 456 const query = {
458 where: { 457 where: {
459 updatedAt: { 458 updatedAt: {
460 [Sequelize.Op.lt]: beforeUpdatedAt 459 [Op.lt]: beforeUpdatedAt
461 }, 460 },
462 videoId 461 videoId
463 } 462 }
diff --git a/server/models/video/video-import.ts b/server/models/video/video-import.ts
index ec10085d6..588a13a4f 100644
--- a/server/models/video/video-import.ts
+++ b/server/models/video/video-import.ts
@@ -55,13 +55,13 @@ export class VideoImportModel extends Model<VideoImportModel> {
55 55
56 @AllowNull(true) 56 @AllowNull(true)
57 @Default(null) 57 @Default(null)
58 @Is('VideoImportTargetUrl', value => throwIfNotValid(value, isVideoImportTargetUrlValid, 'targetUrl')) 58 @Is('VideoImportTargetUrl', value => throwIfNotValid(value, isVideoImportTargetUrlValid, 'targetUrl', true))
59 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.URL.max)) 59 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.URL.max))
60 targetUrl: string 60 targetUrl: string
61 61
62 @AllowNull(true) 62 @AllowNull(true)
63 @Default(null) 63 @Default(null)
64 @Is('VideoImportMagnetUri', value => throwIfNotValid(value, isVideoMagnetUriValid, 'magnetUri')) 64 @Is('VideoImportMagnetUri', value => throwIfNotValid(value, isVideoMagnetUriValid, 'magnetUri', true))
65 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.URL.max)) // Use the same constraints than URLs 65 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.URL.max)) // Use the same constraints than URLs
66 magnetUri: string 66 magnetUri: string
67 67
diff --git a/server/models/video/video-playlist-element.ts b/server/models/video/video-playlist-element.ts
index 3396b1136..eeb3d6bbd 100644
--- a/server/models/video/video-playlist-element.ts
+++ b/server/models/video/video-playlist-element.ts
@@ -15,12 +15,12 @@ import {
15} from 'sequelize-typescript' 15} from 'sequelize-typescript'
16import { VideoModel } from './video' 16import { VideoModel } from './video'
17import { VideoPlaylistModel } from './video-playlist' 17import { VideoPlaylistModel } from './video-playlist'
18import * as Sequelize from 'sequelize'
19import { getSort, throwIfNotValid } from '../utils' 18import { getSort, throwIfNotValid } from '../utils'
20import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 19import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
21import { CONSTRAINTS_FIELDS } from '../../initializers/constants' 20import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
22import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object' 21import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object'
23import * as validator from 'validator' 22import * as validator from 'validator'
23import { AggregateOptions, Op, Sequelize, Transaction } from 'sequelize'
24 24
25@Table({ 25@Table({
26 tableName: 'videoPlaylistElement', 26 tableName: 'videoPlaylistElement',
@@ -96,7 +96,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
96 }) 96 })
97 Video: VideoModel 97 Video: VideoModel
98 98
99 static deleteAllOf (videoPlaylistId: number, transaction?: Sequelize.Transaction) { 99 static deleteAllOf (videoPlaylistId: number, transaction?: Transaction) {
100 const query = { 100 const query = {
101 where: { 101 where: {
102 videoPlaylistId 102 videoPlaylistId
@@ -140,7 +140,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
140 return VideoPlaylistElementModel.findOne(query) 140 return VideoPlaylistElementModel.findOne(query)
141 } 141 }
142 142
143 static listUrlsOfForAP (videoPlaylistId: number, start: number, count: number, t?: Sequelize.Transaction) { 143 static listUrlsOfForAP (videoPlaylistId: number, start: number, count: number, t?: Transaction) {
144 const query = { 144 const query = {
145 attributes: [ 'url' ], 145 attributes: [ 'url' ],
146 offset: start, 146 offset: start,
@@ -159,8 +159,8 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
159 }) 159 })
160 } 160 }
161 161
162 static getNextPositionOf (videoPlaylistId: number, transaction?: Sequelize.Transaction) { 162 static getNextPositionOf (videoPlaylistId: number, transaction?: Transaction) {
163 const query = { 163 const query: AggregateOptions<number> = {
164 where: { 164 where: {
165 videoPlaylistId 165 videoPlaylistId
166 }, 166 },
@@ -176,14 +176,14 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
176 firstPosition: number, 176 firstPosition: number,
177 endPosition: number, 177 endPosition: number,
178 newPosition: number, 178 newPosition: number,
179 transaction?: Sequelize.Transaction 179 transaction?: Transaction
180 ) { 180 ) {
181 const query = { 181 const query = {
182 where: { 182 where: {
183 videoPlaylistId, 183 videoPlaylistId,
184 position: { 184 position: {
185 [Sequelize.Op.gte]: firstPosition, 185 [Op.gte]: firstPosition,
186 [Sequelize.Op.lte]: endPosition 186 [Op.lte]: endPosition
187 } 187 }
188 }, 188 },
189 transaction, 189 transaction,
@@ -198,13 +198,13 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
198 fromPosition: number, 198 fromPosition: number,
199 toPosition?: number, 199 toPosition?: number,
200 by = 1, 200 by = 1,
201 transaction?: Sequelize.Transaction 201 transaction?: Transaction
202 ) { 202 ) {
203 const query = { 203 const query = {
204 where: { 204 where: {
205 videoPlaylistId, 205 videoPlaylistId,
206 position: { 206 position: {
207 [Sequelize.Op.gte]: fromPosition 207 [Op.gte]: fromPosition
208 } 208 }
209 }, 209 },
210 transaction 210 transaction
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts
index 073609c24..3e436acfc 100644
--- a/server/models/video/video-playlist.ts
+++ b/server/models/video/video-playlist.ts
@@ -15,7 +15,6 @@ import {
15 Table, 15 Table,
16 UpdatedAt 16 UpdatedAt
17} from 'sequelize-typescript' 17} from 'sequelize-typescript'
18import * as Sequelize from 'sequelize'
19import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' 18import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
20import { buildServerIdsFollowedBy, buildWhereIdOrUUID, getSort, isOutdated, throwIfNotValid } from '../utils' 19import { buildServerIdsFollowedBy, buildWhereIdOrUUID, getSort, isOutdated, throwIfNotValid } from '../utils'
21import { 20import {
@@ -43,6 +42,7 @@ import { activityPubCollectionPagination } from '../../helpers/activitypub'
43import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video-playlist-type.model' 42import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video-playlist-type.model'
44import { ThumbnailModel } from './thumbnail' 43import { ThumbnailModel } from './thumbnail'
45import { ActivityIconObject } from '../../../shared/models/activitypub/objects' 44import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
45import { fn, literal, Op, Transaction } from 'sequelize'
46 46
47enum ScopeNames { 47enum ScopeNames {
48 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', 48 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST',
@@ -74,7 +74,11 @@ type AvailableForListOptions = {
74 attributes: { 74 attributes: {
75 include: [ 75 include: [
76 [ 76 [
77 Sequelize.literal('(SELECT COUNT("id") FROM "videoPlaylistElement" WHERE "videoPlaylistId" = "VideoPlaylistModel"."id")'), 77 fn('COUNT', 'toto'),
78 'coucou'
79 ],
80 [
81 literal('(SELECT COUNT("id") FROM "videoPlaylistElement" WHERE "videoPlaylistId" = "VideoPlaylistModel"."id")'),
78 'videosLength' 82 'videosLength'
79 ] 83 ]
80 ] 84 ]
@@ -116,13 +120,13 @@ type AvailableForListOptions = {
116 // Only list local playlists OR playlists that are on an instance followed by actorId 120 // Only list local playlists OR playlists that are on an instance followed by actorId
117 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.followerActorId) 121 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.followerActorId)
118 const actorWhere = { 122 const actorWhere = {
119 [ Sequelize.Op.or ]: [ 123 [ Op.or ]: [
120 { 124 {
121 serverId: null 125 serverId: null
122 }, 126 },
123 { 127 {
124 serverId: { 128 serverId: {
125 [ Sequelize.Op.in ]: Sequelize.literal(inQueryInstanceFollow) 129 [ Op.in ]: literal(inQueryInstanceFollow)
126 } 130 }
127 } 131 }
128 ] 132 ]
@@ -155,7 +159,7 @@ type AvailableForListOptions = {
155 } 159 }
156 160
157 const where = { 161 const where = {
158 [Sequelize.Op.and]: whereAnd 162 [Op.and]: whereAnd
159 } 163 }
160 164
161 const accountScope = { 165 const accountScope = {
@@ -206,7 +210,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
206 name: string 210 name: string
207 211
208 @AllowNull(true) 212 @AllowNull(true)
209 @Is('VideoPlaylistDescription', value => throwIfNotValid(value, isVideoPlaylistDescriptionValid, 'description')) 213 @Is('VideoPlaylistDescription', value => throwIfNotValid(value, isVideoPlaylistDescriptionValid, 'description', true))
210 @Column 214 @Column
211 description: string 215 description: string
212 216
@@ -344,7 +348,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
344 model: VideoPlaylistElementModel.unscoped(), 348 model: VideoPlaylistElementModel.unscoped(),
345 where: { 349 where: {
346 videoId: { 350 videoId: {
347 [Sequelize.Op.any]: videoIds 351 [Op.any]: videoIds
348 } 352 }
349 }, 353 },
350 required: true 354 required: true
@@ -368,7 +372,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
368 .then(e => !!e) 372 .then(e => !!e)
369 } 373 }
370 374
371 static loadWithAccountAndChannelSummary (id: number | string, transaction: Sequelize.Transaction) { 375 static loadWithAccountAndChannelSummary (id: number | string, transaction: Transaction) {
372 const where = buildWhereIdOrUUID(id) 376 const where = buildWhereIdOrUUID(id)
373 377
374 const query = { 378 const query = {
@@ -381,7 +385,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
381 .findOne(query) 385 .findOne(query)
382 } 386 }
383 387
384 static loadWithAccountAndChannel (id: number | string, transaction: Sequelize.Transaction) { 388 static loadWithAccountAndChannel (id: number | string, transaction: Transaction) {
385 const where = buildWhereIdOrUUID(id) 389 const where = buildWhereIdOrUUID(id)
386 390
387 const query = { 391 const query = {
@@ -412,7 +416,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
412 return VIDEO_PLAYLIST_TYPES[type] || 'Unknown' 416 return VIDEO_PLAYLIST_TYPES[type] || 'Unknown'
413 } 417 }
414 418
415 static resetPlaylistsOfChannel (videoChannelId: number, transaction: Sequelize.Transaction) { 419 static resetPlaylistsOfChannel (videoChannelId: number, transaction: Transaction) {
416 const query = { 420 const query = {
417 where: { 421 where: {
418 videoChannelId 422 videoChannelId
@@ -489,7 +493,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
489 label: VideoPlaylistModel.getTypeLabel(this.type) 493 label: VideoPlaylistModel.getTypeLabel(this.type)
490 }, 494 },
491 495
492 videosLength: this.get('videosLength'), 496 videosLength: this.get('videosLength') as number,
493 497
494 createdAt: this.createdAt, 498 createdAt: this.createdAt,
495 updatedAt: this.updatedAt, 499 updatedAt: this.updatedAt,
@@ -499,7 +503,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
499 } 503 }
500 } 504 }
501 505
502 toActivityPubObject (page: number, t: Sequelize.Transaction): Promise<PlaylistObject> { 506 toActivityPubObject (page: number, t: Transaction): Promise<PlaylistObject> {
503 const handler = (start: number, count: number) => { 507 const handler = (start: number, count: number) => {
504 return VideoPlaylistElementModel.listUrlsOfForAP(this.id, start, count, t) 508 return VideoPlaylistElementModel.listUrlsOfForAP(this.id, start, count, t)
505 } 509 }
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts
index e50b5d106..b30267e09 100644
--- a/server/models/video/video-streaming-playlist.ts
+++ b/server/models/video/video-streaming-playlist.ts
@@ -1,8 +1,7 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, HasMany, Is, Model, Table, UpdatedAt, DataType } from 'sequelize-typescript'
2import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' 2import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos'
3import { throwIfNotValid } from '../utils' 3import { throwIfNotValid } from '../utils'
4import { VideoModel } from './video' 4import { VideoModel } from './video'
5import * as Sequelize from 'sequelize'
6import { VideoRedundancyModel } from '../redundancy/video-redundancy' 5import { VideoRedundancyModel } from '../redundancy/video-redundancy'
7import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' 6import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
8import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 7import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
@@ -11,6 +10,7 @@ import { VideoFileModel } from './video-file'
11import { join } from 'path' 10import { join } from 'path'
12import { sha1 } from '../../helpers/core-utils' 11import { sha1 } from '../../helpers/core-utils'
13import { isArrayOf } from '../../helpers/custom-validators/misc' 12import { isArrayOf } from '../../helpers/custom-validators/misc'
13import { QueryTypes, Op } from 'sequelize'
14 14
15@Table({ 15@Table({
16 tableName: 'videoStreamingPlaylist', 16 tableName: 'videoStreamingPlaylist',
@@ -26,7 +26,7 @@ import { isArrayOf } from '../../helpers/custom-validators/misc'
26 fields: [ 'p2pMediaLoaderInfohashes' ], 26 fields: [ 'p2pMediaLoaderInfohashes' ],
27 using: 'gin' 27 using: 'gin'
28 } 28 }
29 ] 29 ] as any // FIXME: sequelize typings
30}) 30})
31export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistModel> { 31export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistModel> {
32 @CreatedAt 32 @CreatedAt
@@ -46,7 +46,7 @@ export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistMod
46 46
47 @AllowNull(false) 47 @AllowNull(false)
48 @Is('VideoStreamingPlaylistInfoHashes', value => throwIfNotValid(value, v => isArrayOf(v, isVideoFileInfoHashValid), 'info hashes')) 48 @Is('VideoStreamingPlaylistInfoHashes', value => throwIfNotValid(value, v => isArrayOf(v, isVideoFileInfoHashValid), 'info hashes'))
49 @Column(DataType.ARRAY(DataType.STRING)) 49 @Column({ type: DataType.ARRAY(DataType.STRING) }) // FIXME: typings
50 p2pMediaLoaderInfohashes: string[] 50 p2pMediaLoaderInfohashes: string[]
51 51
52 @AllowNull(false) 52 @AllowNull(false)
@@ -82,15 +82,13 @@ export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistMod
82 static doesInfohashExist (infoHash: string) { 82 static doesInfohashExist (infoHash: string) {
83 const query = 'SELECT 1 FROM "videoStreamingPlaylist" WHERE $infoHash = ANY("p2pMediaLoaderInfohashes") LIMIT 1' 83 const query = 'SELECT 1 FROM "videoStreamingPlaylist" WHERE $infoHash = ANY("p2pMediaLoaderInfohashes") LIMIT 1'
84 const options = { 84 const options = {
85 type: Sequelize.QueryTypes.SELECT, 85 type: QueryTypes.SELECT as QueryTypes.SELECT,
86 bind: { infoHash }, 86 bind: { infoHash },
87 raw: true 87 raw: true
88 } 88 }
89 89
90 return VideoModel.sequelize.query(query, options) 90 return VideoModel.sequelize.query<any>(query, options)
91 .then(results => { 91 .then(results => results.length === 1)
92 return results.length === 1
93 })
94 } 92 }
95 93
96 static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: VideoFileModel[]) { 94 static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: VideoFileModel[]) {
@@ -108,7 +106,7 @@ export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistMod
108 const query = { 106 const query = {
109 where: { 107 where: {
110 p2pMediaLoaderPeerVersion: { 108 p2pMediaLoaderPeerVersion: {
111 [Sequelize.Op.ne]: P2P_MEDIA_LOADER_PEER_VERSION 109 [Op.ne]: P2P_MEDIA_LOADER_PEER_VERSION
112 } 110 }
113 } 111 }
114 } 112 }
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 9840d17fd..329cebd28 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -3,7 +3,18 @@ import { maxBy } from 'lodash'
3import * as magnetUtil from 'magnet-uri' 3import * as magnetUtil from 'magnet-uri'
4import * as parseTorrent from 'parse-torrent' 4import * as parseTorrent from 'parse-torrent'
5import { join } from 'path' 5import { join } from 'path'
6import * as Sequelize from 'sequelize' 6import {
7 CountOptions,
8 FindOptions,
9 IncludeOptions,
10 ModelIndexesOptions,
11 Op,
12 QueryTypes,
13 ScopeOptions,
14 Sequelize,
15 Transaction,
16 WhereOptions
17} from 'sequelize'
7import { 18import {
8 AllowNull, 19 AllowNull,
9 BeforeDestroy, 20 BeforeDestroy,
@@ -16,8 +27,6 @@ import {
16 ForeignKey, 27 ForeignKey,
17 HasMany, 28 HasMany,
18 HasOne, 29 HasOne,
19 IFindOptions,
20 IIncludeOptions,
21 Is, 30 Is,
22 IsInt, 31 IsInt,
23 IsUUID, 32 IsUUID,
@@ -45,7 +54,7 @@ import {
45 isVideoStateValid, 54 isVideoStateValid,
46 isVideoSupportValid 55 isVideoSupportValid
47} from '../../helpers/custom-validators/videos' 56} from '../../helpers/custom-validators/videos'
48import { generateImageFromVideoFile, getVideoFileResolution } from '../../helpers/ffmpeg-utils' 57import { getVideoFileResolution } from '../../helpers/ffmpeg-utils'
49import { logger } from '../../helpers/logger' 58import { logger } from '../../helpers/logger'
50import { getServerActor } from '../../helpers/utils' 59import { getServerActor } from '../../helpers/utils'
51import { 60import {
@@ -54,11 +63,9 @@ import {
54 CONSTRAINTS_FIELDS, 63 CONSTRAINTS_FIELDS,
55 HLS_REDUNDANCY_DIRECTORY, 64 HLS_REDUNDANCY_DIRECTORY,
56 HLS_STREAMING_PLAYLIST_DIRECTORY, 65 HLS_STREAMING_PLAYLIST_DIRECTORY,
57 PREVIEWS_SIZE,
58 REMOTE_SCHEME, 66 REMOTE_SCHEME,
59 STATIC_DOWNLOAD_PATHS, 67 STATIC_DOWNLOAD_PATHS,
60 STATIC_PATHS, 68 STATIC_PATHS,
61 THUMBNAILS_SIZE,
62 VIDEO_CATEGORIES, 69 VIDEO_CATEGORIES,
63 VIDEO_LANGUAGES, 70 VIDEO_LANGUAGES,
64 VIDEO_LICENCES, 71 VIDEO_LICENCES,
@@ -111,7 +118,7 @@ import { ThumbnailModel } from './thumbnail'
111import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' 118import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
112 119
113// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation 120// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
114const indexes: Sequelize.DefineIndexesOptions[] = [ 121const indexes: (ModelIndexesOptions & { where?: WhereOptions })[] = [
115 buildTrigramSearchIndex('video_name_trigram', 'name'), 122 buildTrigramSearchIndex('video_name_trigram', 'name'),
116 123
117 { fields: [ 'createdAt' ] }, 124 { fields: [ 'createdAt' ] },
@@ -123,7 +130,7 @@ const indexes: Sequelize.DefineIndexesOptions[] = [
123 fields: [ 'originallyPublishedAt' ], 130 fields: [ 'originallyPublishedAt' ],
124 where: { 131 where: {
125 originallyPublishedAt: { 132 originallyPublishedAt: {
126 [Sequelize.Op.ne]: null 133 [Op.ne]: null
127 } 134 }
128 } 135 }
129 }, 136 },
@@ -131,7 +138,7 @@ const indexes: Sequelize.DefineIndexesOptions[] = [
131 fields: [ 'category' ], // We don't care videos with an unknown category 138 fields: [ 'category' ], // We don't care videos with an unknown category
132 where: { 139 where: {
133 category: { 140 category: {
134 [Sequelize.Op.ne]: null 141 [Op.ne]: null
135 } 142 }
136 } 143 }
137 }, 144 },
@@ -139,7 +146,7 @@ const indexes: Sequelize.DefineIndexesOptions[] = [
139 fields: [ 'licence' ], // We don't care videos with an unknown licence 146 fields: [ 'licence' ], // We don't care videos with an unknown licence
140 where: { 147 where: {
141 licence: { 148 licence: {
142 [Sequelize.Op.ne]: null 149 [Op.ne]: null
143 } 150 }
144 } 151 }
145 }, 152 },
@@ -147,7 +154,7 @@ const indexes: Sequelize.DefineIndexesOptions[] = [
147 fields: [ 'language' ], // We don't care videos with an unknown language 154 fields: [ 'language' ], // We don't care videos with an unknown language
148 where: { 155 where: {
149 language: { 156 language: {
150 [Sequelize.Op.ne]: null 157 [Op.ne]: null
151 } 158 }
152 } 159 }
153 }, 160 },
@@ -222,10 +229,10 @@ type AvailableForListIDsOptions = {
222 229
223@Scopes({ 230@Scopes({
224 [ ScopeNames.FOR_API ]: (options: ForAPIOptions) => { 231 [ ScopeNames.FOR_API ]: (options: ForAPIOptions) => {
225 const query: IFindOptions<VideoModel> = { 232 const query: FindOptions = {
226 where: { 233 where: {
227 id: { 234 id: {
228 [ Sequelize.Op.any ]: options.ids 235 [ Op.in ]: options.ids // FIXME: sequelize any seems broken
229 } 236 }
230 }, 237 },
231 include: [ 238 include: [
@@ -256,21 +263,21 @@ type AvailableForListIDsOptions = {
256 return query 263 return query
257 }, 264 },
258 [ ScopeNames.AVAILABLE_FOR_LIST_IDS ]: (options: AvailableForListIDsOptions) => { 265 [ ScopeNames.AVAILABLE_FOR_LIST_IDS ]: (options: AvailableForListIDsOptions) => {
259 const query: IFindOptions<VideoModel> = { 266 const query: FindOptions = {
260 raw: true, 267 raw: true,
261 attributes: [ 'id' ], 268 attributes: [ 'id' ],
262 where: { 269 where: {
263 id: { 270 id: {
264 [ Sequelize.Op.and ]: [ 271 [ Op.and ]: [
265 { 272 {
266 [ Sequelize.Op.notIn ]: Sequelize.literal( 273 [ Op.notIn ]: Sequelize.literal(
267 '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")' 274 '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")'
268 ) 275 )
269 } 276 }
270 ] 277 ]
271 }, 278 },
272 channelId: { 279 channelId: {
273 [ Sequelize.Op.notIn ]: Sequelize.literal( 280 [ Op.notIn ]: Sequelize.literal(
274 '(' + 281 '(' +
275 'SELECT id FROM "videoChannel" WHERE "accountId" IN (' + 282 'SELECT id FROM "videoChannel" WHERE "accountId" IN (' +
276 buildBlockedAccountSQL(options.serverAccountId, options.user ? options.user.Account.id : undefined) + 283 buildBlockedAccountSQL(options.serverAccountId, options.user ? options.user.Account.id : undefined) +
@@ -288,12 +295,12 @@ type AvailableForListIDsOptions = {
288 // Always list public videos 295 // Always list public videos
289 privacy: VideoPrivacy.PUBLIC, 296 privacy: VideoPrivacy.PUBLIC,
290 // Always list published videos, or videos that are being transcoded but on which we don't want to wait for transcoding 297 // Always list published videos, or videos that are being transcoded but on which we don't want to wait for transcoding
291 [ Sequelize.Op.or ]: [ 298 [ Op.or ]: [
292 { 299 {
293 state: VideoState.PUBLISHED 300 state: VideoState.PUBLISHED
294 }, 301 },
295 { 302 {
296 [ Sequelize.Op.and ]: { 303 [ Op.and ]: {
297 state: VideoState.TO_TRANSCODE, 304 state: VideoState.TO_TRANSCODE,
298 waitTranscoding: false 305 waitTranscoding: false
299 } 306 }
@@ -318,7 +325,7 @@ type AvailableForListIDsOptions = {
318 } 325 }
319 326
320 if (options.filter || options.accountId || options.videoChannelId) { 327 if (options.filter || options.accountId || options.videoChannelId) {
321 const videoChannelInclude: IIncludeOptions = { 328 const videoChannelInclude: IncludeOptions = {
322 attributes: [], 329 attributes: [],
323 model: VideoChannelModel.unscoped(), 330 model: VideoChannelModel.unscoped(),
324 required: true 331 required: true
@@ -331,7 +338,7 @@ type AvailableForListIDsOptions = {
331 } 338 }
332 339
333 if (options.filter || options.accountId) { 340 if (options.filter || options.accountId) {
334 const accountInclude: IIncludeOptions = { 341 const accountInclude: IncludeOptions = {
335 attributes: [], 342 attributes: [],
336 model: AccountModel.unscoped(), 343 model: AccountModel.unscoped(),
337 required: true 344 required: true
@@ -371,8 +378,8 @@ type AvailableForListIDsOptions = {
371 378
372 // Force actorId to be a number to avoid SQL injections 379 // Force actorId to be a number to avoid SQL injections
373 const actorIdNumber = parseInt(options.followerActorId.toString(), 10) 380 const actorIdNumber = parseInt(options.followerActorId.toString(), 10)
374 query.where[ 'id' ][ Sequelize.Op.and ].push({ 381 query.where[ 'id' ][ Op.and ].push({
375 [ Sequelize.Op.in ]: Sequelize.literal( 382 [ Op.in ]: Sequelize.literal(
376 '(' + 383 '(' +
377 'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' + 384 'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' +
378 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' + 385 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' +
@@ -391,8 +398,8 @@ type AvailableForListIDsOptions = {
391 } 398 }
392 399
393 if (options.withFiles === true) { 400 if (options.withFiles === true) {
394 query.where[ 'id' ][ Sequelize.Op.and ].push({ 401 query.where[ 'id' ][ Op.and ].push({
395 [ Sequelize.Op.in ]: Sequelize.literal( 402 [ Op.in ]: Sequelize.literal(
396 '(SELECT "videoId" FROM "videoFile")' 403 '(SELECT "videoId" FROM "videoFile")'
397 ) 404 )
398 }) 405 })
@@ -406,8 +413,8 @@ type AvailableForListIDsOptions = {
406 } 413 }
407 414
408 if (options.tagsOneOf) { 415 if (options.tagsOneOf) {
409 query.where[ 'id' ][ Sequelize.Op.and ].push({ 416 query.where[ 'id' ][ Op.and ].push({
410 [ Sequelize.Op.in ]: Sequelize.literal( 417 [ Op.in ]: Sequelize.literal(
411 '(' + 418 '(' +
412 'SELECT "videoId" FROM "videoTag" ' + 419 'SELECT "videoId" FROM "videoTag" ' +
413 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + 420 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' +
@@ -418,8 +425,8 @@ type AvailableForListIDsOptions = {
418 } 425 }
419 426
420 if (options.tagsAllOf) { 427 if (options.tagsAllOf) {
421 query.where[ 'id' ][ Sequelize.Op.and ].push({ 428 query.where[ 'id' ][ Op.and ].push({
422 [ Sequelize.Op.in ]: Sequelize.literal( 429 [ Op.in ]: Sequelize.literal(
423 '(' + 430 '(' +
424 'SELECT "videoId" FROM "videoTag" ' + 431 'SELECT "videoId" FROM "videoTag" ' +
425 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + 432 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' +
@@ -437,19 +444,19 @@ type AvailableForListIDsOptions = {
437 444
438 if (options.categoryOneOf) { 445 if (options.categoryOneOf) {
439 query.where[ 'category' ] = { 446 query.where[ 'category' ] = {
440 [ Sequelize.Op.or ]: options.categoryOneOf 447 [ Op.or ]: options.categoryOneOf
441 } 448 }
442 } 449 }
443 450
444 if (options.licenceOneOf) { 451 if (options.licenceOneOf) {
445 query.where[ 'licence' ] = { 452 query.where[ 'licence' ] = {
446 [ Sequelize.Op.or ]: options.licenceOneOf 453 [ Op.or ]: options.licenceOneOf
447 } 454 }
448 } 455 }
449 456
450 if (options.languageOneOf) { 457 if (options.languageOneOf) {
451 query.where[ 'language' ] = { 458 query.where[ 'language' ] = {
452 [ Sequelize.Op.or ]: options.languageOneOf 459 [ Op.or ]: options.languageOneOf
453 } 460 }
454 } 461 }
455 462
@@ -498,7 +505,7 @@ type AvailableForListIDsOptions = {
498 } 505 }
499 ] 506 ]
500 } 507 }
501 ] 508 ] as any // FIXME: sequelize typings
502 }, 509 },
503 [ ScopeNames.WITH_ACCOUNT_DETAILS ]: { 510 [ ScopeNames.WITH_ACCOUNT_DETAILS ]: {
504 include: [ 511 include: [
@@ -550,7 +557,7 @@ type AvailableForListIDsOptions = {
550 } 557 }
551 ] 558 ]
552 } 559 }
553 ] 560 ] as any // FIXME: sequelize typings
554 }, 561 },
555 [ ScopeNames.WITH_TAGS ]: { 562 [ ScopeNames.WITH_TAGS ]: {
556 include: [ () => TagModel ] 563 include: [ () => TagModel ]
@@ -656,19 +663,19 @@ export class VideoModel extends Model<VideoModel> {
656 663
657 @AllowNull(true) 664 @AllowNull(true)
658 @Default(null) 665 @Default(null)
659 @Is('VideoCategory', value => throwIfNotValid(value, isVideoCategoryValid, 'category')) 666 @Is('VideoCategory', value => throwIfNotValid(value, isVideoCategoryValid, 'category', true))
660 @Column 667 @Column
661 category: number 668 category: number
662 669
663 @AllowNull(true) 670 @AllowNull(true)
664 @Default(null) 671 @Default(null)
665 @Is('VideoLicence', value => throwIfNotValid(value, isVideoLicenceValid, 'licence')) 672 @Is('VideoLicence', value => throwIfNotValid(value, isVideoLicenceValid, 'licence', true))
666 @Column 673 @Column
667 licence: number 674 licence: number
668 675
669 @AllowNull(true) 676 @AllowNull(true)
670 @Default(null) 677 @Default(null)
671 @Is('VideoLanguage', value => throwIfNotValid(value, isVideoLanguageValid, 'language')) 678 @Is('VideoLanguage', value => throwIfNotValid(value, isVideoLanguageValid, 'language', true))
672 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.LANGUAGE.max)) 679 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.LANGUAGE.max))
673 language: string 680 language: string
674 681
@@ -684,13 +691,13 @@ export class VideoModel extends Model<VideoModel> {
684 691
685 @AllowNull(true) 692 @AllowNull(true)
686 @Default(null) 693 @Default(null)
687 @Is('VideoDescription', value => throwIfNotValid(value, isVideoDescriptionValid, 'description')) 694 @Is('VideoDescription', value => throwIfNotValid(value, isVideoDescriptionValid, 'description', true))
688 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max)) 695 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max))
689 description: string 696 description: string
690 697
691 @AllowNull(true) 698 @AllowNull(true)
692 @Default(null) 699 @Default(null)
693 @Is('VideoSupport', value => throwIfNotValid(value, isVideoSupportValid, 'support')) 700 @Is('VideoSupport', value => throwIfNotValid(value, isVideoSupportValid, 'support', true))
694 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.SUPPORT.max)) 701 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.SUPPORT.max))
695 support: string 702 support: string
696 703
@@ -754,7 +761,7 @@ export class VideoModel extends Model<VideoModel> {
754 updatedAt: Date 761 updatedAt: Date
755 762
756 @AllowNull(false) 763 @AllowNull(false)
757 @Default(Sequelize.NOW) 764 @Default(DataType.NOW)
758 @Column 765 @Column
759 publishedAt: Date 766 publishedAt: Date
760 767
@@ -999,12 +1006,12 @@ export class VideoModel extends Model<VideoModel> {
999 distinct: true, 1006 distinct: true,
1000 offset: start, 1007 offset: start,
1001 limit: count, 1008 limit: count,
1002 order: getVideoSort('createdAt', [ 'Tags', 'name', 'ASC' ]), 1009 order: getVideoSort('createdAt', [ 'Tags', 'name', 'ASC' ] as any), // FIXME: sequelize typings
1003 where: { 1010 where: {
1004 id: { 1011 id: {
1005 [ Sequelize.Op.in ]: Sequelize.literal('(' + rawQuery + ')') 1012 [ Op.in ]: Sequelize.literal('(' + rawQuery + ')')
1006 }, 1013 },
1007 [ Sequelize.Op.or ]: [ 1014 [ Op.or ]: [
1008 { privacy: VideoPrivacy.PUBLIC }, 1015 { privacy: VideoPrivacy.PUBLIC },
1009 { privacy: VideoPrivacy.UNLISTED } 1016 { privacy: VideoPrivacy.UNLISTED }
1010 ] 1017 ]
@@ -1021,10 +1028,10 @@ export class VideoModel extends Model<VideoModel> {
1021 required: false, 1028 required: false,
1022 // We only want videos shared by this actor 1029 // We only want videos shared by this actor
1023 where: { 1030 where: {
1024 [ Sequelize.Op.and ]: [ 1031 [ Op.and ]: [
1025 { 1032 {
1026 id: { 1033 id: {
1027 [ Sequelize.Op.not ]: null 1034 [ Op.not ]: null
1028 } 1035 }
1029 }, 1036 },
1030 { 1037 {
@@ -1070,13 +1077,13 @@ export class VideoModel extends Model<VideoModel> {
1070 return Bluebird.all([ 1077 return Bluebird.all([
1071 // FIXME: typing issue 1078 // FIXME: typing issue
1072 VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findAll(query as any), 1079 VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findAll(query as any),
1073 VideoModel.sequelize.query(rawCountQuery, { type: Sequelize.QueryTypes.SELECT }) 1080 VideoModel.sequelize.query<{ total: number }>(rawCountQuery, { type: QueryTypes.SELECT })
1074 ]).then(([ rows, totals ]) => { 1081 ]).then(([ rows, totals ]) => {
1075 // totals: totalVideos + totalVideoShares 1082 // totals: totalVideos + totalVideoShares
1076 let totalVideos = 0 1083 let totalVideos = 0
1077 let totalVideoShares = 0 1084 let totalVideoShares = 0
1078 if (totals[ 0 ]) totalVideos = parseInt(totals[ 0 ].total, 10) 1085 if (totals[ 0 ]) totalVideos = parseInt(totals[ 0 ].total + '', 10)
1079 if (totals[ 1 ]) totalVideoShares = parseInt(totals[ 1 ].total, 10) 1086 if (totals[ 1 ]) totalVideoShares = parseInt(totals[ 1 ].total + '', 10)
1080 1087
1081 const total = totalVideos + totalVideoShares 1088 const total = totalVideos + totalVideoShares
1082 return { 1089 return {
@@ -1087,7 +1094,7 @@ export class VideoModel extends Model<VideoModel> {
1087 } 1094 }
1088 1095
1089 static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) { 1096 static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) {
1090 const query: IFindOptions<VideoModel> = { 1097 const query: FindOptions = {
1091 offset: start, 1098 offset: start,
1092 limit: count, 1099 limit: count,
1093 order: getVideoSort(sort), 1100 order: getVideoSort(sort),
@@ -1158,7 +1165,7 @@ export class VideoModel extends Model<VideoModel> {
1158 throw new Error('Try to filter all-local but no user has not the see all videos right') 1165 throw new Error('Try to filter all-local but no user has not the see all videos right')
1159 } 1166 }
1160 1167
1161 const query: IFindOptions<VideoModel> = { 1168 const query: FindOptions = {
1162 offset: options.start, 1169 offset: options.start,
1163 limit: options.count, 1170 limit: options.count,
1164 order: getVideoSort(options.sort) 1171 order: getVideoSort(options.sort)
@@ -1225,8 +1232,8 @@ export class VideoModel extends Model<VideoModel> {
1225 if (options.startDate || options.endDate) { 1232 if (options.startDate || options.endDate) {
1226 const publishedAtRange = {} 1233 const publishedAtRange = {}
1227 1234
1228 if (options.startDate) publishedAtRange[ Sequelize.Op.gte ] = options.startDate 1235 if (options.startDate) publishedAtRange[ Op.gte ] = options.startDate
1229 if (options.endDate) publishedAtRange[ Sequelize.Op.lte ] = options.endDate 1236 if (options.endDate) publishedAtRange[ Op.lte ] = options.endDate
1230 1237
1231 whereAnd.push({ publishedAt: publishedAtRange }) 1238 whereAnd.push({ publishedAt: publishedAtRange })
1232 } 1239 }
@@ -1234,8 +1241,8 @@ export class VideoModel extends Model<VideoModel> {
1234 if (options.originallyPublishedStartDate || options.originallyPublishedEndDate) { 1241 if (options.originallyPublishedStartDate || options.originallyPublishedEndDate) {
1235 const originallyPublishedAtRange = {} 1242 const originallyPublishedAtRange = {}
1236 1243
1237 if (options.originallyPublishedStartDate) originallyPublishedAtRange[ Sequelize.Op.gte ] = options.originallyPublishedStartDate 1244 if (options.originallyPublishedStartDate) originallyPublishedAtRange[ Op.gte ] = options.originallyPublishedStartDate
1238 if (options.originallyPublishedEndDate) originallyPublishedAtRange[ Sequelize.Op.lte ] = options.originallyPublishedEndDate 1245 if (options.originallyPublishedEndDate) originallyPublishedAtRange[ Op.lte ] = options.originallyPublishedEndDate
1239 1246
1240 whereAnd.push({ originallyPublishedAt: originallyPublishedAtRange }) 1247 whereAnd.push({ originallyPublishedAt: originallyPublishedAtRange })
1241 } 1248 }
@@ -1243,8 +1250,8 @@ export class VideoModel extends Model<VideoModel> {
1243 if (options.durationMin || options.durationMax) { 1250 if (options.durationMin || options.durationMax) {
1244 const durationRange = {} 1251 const durationRange = {}
1245 1252
1246 if (options.durationMin) durationRange[ Sequelize.Op.gte ] = options.durationMin 1253 if (options.durationMin) durationRange[ Op.gte ] = options.durationMin
1247 if (options.durationMax) durationRange[ Sequelize.Op.lte ] = options.durationMax 1254 if (options.durationMax) durationRange[ Op.lte ] = options.durationMax
1248 1255
1249 whereAnd.push({ duration: durationRange }) 1256 whereAnd.push({ duration: durationRange })
1250 } 1257 }
@@ -1256,7 +1263,7 @@ export class VideoModel extends Model<VideoModel> {
1256 whereAnd.push( 1263 whereAnd.push(
1257 { 1264 {
1258 id: { 1265 id: {
1259 [ Sequelize.Op.in ]: Sequelize.literal( 1266 [ Op.in ]: Sequelize.literal(
1260 '(' + 1267 '(' +
1261 'SELECT "video"."id" FROM "video" ' + 1268 'SELECT "video"."id" FROM "video" ' +
1262 'WHERE ' + 1269 'WHERE ' +
@@ -1282,7 +1289,7 @@ export class VideoModel extends Model<VideoModel> {
1282 ) 1289 )
1283 } 1290 }
1284 1291
1285 const query: IFindOptions<VideoModel> = { 1292 const query: FindOptions = {
1286 attributes: { 1293 attributes: {
1287 include: attributesInclude 1294 include: attributesInclude
1288 }, 1295 },
@@ -1290,7 +1297,7 @@ export class VideoModel extends Model<VideoModel> {
1290 limit: options.count, 1297 limit: options.count,
1291 order: getVideoSort(options.sort), 1298 order: getVideoSort(options.sort),
1292 where: { 1299 where: {
1293 [ Sequelize.Op.and ]: whereAnd 1300 [ Op.and ]: whereAnd
1294 } 1301 }
1295 } 1302 }
1296 1303
@@ -1312,7 +1319,7 @@ export class VideoModel extends Model<VideoModel> {
1312 return VideoModel.getAvailableForApi(query, queryOptions) 1319 return VideoModel.getAvailableForApi(query, queryOptions)
1313 } 1320 }
1314 1321
1315 static load (id: number | string, t?: Sequelize.Transaction) { 1322 static load (id: number | string, t?: Transaction) {
1316 const where = buildWhereIdOrUUID(id) 1323 const where = buildWhereIdOrUUID(id)
1317 const options = { 1324 const options = {
1318 where, 1325 where,
@@ -1322,7 +1329,7 @@ export class VideoModel extends Model<VideoModel> {
1322 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) 1329 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
1323 } 1330 }
1324 1331
1325 static loadWithRights (id: number | string, t?: Sequelize.Transaction) { 1332 static loadWithRights (id: number | string, t?: Transaction) {
1326 const where = buildWhereIdOrUUID(id) 1333 const where = buildWhereIdOrUUID(id)
1327 const options = { 1334 const options = {
1328 where, 1335 where,
@@ -1336,7 +1343,7 @@ export class VideoModel extends Model<VideoModel> {
1336 ]).findOne(options) 1343 ]).findOne(options)
1337 } 1344 }
1338 1345
1339 static loadOnlyId (id: number | string, t?: Sequelize.Transaction) { 1346 static loadOnlyId (id: number | string, t?: Transaction) {
1340 const where = buildWhereIdOrUUID(id) 1347 const where = buildWhereIdOrUUID(id)
1341 1348
1342 const options = { 1349 const options = {
@@ -1348,7 +1355,7 @@ export class VideoModel extends Model<VideoModel> {
1348 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) 1355 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
1349 } 1356 }
1350 1357
1351 static loadWithFiles (id: number, t?: Sequelize.Transaction, logging?: boolean) { 1358 static loadWithFiles (id: number, t?: Transaction, logging?: boolean) {
1352 return VideoModel.scope([ 1359 return VideoModel.scope([
1353 ScopeNames.WITH_FILES, 1360 ScopeNames.WITH_FILES,
1354 ScopeNames.WITH_STREAMING_PLAYLISTS, 1361 ScopeNames.WITH_STREAMING_PLAYLISTS,
@@ -1366,8 +1373,8 @@ export class VideoModel extends Model<VideoModel> {
1366 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) 1373 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
1367 } 1374 }
1368 1375
1369 static loadByUrl (url: string, transaction?: Sequelize.Transaction) { 1376 static loadByUrl (url: string, transaction?: Transaction) {
1370 const query: IFindOptions<VideoModel> = { 1377 const query: FindOptions = {
1371 where: { 1378 where: {
1372 url 1379 url
1373 }, 1380 },
@@ -1377,8 +1384,8 @@ export class VideoModel extends Model<VideoModel> {
1377 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query) 1384 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query)
1378 } 1385 }
1379 1386
1380 static loadByUrlAndPopulateAccount (url: string, transaction?: Sequelize.Transaction) { 1387 static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction) {
1381 const query: IFindOptions<VideoModel> = { 1388 const query: FindOptions = {
1382 where: { 1389 where: {
1383 url 1390 url
1384 }, 1391 },
@@ -1393,11 +1400,11 @@ export class VideoModel extends Model<VideoModel> {
1393 ]).findOne(query) 1400 ]).findOne(query)
1394 } 1401 }
1395 1402
1396 static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Sequelize.Transaction, userId?: number) { 1403 static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number) {
1397 const where = buildWhereIdOrUUID(id) 1404 const where = buildWhereIdOrUUID(id)
1398 1405
1399 const options = { 1406 const options = {
1400 order: [ [ 'Tags', 'name', 'ASC' ] ], 1407 order: [ [ 'Tags', 'name', 'ASC' ] ] as any, // FIXME: sequelize typings
1401 where, 1408 where,
1402 transaction: t 1409 transaction: t
1403 } 1410 }
@@ -1421,11 +1428,11 @@ export class VideoModel extends Model<VideoModel> {
1421 .findOne(options) 1428 .findOne(options)
1422 } 1429 }
1423 1430
1424 static loadForGetAPI (id: number | string, t?: Sequelize.Transaction, userId?: number) { 1431 static loadForGetAPI (id: number | string, t?: Transaction, userId?: number) {
1425 const where = buildWhereIdOrUUID(id) 1432 const where = buildWhereIdOrUUID(id)
1426 1433
1427 const options = { 1434 const options = {
1428 order: [ [ 'Tags', 'name', 'ASC' ] ], 1435 order: [ [ 'Tags', 'name', 'ASC' ] ] as any, // FIXME: sequelize typings
1429 where, 1436 where,
1430 transaction: t 1437 transaction: t
1431 } 1438 }
@@ -1489,7 +1496,7 @@ export class VideoModel extends Model<VideoModel> {
1489 'LIMIT 1' 1496 'LIMIT 1'
1490 1497
1491 const options = { 1498 const options = {
1492 type: Sequelize.QueryTypes.SELECT, 1499 type: QueryTypes.SELECT,
1493 bind: { followerActorId, videoId }, 1500 bind: { followerActorId, videoId },
1494 raw: true 1501 raw: true
1495 } 1502 }
@@ -1509,14 +1516,14 @@ export class VideoModel extends Model<VideoModel> {
1509 includeLocalVideos: true 1516 includeLocalVideos: true
1510 } 1517 }
1511 1518
1512 const query: IFindOptions<VideoModel> = { 1519 const query: FindOptions = {
1513 attributes: [ field ], 1520 attributes: [ field ],
1514 limit: count, 1521 limit: count,
1515 group: field, 1522 group: field,
1516 having: Sequelize.where(Sequelize.fn('COUNT', Sequelize.col(field)), { 1523 having: Sequelize.where(Sequelize.fn('COUNT', Sequelize.col(field)), {
1517 [ Sequelize.Op.gte ]: threshold 1524 [ Op.gte ]: threshold
1518 }) as any, // FIXME: typings 1525 }) as any, // FIXME: typings
1519 order: [ this.sequelize.random() ] 1526 order: [ (this.sequelize as any).random() ]
1520 } 1527 }
1521 1528
1522 return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST_IDS, scopeOptions ] }) 1529 return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST_IDS, scopeOptions ] })
@@ -1532,7 +1539,7 @@ export class VideoModel extends Model<VideoModel> {
1532 required: false, 1539 required: false,
1533 where: { 1540 where: {
1534 startDate: { 1541 startDate: {
1535 [ Sequelize.Op.gte ]: new Date(new Date().getTime() - (24 * 3600 * 1000) * trendingDays) 1542 [ Op.gte ]: new Date(new Date().getTime() - (24 * 3600 * 1000) * trendingDays)
1536 } 1543 }
1537 } 1544 }
1538 } 1545 }
@@ -1549,11 +1556,11 @@ export class VideoModel extends Model<VideoModel> {
1549 } 1556 }
1550 1557
1551 private static async getAvailableForApi ( 1558 private static async getAvailableForApi (
1552 query: IFindOptions<VideoModel>, 1559 query: FindOptions,
1553 options: AvailableForListIDsOptions, 1560 options: AvailableForListIDsOptions,
1554 countVideos = true 1561 countVideos = true
1555 ) { 1562 ) {
1556 const idsScope = { 1563 const idsScope: ScopeOptions = {
1557 method: [ 1564 method: [
1558 ScopeNames.AVAILABLE_FOR_LIST_IDS, options 1565 ScopeNames.AVAILABLE_FOR_LIST_IDS, options
1559 ] 1566 ]
@@ -1561,8 +1568,8 @@ export class VideoModel extends Model<VideoModel> {
1561 1568
1562 // Remove trending sort on count, because it uses a group by 1569 // Remove trending sort on count, because it uses a group by
1563 const countOptions = Object.assign({}, options, { trendingDays: undefined }) 1570 const countOptions = Object.assign({}, options, { trendingDays: undefined })
1564 const countQuery = Object.assign({}, query, { attributes: undefined, group: undefined }) 1571 const countQuery: CountOptions = Object.assign({}, query, { attributes: undefined, group: undefined })
1565 const countScope = { 1572 const countScope: ScopeOptions = {
1566 method: [ 1573 method: [
1567 ScopeNames.AVAILABLE_FOR_LIST_IDS, countOptions 1574 ScopeNames.AVAILABLE_FOR_LIST_IDS, countOptions
1568 ] 1575 ]
@@ -1576,7 +1583,7 @@ export class VideoModel extends Model<VideoModel> {
1576 1583
1577 if (ids.length === 0) return { data: [], total: count } 1584 if (ids.length === 0) return { data: [], total: count }
1578 1585
1579 const secondQuery: IFindOptions<VideoModel> = { 1586 const secondQuery: FindOptions = {
1580 offset: 0, 1587 offset: 0,
1581 limit: query.limit, 1588 limit: query.limit,
1582 attributes: query.attributes, 1589 attributes: query.attributes,
diff --git a/shared/extra-utils/miscs/sql.ts b/shared/extra-utils/miscs/sql.ts
index b281471ce..7aebffc32 100644
--- a/shared/extra-utils/miscs/sql.ts
+++ b/shared/extra-utils/miscs/sql.ts
@@ -1,6 +1,6 @@
1import * as Sequelize from 'sequelize' 1import { QueryTypes, Sequelize } from 'sequelize'
2 2
3let sequelizes: { [ id: number ]: Sequelize.Sequelize } = {} 3let sequelizes: { [ id: number ]: Sequelize } = {}
4 4
5function getSequelize (serverNumber: number) { 5function getSequelize (serverNumber: number) {
6 if (sequelizes[serverNumber]) return sequelizes[serverNumber] 6 if (sequelizes[serverNumber]) return sequelizes[serverNumber]
@@ -27,7 +27,7 @@ function getSequelize (serverNumber: number) {
27function setActorField (serverNumber: number, to: string, field: string, value: string) { 27function setActorField (serverNumber: number, to: string, field: string, value: string) {
28 const seq = getSequelize(serverNumber) 28 const seq = getSequelize(serverNumber)
29 29
30 const options = { type: Sequelize.QueryTypes.UPDATE } 30 const options = { type: QueryTypes.UPDATE }
31 31
32 return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options) 32 return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options)
33} 33}
@@ -35,7 +35,7 @@ function setActorField (serverNumber: number, to: string, field: string, value:
35function setVideoField (serverNumber: number, uuid: string, field: string, value: string) { 35function setVideoField (serverNumber: number, uuid: string, field: string, value: string) {
36 const seq = getSequelize(serverNumber) 36 const seq = getSequelize(serverNumber)
37 37
38 const options = { type: Sequelize.QueryTypes.UPDATE } 38 const options = { type: QueryTypes.UPDATE }
39 39
40 return seq.query(`UPDATE video SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options) 40 return seq.query(`UPDATE video SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
41} 41}
@@ -43,7 +43,7 @@ function setVideoField (serverNumber: number, uuid: string, field: string, value
43function setPlaylistField (serverNumber: number, uuid: string, field: string, value: string) { 43function setPlaylistField (serverNumber: number, uuid: string, field: string, value: string) {
44 const seq = getSequelize(serverNumber) 44 const seq = getSequelize(serverNumber)
45 45
46 const options = { type: Sequelize.QueryTypes.UPDATE } 46 const options = { type: QueryTypes.UPDATE }
47 47
48 return seq.query(`UPDATE "videoPlaylist" SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options) 48 return seq.query(`UPDATE "videoPlaylist" SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
49} 49}
@@ -54,12 +54,13 @@ async function countVideoViewsOf (serverNumber: number, uuid: string) {
54 // tslint:disable 54 // tslint:disable
55 const query = `SELECT SUM("videoView"."views") AS "total" FROM "videoView" INNER JOIN "video" ON "video"."id" = "videoView"."videoId" WHERE "video"."uuid" = '${uuid}'` 55 const query = `SELECT SUM("videoView"."views") AS "total" FROM "videoView" INNER JOIN "video" ON "video"."id" = "videoView"."videoId" WHERE "video"."uuid" = '${uuid}'`
56 56
57 const options = { type: Sequelize.QueryTypes.SELECT } 57 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
58 const [ { total } ] = await seq.query(query, options) 58 const [ { total } ] = await seq.query<{ total: number }>(query, options)
59 59
60 if (!total) return 0 60 if (!total) return 0
61 61
62 return parseInt(total, 10) 62 // FIXME: check if we really need parseInt
63 return parseInt(total + '', 10)
63} 64}
64 65
65async function closeAllSequelize (servers: any[]) { 66async function closeAllSequelize (servers: any[]) {
diff --git a/yarn.lock b/yarn.lock
index 8b4f07509..dd1de4fa0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -38,7 +38,7 @@
38 dependencies: 38 dependencies:
39 "@types/node" "*" 39 "@types/node" "*"
40 40
41"@types/bluebird@*", "@types/bluebird@3.5.18", "@types/bluebird@3.5.21": 41"@types/bluebird@*", "@types/bluebird@3.5.21":
42 version "3.5.21" 42 version "3.5.21"
43 resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.21.tgz#567615589cc913e84a28ecf9edb031732bdf2634" 43 resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.21.tgz#567615589cc913e84a28ecf9edb031732bdf2634"
44 integrity sha512-6UNEwyw+6SGMC/WMI0ld0PS4st7Qq51qgguFrFizOSpGvZiqe9iswztFSdZvwJBEhLOy2JaxNE6VC7yMAlbfyQ== 44 integrity sha512-6UNEwyw+6SGMC/WMI0ld0PS4st7Qq51qgguFrFizOSpGvZiqe9iswztFSdZvwJBEhLOy2JaxNE6VC7yMAlbfyQ==
@@ -100,13 +100,6 @@
100 dependencies: 100 dependencies:
101 "@types/node" "*" 101 "@types/node" "*"
102 102
103"@types/continuation-local-storage@*":
104 version "3.2.1"
105 resolved "https://registry.yarnpkg.com/@types/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#a33e0df9dce9b424d1c98fc4fdebd8578dceec7e"
106 integrity sha1-oz4N+dzptCTRyY/E/evYV43O7H4=
107 dependencies:
108 "@types/node" "*"
109
110"@types/cookiejar@*": 103"@types/cookiejar@*":
111 version "2.1.1" 104 version "2.1.1"
112 resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.1.tgz#90b68446364baf9efd8e8349bb36bd3852b75b80" 105 resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.1.tgz#90b68446364baf9efd8e8349bb36bd3852b75b80"
@@ -162,11 +155,6 @@
162 dependencies: 155 dependencies:
163 "@types/node" "*" 156 "@types/node" "*"
164 157
165"@types/geojson@^1.0.0":
166 version "1.0.6"
167 resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-1.0.6.tgz#3e02972728c69248c2af08d60a48cbb8680fffdf"
168 integrity sha512-Xqg/lIZMrUd0VRmSRbCAewtwGZiAk3mEUDvV4op1tGl+LvyPcb/MIOSxTl9z+9+J+R4/vpjiCAT4xeKzH9ji1w==
169
170"@types/ioredis@*": 158"@types/ioredis@*":
171 version "4.0.10" 159 version "4.0.10"
172 resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.0.10.tgz#ca8bd95ca7d5fee32cbc5a0bf92fc29264bee237" 160 resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.0.10.tgz#ca8bd95ca7d5fee32cbc5a0bf92fc29264bee237"
@@ -181,7 +169,7 @@
181 dependencies: 169 dependencies:
182 "@types/node" "*" 170 "@types/node" "*"
183 171
184"@types/lodash@*", "@types/lodash@^4.14.64": 172"@types/lodash@^4.14.64":
185 version "4.14.123" 173 version "4.14.123"
186 resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.123.tgz#39be5d211478c8dd3bdae98ee75bb7efe4abfe4d" 174 resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.123.tgz#39be5d211478c8dd3bdae98ee75bb7efe4abfe4d"
187 integrity sha512-pQvPkc4Nltyx7G1Ww45OjVqUsJP4UsZm+GWJpigXgkikZqJgRm4c48g027o6tdgubWHwFRF15iFd+Y4Pmqv6+Q== 175 integrity sha512-pQvPkc4Nltyx7G1Ww45OjVqUsJP4UsZm+GWJpigXgkikZqJgRm4c48g027o6tdgubWHwFRF15iFd+Y4Pmqv6+Q==
@@ -241,11 +229,6 @@
241 resolved "https://registry.yarnpkg.com/@types/node/-/node-11.13.0.tgz#b0df8d6ef9b5001b2be3a94d909ce3c29a80f9e1" 229 resolved "https://registry.yarnpkg.com/@types/node/-/node-11.13.0.tgz#b0df8d6ef9b5001b2be3a94d909ce3c29a80f9e1"
242 integrity sha512-rx29MMkRdVmzunmiA4lzBYJNnXsW/PhG4kMBy2ATsYaDjGGR75dCFEVVROKpNwlVdcUX3xxlghKQOeDPBJobng== 230 integrity sha512-rx29MMkRdVmzunmiA4lzBYJNnXsW/PhG4kMBy2ATsYaDjGGR75dCFEVVROKpNwlVdcUX3xxlghKQOeDPBJobng==
243 231
244"@types/node@6.0.41":
245 version "6.0.41"
246 resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.41.tgz#578cf53aaec65887bcaf16792f8722932e8ff8ea"
247 integrity sha1-V4z1Oq7GWIe8rxZ5L4ciky6P+Oo=
248
249"@types/node@^10.0.8": 232"@types/node@^10.0.8":
250 version "10.14.4" 233 version "10.14.4"
251 resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.4.tgz#1c586b991457cbb58fef51bc4e0cfcfa347714b5" 234 resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.4.tgz#1c586b991457cbb58fef51bc4e0cfcfa347714b5"
@@ -310,16 +293,6 @@
310 "@types/node" "*" 293 "@types/node" "*"
311 "@types/tough-cookie" "*" 294 "@types/tough-cookie" "*"
312 295
313"@types/sequelize@4.27.24":
314 version "4.27.24"
315 resolved "https://registry.yarnpkg.com/@types/sequelize/-/sequelize-4.27.24.tgz#7d593c062c368f570c68b0217f5c1d4c892ead48"
316 integrity sha512-5uMFsMa/0hU/7/8znyfBKSJy2Mbd57uRpYk5X1+Phz9dN0MRZLbTbj1JMeB3CJ4R9b1coNQGfp2kXh4OjI9UyA==
317 dependencies:
318 "@types/bluebird" "*"
319 "@types/continuation-local-storage" "*"
320 "@types/lodash" "*"
321 "@types/validator" "*"
322
323"@types/serve-static@*": 296"@types/serve-static@*":
324 version "1.13.2" 297 version "1.13.2"
325 resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48" 298 resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48"
@@ -374,7 +347,7 @@
374 resolved "https://registry.yarnpkg.com/@types/tv4/-/tv4-1.2.29.tgz#4c6d2222b03245dd2104f4fd67f54d1658985911" 347 resolved "https://registry.yarnpkg.com/@types/tv4/-/tv4-1.2.29.tgz#4c6d2222b03245dd2104f4fd67f54d1658985911"
375 integrity sha512-NtJmi+XbYocrLb5Au4Q64srX4FlCPDvrSF/OnK3H0QJwrw40tIUoQPDoUHnZ5wpAB2KThtVyeS+kOEQyZabORg== 348 integrity sha512-NtJmi+XbYocrLb5Au4Q64srX4FlCPDvrSF/OnK3H0QJwrw40tIUoQPDoUHnZ5wpAB2KThtVyeS+kOEQyZabORg==
376 349
377"@types/validator@*", "@types/validator@^10.9.0": 350"@types/validator@^10.9.0":
378 version "10.11.0" 351 version "10.11.0"
379 resolved "https://registry.yarnpkg.com/@types/validator/-/validator-10.11.0.tgz#aae9009ce28cc4f878e32c34d3900a81193c98d5" 352 resolved "https://registry.yarnpkg.com/@types/validator/-/validator-10.11.0.tgz#aae9009ce28cc4f878e32c34d3900a81193c98d5"
380 integrity sha512-i1aY7RKb6HmQIEnK0cBmUZUp1URx0riIHw/GYNoZ46Su0GWfLiDmMI8zMRmaauMnOTg2bQag0qfwcyUFC9Tn+A== 353 integrity sha512-i1aY7RKb6HmQIEnK0cBmUZUp1URx0riIHw/GYNoZ46Su0GWfLiDmMI8zMRmaauMnOTg2bQag0qfwcyUFC9Tn+A==
@@ -562,6 +535,11 @@ any-observable@^0.3.0:
562 resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" 535 resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b"
563 integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== 536 integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==
564 537
538any-promise@^1.3.0:
539 version "1.3.0"
540 resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
541 integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
542
565anymatch@^2.0.0: 543anymatch@^2.0.0:
566 version "2.0.0" 544 version "2.0.0"
567 resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" 545 resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@@ -1033,7 +1011,7 @@ bluebird@^2.10.0:
1033 resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" 1011 resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
1034 integrity sha1-U0uQM8AiyVecVro7Plpcqvu2UOE= 1012 integrity sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=
1035 1013
1036bluebird@^3.0.5, bluebird@^3.4.6, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.3: 1014bluebird@^3.0.5, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.3:
1037 version "3.5.3" 1015 version "3.5.3"
1038 resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" 1016 resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7"
1039 integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw== 1017 integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==
@@ -1992,7 +1970,7 @@ debug@2.3.3:
1992 dependencies: 1970 dependencies:
1993 ms "0.7.2" 1971 ms "0.7.2"
1994 1972
1995debug@2.6.9, debug@^2.1.1, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: 1973debug@2.6.9, debug@^2.1.1, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3:
1996 version "2.6.9" 1974 version "2.6.9"
1997 resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 1975 resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
1998 integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 1976 integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
@@ -2013,7 +1991,7 @@ debug@3.2.6, debug@^3.1.0:
2013 dependencies: 1991 dependencies:
2014 ms "^2.1.1" 1992 ms "^2.1.1"
2015 1993
2016debug@^4.0.1, debug@^4.1.0, debug@~4.1.0: 1994debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0:
2017 version "4.1.1" 1995 version "4.1.1"
2018 resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" 1996 resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
2019 integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== 1997 integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
@@ -2154,7 +2132,7 @@ depd@2.0.0:
2154 resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" 2132 resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
2155 integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== 2133 integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
2156 2134
2157depd@^1.1.0, depd@~1.1.0, depd@~1.1.2: 2135depd@~1.1.0, depd@~1.1.2:
2158 version "1.1.2" 2136 version "1.1.2"
2159 resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 2137 resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
2160 integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= 2138 integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
@@ -2548,11 +2526,6 @@ es6-set@~0.1.5:
2548 es6-symbol "3.1.1" 2526 es6-symbol "3.1.1"
2549 event-emitter "~0.3.5" 2527 event-emitter "~0.3.5"
2550 2528
2551es6-shim@0.35.3:
2552 version "0.35.3"
2553 resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.3.tgz#9bfb7363feffff87a6cdb6cd93e405ec3c4b6f26"
2554 integrity sha1-m/tzY/7//4emzbbNk+QF7DxLbyY=
2555
2556es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1: 2529es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1:
2557 version "3.1.1" 2530 version "3.1.1"
2558 resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" 2531 resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
@@ -3295,11 +3268,6 @@ generate-object-property@^1.1.0:
3295 dependencies: 3268 dependencies:
3296 is-property "^1.0.0" 3269 is-property "^1.0.0"
3297 3270
3298generic-pool@^3.4.0:
3299 version "3.7.1"
3300 resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.7.1.tgz#36fe5bb83e7e0e032e5d32cd05dc00f5ff119aa8"
3301 integrity sha512-ug6DAZoNgWm6q5KhPFA+hzXfBLFQu5sTXxPpv44DmE0A2g+CiHoq9LTVdkXpZMkYVMoGw83F6W+WT0h0MFMK/w==
3302
3303genfun@^5.0.0: 3271genfun@^5.0.0:
3304 version "5.0.0" 3272 version "5.0.0"
3305 resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" 3273 resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537"
@@ -4955,7 +4923,7 @@ lodash@=3.10.1:
4955 resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" 4923 resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
4956 integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y= 4924 integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=
4957 4925
4958lodash@^4.0.0, lodash@^4.17.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.3.0, lodash@~4.17.10: 4926lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.3.0, lodash@~4.17.10:
4959 version "4.17.11" 4927 version "4.17.11"
4960 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" 4928 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
4961 integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== 4929 integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
@@ -5376,14 +5344,14 @@ mocha@^6.0.0:
5376 yargs-parser "11.1.1" 5344 yargs-parser "11.1.1"
5377 yargs-unparser "1.5.0" 5345 yargs-unparser "1.5.0"
5378 5346
5379moment-timezone@^0.5.14, moment-timezone@^0.5.23: 5347moment-timezone@^0.5.21, moment-timezone@^0.5.23:
5380 version "0.5.23" 5348 version "0.5.23"
5381 resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.23.tgz#7cbb00db2c14c71b19303cb47b0fb0a6d8651463" 5349 resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.23.tgz#7cbb00db2c14c71b19303cb47b0fb0a6d8651463"
5382 integrity sha512-WHFH85DkCfiNMDX5D3X7hpNH3/PUhjTGcD0U1SgfBGZxJ3qUmJh5FdvaFjcClxOvB3rzdfj4oRffbI38jEnC1w== 5350 integrity sha512-WHFH85DkCfiNMDX5D3X7hpNH3/PUhjTGcD0U1SgfBGZxJ3qUmJh5FdvaFjcClxOvB3rzdfj4oRffbI38jEnC1w==
5383 dependencies: 5351 dependencies:
5384 moment ">= 2.9.0" 5352 moment ">= 2.9.0"
5385 5353
5386"moment@>= 2.9.0", moment@^2.20.0: 5354"moment@>= 2.9.0", moment@^2.24.0:
5387 version "2.24.0" 5355 version "2.24.0"
5388 resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" 5356 resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
5389 integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== 5357 integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
@@ -7272,13 +7240,12 @@ ret@~0.1.10:
7272 resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" 7240 resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
7273 integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== 7241 integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
7274 7242
7275retry-as-promised@^2.3.2: 7243retry-as-promised@^3.1.0:
7276 version "2.3.2" 7244 version "3.2.0"
7277 resolved "https://registry.yarnpkg.com/retry-as-promised/-/retry-as-promised-2.3.2.tgz#cd974ee4fd9b5fe03cbf31871ee48221c07737b7" 7245 resolved "https://registry.yarnpkg.com/retry-as-promised/-/retry-as-promised-3.2.0.tgz#769f63d536bec4783549db0777cb56dadd9d8543"
7278 integrity sha1-zZdO5P2bX+A8vzGHHuSCIcB3N7c= 7246 integrity sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==
7279 dependencies: 7247 dependencies:
7280 bluebird "^3.4.6" 7248 any-promise "^1.3.0"
7281 debug "^2.6.9"
7282 7249
7283retry@^0.10.0: 7250retry@^0.10.0:
7284 version "0.10.1" 7251 version "0.10.1"
@@ -7487,39 +7454,40 @@ send@0.16.2:
7487 range-parser "~1.2.0" 7454 range-parser "~1.2.0"
7488 statuses "~1.4.0" 7455 statuses "~1.4.0"
7489 7456
7490sequelize-typescript@0.6.7: 7457sequelize-pool@^1.0.2:
7491 version "0.6.7" 7458 version "1.0.2"
7492 resolved "https://registry.yarnpkg.com/sequelize-typescript/-/sequelize-typescript-0.6.7.tgz#372be979cbae060c554fdbaf2c2d8c93a0283303" 7459 resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-1.0.2.tgz#89c767882bbdb8a41dac66922ed9820939a5401e"
7493 integrity sha512-ae21Gq9VOVXlIjnh2vLdL42Kev6r4LC82xOQ2fXo0lHzQvjFeH8/GaWUlpvYv57AOgc+ZnElbYFbSLKdT9ue0w== 7460 integrity sha512-VMKl/gCCdIvB1gFZ7p+oqLFEyZEz3oMMYjkKvfEC7GoO9bBcxmfOOU9RdkoltfXGgBZFigSChihRly2gKtsh2w==
7461 dependencies:
7462 bluebird "^3.5.3"
7463
7464sequelize-typescript@^1.0.0-beta.1:
7465 version "1.0.0-beta.1"
7466 resolved "https://registry.yarnpkg.com/sequelize-typescript/-/sequelize-typescript-1.0.0-beta.1.tgz#402279fec52669cbd78ecbf50e189638483a7360"
7467 integrity sha512-xD28kqa1rIKujlmgA4hWQgtwFfRM6tLv1/mnZOrOFEZxvSWazUbTzqGB7OZydZDNj3iJnyrV1l6i6HOfvrpvEw==
7494 dependencies: 7468 dependencies:
7495 "@types/bluebird" "3.5.18"
7496 "@types/node" "6.0.41"
7497 "@types/sequelize" "4.27.24"
7498 es6-shim "0.35.3"
7499 glob "7.1.2" 7469 glob "7.1.2"
7500 7470
7501sequelize@4.42.0: 7471sequelize@5.6.1:
7502 version "4.42.0" 7472 version "5.6.1"
7503 resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-4.42.0.tgz#439467ba7bfe7d5afcc56d62b3e091860fbf18f3" 7473 resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-5.6.1.tgz#fc22306109fb2504a6573edfb3c469ec86fae873"
7504 integrity sha512-qxAYnX4rcv7PbNtEidb56REpxNJCdbN0qyk1jb3+e6sE7OrmS0nYMU+MFVbNTJtZfSpOEEL1TX0TkMw+wzZBxg== 7474 integrity sha512-QsXUDar6ow0HrF9BtnHRaNumu6qRYb97dfwvez/Z5guH3i6w6k8+bp6gP3VCiDC+2qX+jQIyrYohKg9evy8GFg==
7505 dependencies: 7475 dependencies:
7506 bluebird "^3.5.0" 7476 bluebird "^3.5.0"
7507 cls-bluebird "^2.1.0" 7477 cls-bluebird "^2.1.0"
7508 debug "^3.1.0" 7478 debug "^4.1.1"
7509 depd "^1.1.0"
7510 dottie "^2.0.0" 7479 dottie "^2.0.0"
7511 generic-pool "^3.4.0"
7512 inflection "1.12.0" 7480 inflection "1.12.0"
7513 lodash "^4.17.1" 7481 lodash "^4.17.11"
7514 moment "^2.20.0" 7482 moment "^2.24.0"
7515 moment-timezone "^0.5.14" 7483 moment-timezone "^0.5.21"
7516 retry-as-promised "^2.3.2" 7484 retry-as-promised "^3.1.0"
7517 semver "^5.5.0" 7485 semver "^5.6.0"
7518 terraformer-wkt-parser "^1.1.2" 7486 sequelize-pool "^1.0.2"
7519 toposort-class "^1.0.1" 7487 toposort-class "^1.0.1"
7520 uuid "^3.2.1" 7488 uuid "^3.2.1"
7521 validator "^10.4.0" 7489 validator "^10.11.0"
7522 wkx "^0.4.1" 7490 wkx "^0.4.6"
7523 7491
7524serve-static@1.13.2: 7492serve-static@1.13.2:
7525 version "1.13.2" 7493 version "1.13.2"
@@ -8398,21 +8366,6 @@ term-size@^1.2.0:
8398 dependencies: 8366 dependencies:
8399 execa "^0.7.0" 8367 execa "^0.7.0"
8400 8368
8401terraformer-wkt-parser@^1.1.2:
8402 version "1.2.0"
8403 resolved "https://registry.yarnpkg.com/terraformer-wkt-parser/-/terraformer-wkt-parser-1.2.0.tgz#c9d6ac3dff25f4c0bd344e961f42694961834c34"
8404 integrity sha512-QU3iA54St5lF8Za1jg1oj4NYc8sn5tCZ08aNSWDeGzrsaV48eZk1iAVWasxhNspYBoCqdHuoot1pUTUrE1AJ4w==
8405 dependencies:
8406 "@types/geojson" "^1.0.0"
8407 terraformer "~1.0.5"
8408
8409terraformer@~1.0.5:
8410 version "1.0.9"
8411 resolved "https://registry.yarnpkg.com/terraformer/-/terraformer-1.0.9.tgz#77851fef4a49c90b345dc53cf26809fdf29dcda6"
8412 integrity sha512-YlmQ1fsMWTkKGDGibCRWgmLzrpDRUr63Q025LJ/taYQ6j1Yb8q9McKF7NBi6ACAyUXO6F/bl9w6v4MY307y5Ag==
8413 optionalDependencies:
8414 "@types/geojson" "^1.0.0"
8415
8416text-hex@1.0.x: 8369text-hex@1.0.x:
8417 version "1.0.0" 8370 version "1.0.0"
8418 resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" 8371 resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5"
@@ -9002,7 +8955,7 @@ validate-npm-package-name@^3.0.0, validate-npm-package-name@~3.0.0:
9002 dependencies: 8955 dependencies:
9003 builtins "^1.0.3" 8956 builtins "^1.0.3"
9004 8957
9005validator@^10.0.0, validator@^10.2.0, validator@^10.4.0: 8958validator@^10.0.0, validator@^10.11.0, validator@^10.2.0, validator@^10.4.0:
9006 version "10.11.0" 8959 version "10.11.0"
9007 resolved "https://registry.yarnpkg.com/validator/-/validator-10.11.0.tgz#003108ea6e9a9874d31ccc9e5006856ccd76b228" 8960 resolved "https://registry.yarnpkg.com/validator/-/validator-10.11.0.tgz#003108ea6e9a9874d31ccc9e5006856ccd76b228"
9008 integrity sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw== 8961 integrity sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==
@@ -9172,7 +9125,7 @@ winston@3.2.1:
9172 triple-beam "^1.3.0" 9125 triple-beam "^1.3.0"
9173 winston-transport "^4.3.0" 9126 winston-transport "^4.3.0"
9174 9127
9175wkx@^0.4.1: 9128wkx@^0.4.6:
9176 version "0.4.6" 9129 version "0.4.6"
9177 resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.4.6.tgz#228ab592e6457382ea6fb79fc825058d07fce523" 9130 resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.4.6.tgz#228ab592e6457382ea6fb79fc825058d07fce523"
9178 integrity sha512-LHxXlzRCYQXA9ZHgs8r7Gafh0gVOE8o3QmudM1PIkOdkXXjW7Thcl+gb2P2dRuKgW8cqkitCRZkkjtmWzpHi7A== 9131 integrity sha512-LHxXlzRCYQXA9ZHgs8r7Gafh0gVOE8o3QmudM1PIkOdkXXjW7Thcl+gb2P2dRuKgW8cqkitCRZkkjtmWzpHi7A==