diff options
Diffstat (limited to 'server')
43 files changed, 332 insertions, 318 deletions
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' | |||
15 | import { JobQueue } from '../../../lib/job-queue/job-queue' | 15 | import { JobQueue } from '../../../lib/job-queue/job-queue' |
16 | import { join } from 'path' | 16 | import { join } from 'path' |
17 | import { isArray } from '../../../helpers/custom-validators/misc' | 17 | import { isArray } from '../../../helpers/custom-validators/misc' |
18 | import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model' | ||
19 | import { VideoChannelModel } from '../../../models/video/video-channel' | 18 | import { VideoChannelModel } from '../../../models/video/video-channel' |
20 | import * as Bluebird from 'bluebird' | 19 | import * as Bluebird from 'bluebird' |
21 | import * as parseTorrent from 'parse-torrent' | 20 | import * 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 | ||
69 | function resetSequelizeInstance (instance: Model<any>, savedFields: object) { | 69 | function 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 | |||
37 | import { VideoPlaylistModel } from '../models/video/video-playlist' | 37 | import { VideoPlaylistModel } from '../models/video/video-playlist' |
38 | import { VideoPlaylistElementModel } from '../models/video/video-playlist-element' | 38 | import { VideoPlaylistElementModel } from '../models/video/video-playlist-element' |
39 | import { ThumbnailModel } from '../models/video/thumbnail' | 39 | import { ThumbnailModel } from '../models/video/thumbnail' |
40 | import { QueryTypes, Transaction } from 'sequelize' | ||
40 | 41 | ||
41 | require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string | 42 | require('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 | ||
142 | async function checkPostgresExtension (extension: string) { | 142 | async 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' | |||
3 | import { LAST_MIGRATION_VERSION } from './constants' | 3 | import { LAST_MIGRATION_VERSION } from './constants' |
4 | import { sequelizeTypescript } from './database' | 4 | import { sequelizeTypescript } from './database' |
5 | import { readdir } from 'fs-extra' | 5 | import { readdir } from 'fs-extra' |
6 | import { QueryTypes } from 'sequelize' | ||
6 | 7 | ||
7 | async function migrate () { | 8 | async 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' | |||
14 | import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist' | 14 | import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist' |
15 | import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element' | 15 | import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element' |
16 | import { VideoModel } from '../../models/video/video' | 16 | import { VideoModel } from '../../models/video/video' |
17 | import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model' | ||
18 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' | 17 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' |
19 | import { sequelizeTypescript } from '../../initializers/database' | 18 | import { sequelizeTypescript } from '../../initializers/database' |
20 | import { createPlaylistThumbnailFromUrl } from '../thumbnail' | 19 | import { 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 | ||
158 | async function resetVideoPlaylistElements (elementUrls: string[], playlist: VideoPlaylistModel) { | 158 | async 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' |
12 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' | 12 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' |
@@ -45,7 +45,6 @@ import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' | |||
45 | import { Notifier } from '../notifier' | 45 | import { Notifier } from '../notifier' |
46 | import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' | 46 | import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' |
47 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' | 47 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' |
48 | import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model' | ||
49 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' | 48 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' |
50 | import { VideoShareModel } from '../../models/video/video-share' | 49 | import { VideoShareModel } from '../../models/video/video-share' |
51 | import { VideoCommentModel } from '../../models/video/video-comment' | 50 | import { 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' | |||
7 | import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' | 7 | import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' |
8 | import { createVideoChannel } from './video-channel' | 8 | import { createVideoChannel } from './video-channel' |
9 | import { VideoChannelModel } from '../models/video/video-channel' | 9 | import { VideoChannelModel } from '../models/video/video-channel' |
10 | import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model' | ||
11 | import { ActorModel } from '../models/activitypub/actor' | 10 | import { ActorModel } from '../models/activitypub/actor' |
12 | import { UserNotificationSettingModel } from '../models/account/user-notification-setting' | 11 | import { UserNotificationSettingModel } from '../models/account/user-notification-setting' |
13 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' | 12 | import { 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 @@ | |||
1 | import { values } from 'lodash' | 1 | import { values } from 'lodash' |
2 | import { Transaction, Op } from 'sequelize' | 2 | import { FindOptions, Op, Transaction } from 'sequelize' |
3 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 3 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
4 | import { IFindOptions } from 'sequelize-typescript/lib/interfaces/IFindOptions' | ||
5 | import { VideoRateType } from '../../../shared/models/videos' | 4 | import { VideoRateType } from '../../../shared/models/videos' |
6 | import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants' | 5 | import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants' |
7 | import { VideoModel } from '../video/video' | 6 | import { VideoModel } from '../video/video' |
8 | import { AccountModel } from './account' | 7 | import { AccountModel } from './account' |
9 | import { ActorModel } from '../activitypub/actor' | 8 | import { ActorModel } from '../activitypub/actor' |
10 | import { throwIfNotValid, getSort } from '../utils' | 9 | import { getSort, throwIfNotValid } from '../utils' |
11 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 10 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
12 | import { AccountVideoRate } from '../../../shared' | 11 | import { AccountVideoRate } from '../../../shared' |
13 | import { VideoChannelModel, ScopeNames as VideoChannelScopeNames } from '../video/video-channel' | 12 | import { 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 | |||
40 | export class AccountVideoRateModel extends Model<AccountVideoRateModel> { | 39 | export 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 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import { | 1 | import { |
3 | AllowNull, | 2 | AllowNull, |
4 | BeforeDestroy, | 3 | BeforeDestroy, |
@@ -28,6 +27,7 @@ import { UserModel } from './user' | |||
28 | import { AvatarModel } from '../avatar/avatar' | 27 | import { AvatarModel } from '../avatar/avatar' |
29 | import { VideoPlaylistModel } from '../video/video-playlist' | 28 | import { VideoPlaylistModel } from '../video/video-playlist' |
30 | import { WEBSERVER } from '../../initializers/constants' | 29 | import { WEBSERVER } from '../../initializers/constants' |
30 | import { Op, Transaction, WhereOptions } from 'sequelize' | ||
31 | 31 | ||
32 | export enum ScopeNames { | 32 | export 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 @@ | |||
1 | import { | 1 | import { 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' | ||
15 | import { UserNotification, UserNotificationType } from '../../../shared' | 2 | import { UserNotification, UserNotificationType } from '../../../shared' |
16 | import { getSort, throwIfNotValid } from '../utils' | 3 | import { getSort, throwIfNotValid } from '../utils' |
17 | import { isBooleanValid } from '../../helpers/custom-validators/misc' | 4 | import { isBooleanValid } from '../../helpers/custom-validators/misc' |
@@ -19,7 +6,7 @@ import { isUserNotificationTypeValid } from '../../helpers/custom-validators/use | |||
19 | import { UserModel } from './user' | 6 | import { UserModel } from './user' |
20 | import { VideoModel } from '../video/video' | 7 | import { VideoModel } from '../video/video' |
21 | import { VideoCommentModel } from '../video/video-comment' | 8 | import { VideoCommentModel } from '../video/video-comment' |
22 | import { Op } from 'sequelize' | 9 | import { FindOptions, Op } from 'sequelize' |
23 | import { VideoChannelModel } from '../video/video-channel' | 10 | import { VideoChannelModel } from '../video/video-channel' |
24 | import { AccountModel } from './account' | 11 | import { AccountModel } from './account' |
25 | import { VideoAbuseModel } from '../video/video-abuse' | 12 | import { 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 | }) |
230 | export class UserNotificationModel extends Model<UserNotificationModel> { | 217 | export 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 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { values } from 'lodash' | 2 | import { values } from 'lodash' |
3 | import * as Sequelize from 'sequelize' | ||
4 | import { | 3 | import { |
5 | AfterCreate, | 4 | AfterCreate, |
6 | AfterDestroy, | 5 | AfterDestroy, |
@@ -27,8 +26,8 @@ import { ServerModel } from '../server/server' | |||
27 | import { getSort } from '../utils' | 26 | import { getSort } from '../utils' |
28 | import { ActorModel, unusedActorAttributesForAPI } from './actor' | 27 | import { ActorModel, unusedActorAttributesForAPI } from './actor' |
29 | import { VideoChannelModel } from '../video/video-channel' | 28 | import { VideoChannelModel } from '../video/video-channel' |
30 | import { IIncludeOptions } from '../../../node_modules/sequelize-typescript/lib/interfaces/IIncludeOptions' | ||
31 | import { AccountModel } from '../account/account' | 29 | import { AccountModel } from '../account/account' |
30 | import { 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' | |||
51 | export class ActorFollowModel extends Model<ActorFollowModel> { | 50 | export 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 = [ | |||
131 | export class ActorModel extends Model<ActorModel> { | 131 | export 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 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import { ModelAttributeColumnOptions } from 'sequelize' |
2 | 2 | ||
3 | declare namespace Migration { | 3 | declare 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 @@ | |||
1 | import { Sequelize } from 'sequelize-typescript' | 1 | import { Sequelize } from 'sequelize-typescript' |
2 | import * as validator from 'validator' | 2 | import * as validator from 'validator' |
3 | import { OrderItem } from 'sequelize' | ||
4 | import { Col } from 'sequelize/types/lib/utils' | ||
3 | 5 | ||
4 | type SortType = { sortModel: any, sortValue: string } | 6 | type 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' ] ] |
7 | function getSort (value: string, lastSort: string[] = [ 'id', 'ASC' ]) { | 9 | function 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 | ||
17 | function getVideoSort (value: string, lastSort: string[] = [ 'id', 'ASC' ]) { | 23 | function 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 | ||
40 | function getSortOnModel (model: any, value: string, lastSort: string[] = [ 'id', 'ASC' ]) { | 52 | function 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 | ||
55 | function throwIfNotValid (value: any, validator: (value: any) => boolean, fieldName = 'value') { | 67 | function 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 | ||
133 | function buildDirectionAndField (value: string) { | 147 | function 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 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Sequelize, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { ScopeNames as VideoScopeNames, VideoModel } from './video' | 2 | import { ScopeNames as VideoScopeNames, VideoModel } from './video' |
3 | import { VideoPrivacy } from '../../../shared/models/videos' | 3 | import { VideoPrivacy } from '../../../shared/models/videos' |
4 | import { Transaction } from 'sequelize' | 4 | import { 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 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import * as Sequelize from 'sequelize' | 2 | import { QueryTypes, Transaction } from 'sequelize' |
3 | import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 3 | import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
4 | import { isVideoTagValid } from '../../helpers/custom-validators/videos' | 4 | import { isVideoTagValid } from '../../helpers/custom-validators/videos' |
5 | import { throwIfNotValid } from '../utils' | 5 | import { 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 @@ | |||
1 | import { | 1 | import { 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' | ||
14 | import { getSortOnModel, SortType, throwIfNotValid } from '../utils' | 2 | import { getSortOnModel, SortType, throwIfNotValid } from '../utils' |
15 | import { VideoModel } from './video' | 3 | import { VideoModel } from './video' |
16 | import { VideoChannelModel, ScopeNames as VideoChannelScopeNames } from './video-channel' | 4 | import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from './video-channel' |
17 | import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' | 5 | import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' |
18 | import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' | 6 | import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' |
19 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' | 7 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' |
8 | import { 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' | |||
30 | export class VideoBlacklistModel extends Model<VideoBlacklistModel> { | 19 | export 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 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import { OrderItem, Transaction } from 'sequelize' |
2 | import { | 2 | import { |
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 | }) |
49 | export class VideoChangeOwnershipModel extends Model<VideoChangeOwnershipModel> { | 49 | export 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 | |||
31 | import { VideoModel } from './video' | 30 | import { VideoModel } from './video' |
32 | import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' | 31 | import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' |
33 | import { ServerModel } from '../server/server' | 32 | import { ServerModel } from '../server/server' |
34 | import { DefineIndexesOptions } from 'sequelize' | 33 | import { FindOptions, ModelIndexesOptions, Op } from 'sequelize' |
35 | import { AvatarModel } from '../avatar/avatar' | 34 | import { AvatarModel } from '../avatar/avatar' |
36 | import { VideoPlaylistModel } from './video-playlist' | 35 | import { 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 |
39 | const indexes: DefineIndexesOptions[] = [ | 38 | const 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 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import { | 1 | import { |
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' | |||
32 | import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' | 30 | import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' |
33 | import { regexpCapture } from '../../helpers/regexp' | 31 | import { regexpCapture } from '../../helpers/regexp' |
34 | import { uniq } from 'lodash' | 32 | import { uniq } from 'lodash' |
33 | import { FindOptions, Op, Order, Sequelize, Transaction } from 'sequelize' | ||
35 | 34 | ||
36 | enum ScopeNames { | 35 | enum 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' |
16 | import { VideoModel } from './video' | 16 | import { VideoModel } from './video' |
17 | import { VideoPlaylistModel } from './video-playlist' | 17 | import { VideoPlaylistModel } from './video-playlist' |
18 | import * as Sequelize from 'sequelize' | ||
19 | import { getSort, throwIfNotValid } from '../utils' | 18 | import { getSort, throwIfNotValid } from '../utils' |
20 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 19 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
21 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' | 20 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' |
22 | import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object' | 21 | import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object' |
23 | import * as validator from 'validator' | 22 | import * as validator from 'validator' |
23 | import { 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' |
18 | import * as Sequelize from 'sequelize' | ||
19 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' | 18 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' |
20 | import { buildServerIdsFollowedBy, buildWhereIdOrUUID, getSort, isOutdated, throwIfNotValid } from '../utils' | 19 | import { buildServerIdsFollowedBy, buildWhereIdOrUUID, getSort, isOutdated, throwIfNotValid } from '../utils' |
21 | import { | 20 | import { |
@@ -43,6 +42,7 @@ import { activityPubCollectionPagination } from '../../helpers/activitypub' | |||
43 | import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video-playlist-type.model' | 42 | import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video-playlist-type.model' |
44 | import { ThumbnailModel } from './thumbnail' | 43 | import { ThumbnailModel } from './thumbnail' |
45 | import { ActivityIconObject } from '../../../shared/models/activitypub/objects' | 44 | import { ActivityIconObject } from '../../../shared/models/activitypub/objects' |
45 | import { fn, literal, Op, Transaction } from 'sequelize' | ||
46 | 46 | ||
47 | enum ScopeNames { | 47 | enum 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 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, HasMany, Is, Model, Table, UpdatedAt, DataType } from 'sequelize-typescript' |
2 | import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' | 2 | import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' |
3 | import { throwIfNotValid } from '../utils' | 3 | import { throwIfNotValid } from '../utils' |
4 | import { VideoModel } from './video' | 4 | import { VideoModel } from './video' |
5 | import * as Sequelize from 'sequelize' | ||
6 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | 5 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' |
7 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' | 6 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' |
8 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 7 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
@@ -11,6 +10,7 @@ import { VideoFileModel } from './video-file' | |||
11 | import { join } from 'path' | 10 | import { join } from 'path' |
12 | import { sha1 } from '../../helpers/core-utils' | 11 | import { sha1 } from '../../helpers/core-utils' |
13 | import { isArrayOf } from '../../helpers/custom-validators/misc' | 12 | import { isArrayOf } from '../../helpers/custom-validators/misc' |
13 | import { 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 | }) |
31 | export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistModel> { | 31 | export 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' | |||
3 | import * as magnetUtil from 'magnet-uri' | 3 | import * as magnetUtil from 'magnet-uri' |
4 | import * as parseTorrent from 'parse-torrent' | 4 | import * as parseTorrent from 'parse-torrent' |
5 | import { join } from 'path' | 5 | import { join } from 'path' |
6 | import * as Sequelize from 'sequelize' | 6 | import { |
7 | CountOptions, | ||
8 | FindOptions, | ||
9 | IncludeOptions, | ||
10 | ModelIndexesOptions, | ||
11 | Op, | ||
12 | QueryTypes, | ||
13 | ScopeOptions, | ||
14 | Sequelize, | ||
15 | Transaction, | ||
16 | WhereOptions | ||
17 | } from 'sequelize' | ||
7 | import { | 18 | import { |
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' |
48 | import { generateImageFromVideoFile, getVideoFileResolution } from '../../helpers/ffmpeg-utils' | 57 | import { getVideoFileResolution } from '../../helpers/ffmpeg-utils' |
49 | import { logger } from '../../helpers/logger' | 58 | import { logger } from '../../helpers/logger' |
50 | import { getServerActor } from '../../helpers/utils' | 59 | import { getServerActor } from '../../helpers/utils' |
51 | import { | 60 | import { |
@@ -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' | |||
111 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' | 118 | import { 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 |
114 | const indexes: Sequelize.DefineIndexesOptions[] = [ | 121 | const 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, |