From 1735c825726edaa0af5035cb6cbb0cc0db502c6d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 18 Apr 2019 11:28:17 +0200 Subject: Update sequelize --- server/models/account/account-video-rate.ts | 17 ++- server/models/account/account.ts | 16 +-- server/models/account/user-notification.ts | 23 +--- server/models/account/user-video-history.ts | 2 +- server/models/account/user.ts | 18 +-- server/models/activitypub/actor-follow.ts | 39 +++--- server/models/activitypub/actor.ts | 8 +- server/models/migrations.ts | 16 +-- server/models/oauth/oauth-client.ts | 4 +- server/models/oauth/oauth-token.ts | 2 +- server/models/redundancy/video-redundancy.ts | 2 +- server/models/utils.ts | 48 ++++--- server/models/video/schedule-video-update.ts | 8 +- server/models/video/tag.ts | 8 +- server/models/video/video-abuse.ts | 2 +- server/models/video/video-blacklist.ts | 21 +-- server/models/video/video-caption.ts | 10 +- server/models/video/video-change-ownership.ts | 2 +- server/models/video/video-channel.ts | 17 ++- server/models/video/video-comment.ts | 47 ++++--- server/models/video/video-import.ts | 4 +- server/models/video/video-playlist-element.ts | 20 +-- server/models/video/video-playlist.ts | 28 ++-- server/models/video/video-streaming-playlist.ts | 18 ++- server/models/video/video.ts | 169 ++++++++++++------------ 25 files changed, 272 insertions(+), 277 deletions(-) (limited to 'server/models') 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 @@ import { values } from 'lodash' -import { Transaction, Op } from 'sequelize' +import { FindOptions, Op, Transaction } from 'sequelize' import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' -import { IFindOptions } from 'sequelize-typescript/lib/interfaces/IFindOptions' import { VideoRateType } from '../../../shared/models/videos' import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants' import { VideoModel } from '../video/video' import { AccountModel } from './account' import { ActorModel } from '../activitypub/actor' -import { throwIfNotValid, getSort } from '../utils' +import { getSort, throwIfNotValid } from '../utils' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' import { AccountVideoRate } from '../../../shared' -import { VideoChannelModel, ScopeNames as VideoChannelScopeNames } from '../video/video-channel' +import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from '../video/video-channel' /* Account rates per video. @@ -40,7 +39,7 @@ import { VideoChannelModel, ScopeNames as VideoChannelScopeNames } from '../vide export class AccountVideoRateModel extends Model { @AllowNull(false) - @Column(DataType.ENUM(values(VIDEO_RATE_TYPES))) + @Column(DataType.ENUM(...values(VIDEO_RATE_TYPES))) type: VideoRateType @AllowNull(false) @@ -79,7 +78,7 @@ export class AccountVideoRateModel extends Model { Account: AccountModel static load (accountId: number, videoId: number, transaction?: Transaction) { - const options: IFindOptions = { + const options: FindOptions = { where: { accountId, videoId @@ -97,7 +96,7 @@ export class AccountVideoRateModel extends Model { type?: string, accountId: number }) { - const query: IFindOptions = { + const query: FindOptions = { offset: options.start, limit: options.count, order: getSort(options.sort), @@ -123,7 +122,7 @@ export class AccountVideoRateModel extends Model { } static loadLocalAndPopulateVideo (rateType: VideoRateType, accountName: string, videoId: number, transaction?: Transaction) { - const options: IFindOptions = { + const options: FindOptions = { where: { videoId, type: rateType @@ -155,7 +154,7 @@ export class AccountVideoRateModel extends Model { } static loadByUrl (url: string, transaction: Transaction) { - const options: IFindOptions = { + const options: FindOptions = { where: { url } 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 @@ -import * as Sequelize from 'sequelize' import { AllowNull, BeforeDestroy, @@ -28,6 +27,7 @@ import { UserModel } from './user' import { AvatarModel } from '../avatar/avatar' import { VideoPlaylistModel } from '../video/video-playlist' import { WEBSERVER } from '../../initializers/constants' +import { Op, Transaction, WhereOptions } from 'sequelize' export enum ScopeNames { SUMMARY = 'SUMMARY' @@ -42,7 +42,7 @@ export enum ScopeNames { ] }) @Scopes({ - [ ScopeNames.SUMMARY ]: (whereActor?: Sequelize.WhereOptions) => { + [ ScopeNames.SUMMARY ]: (whereActor?: WhereOptions) => { return { attributes: [ 'id', 'name' ], include: [ @@ -90,7 +90,7 @@ export class AccountModel extends Model { @AllowNull(true) @Default(null) - @Is('AccountDescription', value => throwIfNotValid(value, isAccountDescriptionValid, 'description')) + @Is('AccountDescription', value => throwIfNotValid(value, isAccountDescriptionValid, 'description', true)) @Column description: string @@ -176,7 +176,7 @@ export class AccountModel extends Model { return undefined } - static load (id: number, transaction?: Sequelize.Transaction) { + static load (id: number, transaction?: Transaction) { return AccountModel.findByPk(id, { transaction }) } @@ -207,15 +207,15 @@ export class AccountModel extends Model { static loadLocalByName (name: string) { const query = { where: { - [ Sequelize.Op.or ]: [ + [ Op.or ]: [ { userId: { - [ Sequelize.Op.ne ]: null + [ Op.ne ]: null } }, { applicationId: { - [ Sequelize.Op.ne ]: null + [ Op.ne ]: null } } ] @@ -259,7 +259,7 @@ export class AccountModel extends Model { return AccountModel.findOne(query) } - static loadByUrl (url: string, transaction?: Sequelize.Transaction) { + static loadByUrl (url: string, transaction?: Transaction) { const query = { include: [ { 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 @@ -import { - AllowNull, - BelongsTo, - Column, - CreatedAt, - Default, - ForeignKey, - IFindOptions, - Is, - Model, - Scopes, - Table, - UpdatedAt -} from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { UserNotification, UserNotificationType } from '../../../shared' import { getSort, throwIfNotValid } from '../utils' import { isBooleanValid } from '../../helpers/custom-validators/misc' @@ -19,7 +6,7 @@ import { isUserNotificationTypeValid } from '../../helpers/custom-validators/use import { UserModel } from './user' import { VideoModel } from '../video/video' import { VideoCommentModel } from '../video/video-comment' -import { Op } from 'sequelize' +import { FindOptions, Op } from 'sequelize' import { VideoChannelModel } from '../video/video-channel' import { AccountModel } from './account' import { VideoAbuseModel } from '../video/video-abuse' @@ -160,7 +147,7 @@ function buildAccountInclude (required: boolean, withActor = false) { }, buildAccountInclude(false, true) - ] + ] as any // FIXME: sequelize typings } }) @Table({ @@ -225,7 +212,7 @@ function buildAccountInclude (required: boolean, withActor = false) { } } } - ] + ] as any // FIXME: sequelize typings }) export class UserNotificationModel extends Model { @@ -344,7 +331,7 @@ export class UserNotificationModel extends Model { ActorFollow: ActorFollowModel static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) { - const query: IFindOptions = { + const query: FindOptions = { offset: start, limit: count, 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 { } if (beforeDate) { - query.where.updatedAt = { + query.where['updatedAt'] = { [Op.lt]: beforeDate } } 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 { model: () => UserNotificationSettingModel, required: true } - ] + ] as any // FIXME: sequelize typings } }) @Table({ @@ -115,13 +115,13 @@ export class UserModel extends Model { @AllowNull(true) @Default(null) - @Is('UserEmailVerified', value => throwIfNotValid(value, isUserEmailVerifiedValid, 'email verified boolean')) + @Is('UserEmailVerified', value => throwIfNotValid(value, isUserEmailVerifiedValid, 'email verified boolean', true)) @Column emailVerified: boolean @AllowNull(false) @Is('UserNSFWPolicy', value => throwIfNotValid(value, isUserNSFWPolicyValid, 'NSFW policy')) - @Column(DataType.ENUM(values(NSFW_POLICY_TYPES))) + @Column(DataType.ENUM(...values(NSFW_POLICY_TYPES))) nsfwPolicy: NSFWPolicyType @AllowNull(false) @@ -156,7 +156,7 @@ export class UserModel extends Model { @AllowNull(true) @Default(null) - @Is('UserBlockedReason', value => throwIfNotValid(value, isUserBlockedReasonValid, 'blocked reason')) + @Is('UserBlockedReason', value => throwIfNotValid(value, isUserBlockedReasonValid, 'blocked reason', true)) @Column blockedReason: string @@ -556,10 +556,10 @@ export class UserModel extends Model { notificationSettings: this.NotificationSetting ? this.NotificationSetting.toFormattedJSON() : undefined, videoChannels: [], videoQuotaUsed: videoQuotaUsed !== undefined - ? parseInt(videoQuotaUsed, 10) + ? parseInt(videoQuotaUsed + '', 10) : undefined, videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined - ? parseInt(videoQuotaUsedDaily, 10) + ? parseInt(videoQuotaUsedDaily + '', 10) : undefined } @@ -619,14 +619,14 @@ export class UserModel extends Model { private static getTotalRawQuery (query: string, userId: number) { const options = { bind: { userId }, - type: Sequelize.QueryTypes.SELECT + type: Sequelize.QueryTypes.SELECT as Sequelize.QueryTypes.SELECT } - return UserModel.sequelize.query(query, options) + return UserModel.sequelize.query<{ total: number }>(query, options) .then(([ { total } ]) => { if (total === null) return 0 - return parseInt(total, 10) + return parseInt(total + '', 10) }) } } 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 @@ import * as Bluebird from 'bluebird' import { values } from 'lodash' -import * as Sequelize from 'sequelize' import { AfterCreate, AfterDestroy, @@ -27,8 +26,8 @@ import { ServerModel } from '../server/server' import { getSort } from '../utils' import { ActorModel, unusedActorAttributesForAPI } from './actor' import { VideoChannelModel } from '../video/video-channel' -import { IIncludeOptions } from '../../../node_modules/sequelize-typescript/lib/interfaces/IIncludeOptions' import { AccountModel } from '../account/account' +import { IncludeOptions, Op, Transaction, QueryTypes } from 'sequelize' @Table({ tableName: 'actorFollow', @@ -51,7 +50,7 @@ import { AccountModel } from '../account/account' export class ActorFollowModel extends Model { @AllowNull(false) - @Column(DataType.ENUM(values(FOLLOW_STATES))) + @Column(DataType.ENUM(...values(FOLLOW_STATES))) state: FollowState @AllowNull(false) @@ -126,7 +125,7 @@ export class ActorFollowModel extends Model { if (numberOfActorFollowsRemoved) logger.info('Removed bad %d actor follows.', numberOfActorFollowsRemoved) } - static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Sequelize.Transaction) { + static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction) { const query = { where: { actorId, @@ -150,8 +149,8 @@ export class ActorFollowModel extends Model { return ActorFollowModel.findOne(query) } - static loadByActorAndTargetNameAndHostForAPI (actorId: number, targetName: string, targetHost: string, t?: Sequelize.Transaction) { - const actorFollowingPartInclude: IIncludeOptions = { + static loadByActorAndTargetNameAndHostForAPI (actorId: number, targetName: string, targetHost: string, t?: Transaction) { + const actorFollowingPartInclude: IncludeOptions = { model: ActorModel, required: true, as: 'ActorFollowing', @@ -208,7 +207,7 @@ export class ActorFollowModel extends Model { .map(t => { if (t.host) { return { - [ Sequelize.Op.and ]: [ + [ Op.and ]: [ { '$preferredUsername$': t.name }, @@ -220,7 +219,7 @@ export class ActorFollowModel extends Model { } return { - [ Sequelize.Op.and ]: [ + [ Op.and ]: [ { '$preferredUsername$': t.name }, @@ -234,9 +233,9 @@ export class ActorFollowModel extends Model { const query = { attributes: [], where: { - [ Sequelize.Op.and ]: [ + [ Op.and ]: [ { - [ Sequelize.Op.or ]: whereTab + [ Op.or ]: whereTab }, { actorId @@ -288,7 +287,7 @@ export class ActorFollowModel extends Model { required: true, where: search ? { host: { - [Sequelize.Op.iLike]: '%' + search + '%' + [Op.iLike]: '%' + search + '%' } } : undefined } @@ -323,7 +322,7 @@ export class ActorFollowModel extends Model { required: true, where: search ? { host: { - [ Sequelize.Op.iLike ]: '%' + search + '%' + [ Op.iLike ]: '%' + search + '%' } } : undefined } @@ -406,11 +405,11 @@ export class ActorFollowModel extends Model { }) } - static listAcceptedFollowerUrlsForAP (actorIds: number[], t: Sequelize.Transaction, start?: number, count?: number) { + static listAcceptedFollowerUrlsForAP (actorIds: number[], t: Transaction, start?: number, count?: number) { return ActorFollowModel.createListAcceptedFollowForApiQuery('followers', actorIds, t, start, count) } - static listAcceptedFollowerSharedInboxUrls (actorIds: number[], t: Sequelize.Transaction) { + static listAcceptedFollowerSharedInboxUrls (actorIds: number[], t: Transaction) { return ActorFollowModel.createListAcceptedFollowForApiQuery( 'followers', actorIds, @@ -422,7 +421,7 @@ export class ActorFollowModel extends Model { ) } - static listAcceptedFollowingUrlsForApi (actorIds: number[], t: Sequelize.Transaction, start?: number, count?: number) { + static listAcceptedFollowingUrlsForApi (actorIds: number[], t: Transaction, start?: number, count?: number) { return ActorFollowModel.createListAcceptedFollowForApiQuery('following', actorIds, t, start, count) } @@ -447,7 +446,7 @@ export class ActorFollowModel extends Model { } } - static updateFollowScore (inboxUrl: string, value: number, t?: Sequelize.Transaction) { + static updateFollowScore (inboxUrl: string, value: number, t?: Transaction) { const query = `UPDATE "actorFollow" SET "score" = LEAST("score" + ${value}, ${ACTOR_FOLLOW_SCORE.MAX}) ` + 'WHERE id IN (' + 'SELECT "actorFollow"."id" FROM "actorFollow" ' + @@ -456,7 +455,7 @@ export class ActorFollowModel extends Model { ')' const options = { - type: Sequelize.QueryTypes.BULKUPDATE, + type: QueryTypes.BULKUPDATE, transaction: t } @@ -466,7 +465,7 @@ export class ActorFollowModel extends Model { private static async createListAcceptedFollowForApiQuery ( type: 'followers' | 'following', actorIds: number[], - t: Sequelize.Transaction, + t: Transaction, start?: number, count?: number, columnUrl = 'url', @@ -502,7 +501,7 @@ export class ActorFollowModel extends Model { const options = { bind: { actorIds }, - type: Sequelize.QueryTypes.SELECT, + type: QueryTypes.SELECT, transaction: t } tasks.push(ActorFollowModel.sequelize.query(query, options)) @@ -521,7 +520,7 @@ export class ActorFollowModel extends Model { const query = { where: { score: { - [Sequelize.Op.lte]: 0 + [Op.lte]: 0 } }, 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 = [ model: () => AvatarModel, required: false } - ] + ] as any // FIXME: sequelize typings } }) @Table({ @@ -131,7 +131,7 @@ export const unusedActorAttributesForAPI = [ export class ActorModel extends Model { @AllowNull(false) - @Column(DataType.ENUM(values(ACTIVITY_PUB_ACTOR_TYPES))) + @Column({ type: DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES)) }) // FIXME: sequelize typings type: ActivityPubActorType @AllowNull(false) @@ -151,12 +151,12 @@ export class ActorModel extends Model { url: string @AllowNull(true) - @Is('ActorPublicKey', value => throwIfNotValid(value, isActorPublicKeyValid, 'public key')) + @Is('ActorPublicKey', value => throwIfNotValid(value, isActorPublicKeyValid, 'public key', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.PUBLIC_KEY.max)) publicKey: string @AllowNull(true) - @Is('ActorPublicKey', value => throwIfNotValid(value, isActorPrivateKeyValid, 'private key')) + @Is('ActorPublicKey', value => throwIfNotValid(value, isActorPrivateKeyValid, 'private key', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.PRIVATE_KEY.max)) privateKey: string 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 @@ -import * as Sequelize from 'sequelize' +import { ModelAttributeColumnOptions } from 'sequelize' declare namespace Migration { - interface Boolean extends Sequelize.DefineAttributeColumnOptions { + interface Boolean extends ModelAttributeColumnOptions { defaultValue: boolean | null } - interface String extends Sequelize.DefineAttributeColumnOptions { + interface String extends ModelAttributeColumnOptions { defaultValue: string | null } - interface Integer extends Sequelize.DefineAttributeColumnOptions { + interface Integer extends ModelAttributeColumnOptions { defaultValue: number | null } - interface BigInteger extends Sequelize.DefineAttributeColumnOptions { - defaultValue: Sequelize.DataTypeBigInt | number | null + interface BigInteger extends ModelAttributeColumnOptions { + defaultValue: number | null } - interface UUID extends Sequelize.DefineAttributeColumnOptions { - defaultValue: Sequelize.DataTypeUUIDv4 | null + interface UUID extends ModelAttributeColumnOptions { + defaultValue: null } } 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 { @Column clientSecret: string - @Column(DataType.ARRAY(DataType.STRING)) + @Column({ type: DataType.ARRAY(DataType.STRING) }) // FIXME: sequelize typings grants: string[] - @Column(DataType.ARRAY(DataType.STRING)) + @Column({ type: DataType.ARRAY(DataType.STRING) }) // FIXME: sequelize typings redirectUris: string[] @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 { } ] } - ] + ] as any // FIXME: sequelize typings } }) @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 { } ] } - ] + ] as any // FIXME: sequelize typings } }) 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 @@ import { Sequelize } from 'sequelize-typescript' import * as validator from 'validator' +import { OrderItem } from 'sequelize' +import { Col } from 'sequelize/types/lib/utils' type SortType = { sortModel: any, sortValue: string } // Translate for example "-name" to [ [ 'name', 'DESC' ], [ 'id', 'ASC' ] ] -function getSort (value: string, lastSort: string[] = [ 'id', 'ASC' ]) { - let { direction, field } = buildDirectionAndField(value) +function getSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderItem[] { + const { direction, field } = buildDirectionAndField(value) + + let finalField: string | Col if (field.toLowerCase() === 'match') { // Search - field = Sequelize.col('similarity') + finalField = Sequelize.col('similarity') + } else { + finalField = field } - return [ [ field, direction ], lastSort ] + return [ [ finalField, direction ], lastSort ] } -function getVideoSort (value: string, lastSort: string[] = [ 'id', 'ASC' ]) { - let { direction, field } = buildDirectionAndField(value) +function getVideoSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderItem[] { + const { direction, field } = buildDirectionAndField(value) - // Alias - if (field.toLowerCase() === 'match') { // Search - field = Sequelize.col('similarity') - } else if (field.toLowerCase() === 'trending') { // Sort by aggregation + if (field.toLowerCase() === 'trending') { // Sort by aggregation return [ [ Sequelize.fn('COALESCE', Sequelize.fn('SUM', Sequelize.col('VideoViews.views')), '0'), direction ], @@ -30,15 +33,24 @@ function getVideoSort (value: string, lastSort: string[] = [ 'id', 'ASC' ]) { ] } - const firstSort = typeof field === 'string' ? - field.split('.').concat([ direction ]) : - [ field, direction ] + let finalField: string | Col + + // Alias + if (field.toLowerCase() === 'match') { // Search + finalField = Sequelize.col('similarity') + } else { + finalField = field + } + + const firstSort = typeof finalField === 'string' + ? finalField.split('.').concat([ direction ]) as any // FIXME: sequelize typings + : [ finalField, direction ] return [ firstSort, lastSort ] } -function getSortOnModel (model: any, value: string, lastSort: string[] = [ 'id', 'ASC' ]) { - let [ firstSort ] = getSort(value) +function getSortOnModel (model: any, value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderItem[] { + const [ firstSort ] = getSort(value) if (model) return [ [ model, firstSort[0], firstSort[1] ], lastSort ] return [ firstSort, lastSort ] @@ -52,7 +64,9 @@ function isOutdated (model: { createdAt: Date, updatedAt: Date }, refreshInterva return (now - createdAtTime) > refreshInterval && (now - updatedAtTime) > refreshInterval } -function throwIfNotValid (value: any, validator: (value: any) => boolean, fieldName = 'value') { +function throwIfNotValid (value: any, validator: (value: any) => boolean, fieldName = 'value', nullable = false) { + if (nullable && (value === null || value === undefined)) return + if (validator(value) === false) { throw new Error(`"${value}" is not a valid ${fieldName}.`) } @@ -131,7 +145,7 @@ function searchTrigramNormalizeCol (col: string) { } function buildDirectionAndField (value: string) { - let field: any + let field: string let direction: 'ASC' | 'DESC' 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 @@ -import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Sequelize, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' import { ScopeNames as VideoScopeNames, VideoModel } from './video' import { VideoPrivacy } from '../../../shared/models/videos' -import { Transaction } from 'sequelize' +import { Op, Transaction } from 'sequelize' @Table({ tableName: 'scheduleVideoUpdate', @@ -51,7 +51,7 @@ export class ScheduleVideoUpdateModel extends Model { attributes: [ 'id' ], where: { updateAt: { - [Sequelize.Op.lte]: new Date() + [Op.lte]: new Date() } } } @@ -64,7 +64,7 @@ export class ScheduleVideoUpdateModel extends Model { const query = { where: { updateAt: { - [Sequelize.Op.lte]: new Date() + [Op.lte]: new Date() } }, 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 @@ import * as Bluebird from 'bluebird' -import * as Sequelize from 'sequelize' +import { QueryTypes, Transaction } from 'sequelize' import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' import { isVideoTagValid } from '../../helpers/custom-validators/videos' import { throwIfNotValid } from '../utils' @@ -37,7 +37,7 @@ export class TagModel extends Model { }) Videos: VideoModel[] - static findOrCreateTags (tags: string[], transaction: Sequelize.Transaction) { + static findOrCreateTags (tags: string[], transaction: Transaction) { if (tags === null) return [] const tasks: Bluebird[] = [] @@ -72,10 +72,10 @@ export class TagModel extends Model { const options = { bind: { threshold, count, videoPrivacy: VideoPrivacy.PUBLIC, videoState: VideoState.PUBLISHED }, - type: Sequelize.QueryTypes.SELECT + type: QueryTypes.SELECT as QueryTypes.SELECT } - return TagModel.sequelize.query(query, options) + return TagModel.sequelize.query<{ name }>(query, options) .then(data => data.map(d => d.name)) } } 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 { @AllowNull(true) @Default(null) - @Is('VideoAbuseModerationComment', value => throwIfNotValid(value, isVideoAbuseModerationCommentValid, 'moderationComment')) + @Is('VideoAbuseModerationComment', value => throwIfNotValid(value, isVideoAbuseModerationCommentValid, 'moderationComment', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.MODERATION_COMMENT.max)) moderationComment: string 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 @@ -import { - AllowNull, - BelongsTo, - Column, - CreatedAt, - DataType, - Default, - ForeignKey, - Is, Model, - Table, - UpdatedAt, - IFindOptions -} from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' import { getSortOnModel, SortType, throwIfNotValid } from '../utils' import { VideoModel } from './video' -import { VideoChannelModel, ScopeNames as VideoChannelScopeNames } from './video-channel' +import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from './video-channel' import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' import { CONSTRAINTS_FIELDS } from '../../initializers/constants' +import { FindOptions } from 'sequelize' @Table({ tableName: 'videoBlacklist', @@ -30,7 +19,7 @@ import { CONSTRAINTS_FIELDS } from '../../initializers/constants' export class VideoBlacklistModel extends Model { @AllowNull(true) - @Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason')) + @Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_BLACKLIST.REASON.max)) reason: string @@ -63,7 +52,7 @@ export class VideoBlacklistModel extends Model { Video: VideoModel static listForApi (start: number, count: number, sort: SortType, type?: VideoBlacklistType) { - const query: IFindOptions = { + const query: FindOptions = { offset: start, limit: count, 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 @@ -import * as Sequelize from 'sequelize' +import { OrderItem, Transaction } from 'sequelize' import { AllowNull, BeforeDestroy, @@ -115,19 +115,19 @@ export class VideoCaptionModel extends Model { return VideoCaptionModel.findOne(query) } - static insertOrReplaceLanguage (videoId: number, language: string, transaction: Sequelize.Transaction) { + static insertOrReplaceLanguage (videoId: number, language: string, transaction: Transaction) { const values = { videoId, language } - return VideoCaptionModel.upsert(values, { transaction, returning: true }) + return (VideoCaptionModel.upsert(values, { transaction, returning: true }) as any) // FIXME: typings .then(([ caption ]) => caption) } static listVideoCaptions (videoId: number) { const query = { - order: [ [ 'language', 'ASC' ] ], + order: [ [ 'language', 'ASC' ] ] as OrderItem[], where: { videoId } @@ -140,7 +140,7 @@ export class VideoCaptionModel extends Model { return VIDEO_LANGUAGES[language] || 'Unknown' } - static deleteAllCaptionsOfRemoteVideo (videoId: number, transaction: Sequelize.Transaction) { + static deleteAllCaptionsOfRemoteVideo (videoId: number, transaction: Transaction) { const query = { where: { 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 { { model: () => VideoFileModel } ] } - ] + ] as any // FIXME: sequelize typings } }) export class VideoChangeOwnershipModel extends Model { 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 { DefaultScope, ForeignKey, HasMany, - IFindOptions, Is, Model, Scopes, @@ -31,12 +30,12 @@ import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttr import { VideoModel } from './video' import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' import { ServerModel } from '../server/server' -import { DefineIndexesOptions } from 'sequelize' +import { FindOptions, ModelIndexesOptions, Op } from 'sequelize' import { AvatarModel } from '../avatar/avatar' import { VideoPlaylistModel } from './video-playlist' // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation -const indexes: DefineIndexesOptions[] = [ +const indexes: ModelIndexesOptions[] = [ buildTrigramSearchIndex('video_channel_name_trigram', 'name'), { @@ -69,7 +68,7 @@ type AvailableForListOptions = { }) @Scopes({ [ScopeNames.SUMMARY]: (withAccount = false) => { - const base: IFindOptions = { + const base: FindOptions = { attributes: [ 'name', 'description', 'id', 'actorId' ], include: [ { @@ -112,13 +111,13 @@ type AvailableForListOptions = { }, model: ActorModel, where: { - [Sequelize.Op.or]: [ + [Op.or]: [ { serverId: null }, { serverId: { - [ Sequelize.Op.in ]: Sequelize.literal(inQueryInstanceFollow) + [ Op.in ]: Sequelize.literal(inQueryInstanceFollow) } } ] @@ -172,13 +171,13 @@ export class VideoChannelModel extends Model { @AllowNull(true) @Default(null) - @Is('VideoChannelDescription', value => throwIfNotValid(value, isVideoChannelDescriptionValid, 'description')) + @Is('VideoChannelDescription', value => throwIfNotValid(value, isVideoChannelDescriptionValid, 'description', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.DESCRIPTION.max)) description: string @AllowNull(true) @Default(null) - @Is('VideoChannelSupport', value => throwIfNotValid(value, isVideoChannelSupportValid, 'support')) + @Is('VideoChannelSupport', value => throwIfNotValid(value, isVideoChannelSupportValid, 'support', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.SUPPORT.max)) support: string @@ -313,7 +312,7 @@ export class VideoChannelModel extends Model { limit: options.count, order: getSort(options.sort), where: { - [Sequelize.Op.or]: [ + [Op.or]: [ Sequelize.literal( 'lower(immutable_unaccent("VideoChannelModel"."name")) % lower(immutable_unaccent(' + escapedSearch + '))' ), 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 @@ -import * as Sequelize from 'sequelize' import { AllowNull, BeforeDestroy, @@ -7,7 +6,6 @@ import { CreatedAt, DataType, ForeignKey, - IFindOptions, Is, Model, Scopes, @@ -32,6 +30,7 @@ import { UserModel } from '../account/user' import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' import { regexpCapture } from '../../helpers/regexp' import { uniq } from 'lodash' +import { FindOptions, Op, Order, Sequelize, Transaction } from 'sequelize' enum ScopeNames { WITH_ACCOUNT = 'WITH_ACCOUNT', @@ -86,7 +85,7 @@ enum ScopeNames { } ] } - ] + ] as any // FIXME: sequelize typings }, [ScopeNames.WITH_IN_REPLY_TO]: { include: [ @@ -120,7 +119,7 @@ enum ScopeNames { } ] } - ] + ] as any // FIXME: sequelize typings } }) @Table({ @@ -244,8 +243,8 @@ export class VideoCommentModel extends Model { } } - static loadById (id: number, t?: Sequelize.Transaction) { - const query: IFindOptions = { + static loadById (id: number, t?: Transaction) { + const query: FindOptions = { where: { id } @@ -256,8 +255,8 @@ export class VideoCommentModel extends Model { return VideoCommentModel.findOne(query) } - static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Sequelize.Transaction) { - const query: IFindOptions = { + static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Transaction) { + const query: FindOptions = { where: { id } @@ -270,8 +269,8 @@ export class VideoCommentModel extends Model { .findOne(query) } - static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) { - const query: IFindOptions = { + static loadByUrlAndPopulateAccount (url: string, t?: Transaction) { + const query: FindOptions = { where: { url } @@ -282,8 +281,8 @@ export class VideoCommentModel extends Model { return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT ]).findOne(query) } - static loadByUrlAndPopulateReplyAndVideo (url: string, t?: Sequelize.Transaction) { - const query: IFindOptions = { + static loadByUrlAndPopulateReplyAndVideo (url: string, t?: Transaction) { + const query: FindOptions = { where: { url } @@ -307,7 +306,7 @@ export class VideoCommentModel extends Model { videoId, inReplyToCommentId: null, accountId: { - [Sequelize.Op.notIn]: Sequelize.literal( + [Op.notIn]: Sequelize.literal( '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')' ) } @@ -336,15 +335,15 @@ export class VideoCommentModel extends Model { const userAccountId = user ? user.Account.id : undefined const query = { - order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ], + order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ] as Order, where: { videoId, - [ Sequelize.Op.or ]: [ + [ Op.or ]: [ { id: threadId }, { originCommentId: threadId } ], accountId: { - [Sequelize.Op.notIn]: Sequelize.literal( + [Op.notIn]: Sequelize.literal( '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')' ) } @@ -366,12 +365,12 @@ export class VideoCommentModel extends Model { }) } - static listThreadParentComments (comment: VideoCommentModel, t: Sequelize.Transaction, order: 'ASC' | 'DESC' = 'ASC') { + static listThreadParentComments (comment: VideoCommentModel, t: Transaction, order: 'ASC' | 'DESC' = 'ASC') { const query = { - order: [ [ 'createdAt', order ] ], + order: [ [ 'createdAt', order ] ] as Order, where: { id: { - [ Sequelize.Op.in ]: Sequelize.literal('(' + + [ Op.in ]: Sequelize.literal('(' + 'WITH RECURSIVE children (id, "inReplyToCommentId") AS ( ' + `SELECT id, "inReplyToCommentId" FROM "videoComment" WHERE id = ${comment.id} ` + 'UNION ' + @@ -380,7 +379,7 @@ export class VideoCommentModel extends Model { ') ' + 'SELECT id FROM children' + ')'), - [ Sequelize.Op.ne ]: comment.id + [ Op.ne ]: comment.id } }, transaction: t @@ -391,9 +390,9 @@ export class VideoCommentModel extends Model { .findAll(query) } - static listAndCountByVideoId (videoId: number, start: number, count: number, t?: Sequelize.Transaction, order: 'ASC' | 'DESC' = 'ASC') { + static listAndCountByVideoId (videoId: number, start: number, count: number, t?: Transaction, order: 'ASC' | 'DESC' = 'ASC') { const query = { - order: [ [ 'createdAt', order ] ], + order: [ [ 'createdAt', order ] ] as Order, offset: start, limit: count, where: { @@ -407,7 +406,7 @@ export class VideoCommentModel extends Model { static listForFeed (start: number, count: number, videoId?: number) { const query = { - order: [ [ 'createdAt', 'DESC' ] ], + order: [ [ 'createdAt', 'DESC' ] ] as Order, offset: start, limit: count, where: {}, @@ -457,7 +456,7 @@ export class VideoCommentModel extends Model { const query = { where: { updatedAt: { - [Sequelize.Op.lt]: beforeUpdatedAt + [Op.lt]: beforeUpdatedAt }, videoId } 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 { @AllowNull(true) @Default(null) - @Is('VideoImportTargetUrl', value => throwIfNotValid(value, isVideoImportTargetUrlValid, 'targetUrl')) + @Is('VideoImportTargetUrl', value => throwIfNotValid(value, isVideoImportTargetUrlValid, 'targetUrl', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.URL.max)) targetUrl: string @AllowNull(true) @Default(null) - @Is('VideoImportMagnetUri', value => throwIfNotValid(value, isVideoMagnetUriValid, 'magnetUri')) + @Is('VideoImportMagnetUri', value => throwIfNotValid(value, isVideoMagnetUriValid, 'magnetUri', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.URL.max)) // Use the same constraints than URLs magnetUri: string 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 { } from 'sequelize-typescript' import { VideoModel } from './video' import { VideoPlaylistModel } from './video-playlist' -import * as Sequelize from 'sequelize' import { getSort, throwIfNotValid } from '../utils' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' import { CONSTRAINTS_FIELDS } from '../../initializers/constants' import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object' import * as validator from 'validator' +import { AggregateOptions, Op, Sequelize, Transaction } from 'sequelize' @Table({ tableName: 'videoPlaylistElement', @@ -96,7 +96,7 @@ export class VideoPlaylistElementModel extends Model }) Video: VideoModel - static deleteAllOf (videoPlaylistId: number, transaction?: Sequelize.Transaction) { + static deleteAllOf (videoPlaylistId: number, transaction?: Transaction) { const query = { where: { videoPlaylistId @@ -140,7 +140,7 @@ export class VideoPlaylistElementModel extends Model return VideoPlaylistElementModel.findOne(query) } - static listUrlsOfForAP (videoPlaylistId: number, start: number, count: number, t?: Sequelize.Transaction) { + static listUrlsOfForAP (videoPlaylistId: number, start: number, count: number, t?: Transaction) { const query = { attributes: [ 'url' ], offset: start, @@ -159,8 +159,8 @@ export class VideoPlaylistElementModel extends Model }) } - static getNextPositionOf (videoPlaylistId: number, transaction?: Sequelize.Transaction) { - const query = { + static getNextPositionOf (videoPlaylistId: number, transaction?: Transaction) { + const query: AggregateOptions = { where: { videoPlaylistId }, @@ -176,14 +176,14 @@ export class VideoPlaylistElementModel extends Model firstPosition: number, endPosition: number, newPosition: number, - transaction?: Sequelize.Transaction + transaction?: Transaction ) { const query = { where: { videoPlaylistId, position: { - [Sequelize.Op.gte]: firstPosition, - [Sequelize.Op.lte]: endPosition + [Op.gte]: firstPosition, + [Op.lte]: endPosition } }, transaction, @@ -198,13 +198,13 @@ export class VideoPlaylistElementModel extends Model fromPosition: number, toPosition?: number, by = 1, - transaction?: Sequelize.Transaction + transaction?: Transaction ) { const query = { where: { videoPlaylistId, position: { - [Sequelize.Op.gte]: fromPosition + [Op.gte]: fromPosition } }, 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 { Table, UpdatedAt } from 'sequelize-typescript' -import * as Sequelize from 'sequelize' import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' import { buildServerIdsFollowedBy, buildWhereIdOrUUID, getSort, isOutdated, throwIfNotValid } from '../utils' import { @@ -43,6 +42,7 @@ import { activityPubCollectionPagination } from '../../helpers/activitypub' import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video-playlist-type.model' import { ThumbnailModel } from './thumbnail' import { ActivityIconObject } from '../../../shared/models/activitypub/objects' +import { fn, literal, Op, Transaction } from 'sequelize' enum ScopeNames { AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', @@ -74,7 +74,11 @@ type AvailableForListOptions = { attributes: { include: [ [ - Sequelize.literal('(SELECT COUNT("id") FROM "videoPlaylistElement" WHERE "videoPlaylistId" = "VideoPlaylistModel"."id")'), + fn('COUNT', 'toto'), + 'coucou' + ], + [ + literal('(SELECT COUNT("id") FROM "videoPlaylistElement" WHERE "videoPlaylistId" = "VideoPlaylistModel"."id")'), 'videosLength' ] ] @@ -116,13 +120,13 @@ type AvailableForListOptions = { // Only list local playlists OR playlists that are on an instance followed by actorId const inQueryInstanceFollow = buildServerIdsFollowedBy(options.followerActorId) const actorWhere = { - [ Sequelize.Op.or ]: [ + [ Op.or ]: [ { serverId: null }, { serverId: { - [ Sequelize.Op.in ]: Sequelize.literal(inQueryInstanceFollow) + [ Op.in ]: literal(inQueryInstanceFollow) } } ] @@ -155,7 +159,7 @@ type AvailableForListOptions = { } const where = { - [Sequelize.Op.and]: whereAnd + [Op.and]: whereAnd } const accountScope = { @@ -206,7 +210,7 @@ export class VideoPlaylistModel extends Model { name: string @AllowNull(true) - @Is('VideoPlaylistDescription', value => throwIfNotValid(value, isVideoPlaylistDescriptionValid, 'description')) + @Is('VideoPlaylistDescription', value => throwIfNotValid(value, isVideoPlaylistDescriptionValid, 'description', true)) @Column description: string @@ -344,7 +348,7 @@ export class VideoPlaylistModel extends Model { model: VideoPlaylistElementModel.unscoped(), where: { videoId: { - [Sequelize.Op.any]: videoIds + [Op.any]: videoIds } }, required: true @@ -368,7 +372,7 @@ export class VideoPlaylistModel extends Model { .then(e => !!e) } - static loadWithAccountAndChannelSummary (id: number | string, transaction: Sequelize.Transaction) { + static loadWithAccountAndChannelSummary (id: number | string, transaction: Transaction) { const where = buildWhereIdOrUUID(id) const query = { @@ -381,7 +385,7 @@ export class VideoPlaylistModel extends Model { .findOne(query) } - static loadWithAccountAndChannel (id: number | string, transaction: Sequelize.Transaction) { + static loadWithAccountAndChannel (id: number | string, transaction: Transaction) { const where = buildWhereIdOrUUID(id) const query = { @@ -412,7 +416,7 @@ export class VideoPlaylistModel extends Model { return VIDEO_PLAYLIST_TYPES[type] || 'Unknown' } - static resetPlaylistsOfChannel (videoChannelId: number, transaction: Sequelize.Transaction) { + static resetPlaylistsOfChannel (videoChannelId: number, transaction: Transaction) { const query = { where: { videoChannelId @@ -489,7 +493,7 @@ export class VideoPlaylistModel extends Model { label: VideoPlaylistModel.getTypeLabel(this.type) }, - videosLength: this.get('videosLength'), + videosLength: this.get('videosLength') as number, createdAt: this.createdAt, updatedAt: this.updatedAt, @@ -499,7 +503,7 @@ export class VideoPlaylistModel extends Model { } } - toActivityPubObject (page: number, t: Sequelize.Transaction): Promise { + toActivityPubObject (page: number, t: Transaction): Promise { const handler = (start: number, count: number) => { return VideoPlaylistElementModel.listUrlsOfForAP(this.id, start, count, t) } 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 @@ -import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, HasMany, Is, Model, Table, UpdatedAt, DataType } from 'sequelize-typescript' import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' import { throwIfNotValid } from '../utils' import { VideoModel } from './video' -import * as Sequelize from 'sequelize' import { VideoRedundancyModel } from '../redundancy/video-redundancy' import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' @@ -11,6 +10,7 @@ import { VideoFileModel } from './video-file' import { join } from 'path' import { sha1 } from '../../helpers/core-utils' import { isArrayOf } from '../../helpers/custom-validators/misc' +import { QueryTypes, Op } from 'sequelize' @Table({ tableName: 'videoStreamingPlaylist', @@ -26,7 +26,7 @@ import { isArrayOf } from '../../helpers/custom-validators/misc' fields: [ 'p2pMediaLoaderInfohashes' ], using: 'gin' } - ] + ] as any // FIXME: sequelize typings }) export class VideoStreamingPlaylistModel extends Model { @CreatedAt @@ -46,7 +46,7 @@ export class VideoStreamingPlaylistModel extends Model throwIfNotValid(value, v => isArrayOf(v, isVideoFileInfoHashValid), 'info hashes')) - @Column(DataType.ARRAY(DataType.STRING)) + @Column({ type: DataType.ARRAY(DataType.STRING) }) // FIXME: typings p2pMediaLoaderInfohashes: string[] @AllowNull(false) @@ -82,15 +82,13 @@ export class VideoStreamingPlaylistModel extends Model { - return results.length === 1 - }) + return VideoModel.sequelize.query(query, options) + .then(results => results.length === 1) } static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: VideoFileModel[]) { @@ -108,7 +106,7 @@ export class VideoStreamingPlaylistModel extends Model { - const query: IFindOptions = { + const query: FindOptions = { where: { id: { - [ Sequelize.Op.any ]: options.ids + [ Op.in ]: options.ids // FIXME: sequelize any seems broken } }, include: [ @@ -256,21 +263,21 @@ type AvailableForListIDsOptions = { return query }, [ ScopeNames.AVAILABLE_FOR_LIST_IDS ]: (options: AvailableForListIDsOptions) => { - const query: IFindOptions = { + const query: FindOptions = { raw: true, attributes: [ 'id' ], where: { id: { - [ Sequelize.Op.and ]: [ + [ Op.and ]: [ { - [ Sequelize.Op.notIn ]: Sequelize.literal( + [ Op.notIn ]: Sequelize.literal( '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")' ) } ] }, channelId: { - [ Sequelize.Op.notIn ]: Sequelize.literal( + [ Op.notIn ]: Sequelize.literal( '(' + 'SELECT id FROM "videoChannel" WHERE "accountId" IN (' + buildBlockedAccountSQL(options.serverAccountId, options.user ? options.user.Account.id : undefined) + @@ -288,12 +295,12 @@ type AvailableForListIDsOptions = { // Always list public videos privacy: VideoPrivacy.PUBLIC, // Always list published videos, or videos that are being transcoded but on which we don't want to wait for transcoding - [ Sequelize.Op.or ]: [ + [ Op.or ]: [ { state: VideoState.PUBLISHED }, { - [ Sequelize.Op.and ]: { + [ Op.and ]: { state: VideoState.TO_TRANSCODE, waitTranscoding: false } @@ -318,7 +325,7 @@ type AvailableForListIDsOptions = { } if (options.filter || options.accountId || options.videoChannelId) { - const videoChannelInclude: IIncludeOptions = { + const videoChannelInclude: IncludeOptions = { attributes: [], model: VideoChannelModel.unscoped(), required: true @@ -331,7 +338,7 @@ type AvailableForListIDsOptions = { } if (options.filter || options.accountId) { - const accountInclude: IIncludeOptions = { + const accountInclude: IncludeOptions = { attributes: [], model: AccountModel.unscoped(), required: true @@ -371,8 +378,8 @@ type AvailableForListIDsOptions = { // Force actorId to be a number to avoid SQL injections const actorIdNumber = parseInt(options.followerActorId.toString(), 10) - query.where[ 'id' ][ Sequelize.Op.and ].push({ - [ Sequelize.Op.in ]: Sequelize.literal( + query.where[ 'id' ][ Op.and ].push({ + [ Op.in ]: Sequelize.literal( '(' + 'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' + 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' + @@ -391,8 +398,8 @@ type AvailableForListIDsOptions = { } if (options.withFiles === true) { - query.where[ 'id' ][ Sequelize.Op.and ].push({ - [ Sequelize.Op.in ]: Sequelize.literal( + query.where[ 'id' ][ Op.and ].push({ + [ Op.in ]: Sequelize.literal( '(SELECT "videoId" FROM "videoFile")' ) }) @@ -406,8 +413,8 @@ type AvailableForListIDsOptions = { } if (options.tagsOneOf) { - query.where[ 'id' ][ Sequelize.Op.and ].push({ - [ Sequelize.Op.in ]: Sequelize.literal( + query.where[ 'id' ][ Op.and ].push({ + [ Op.in ]: Sequelize.literal( '(' + 'SELECT "videoId" FROM "videoTag" ' + 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + @@ -418,8 +425,8 @@ type AvailableForListIDsOptions = { } if (options.tagsAllOf) { - query.where[ 'id' ][ Sequelize.Op.and ].push({ - [ Sequelize.Op.in ]: Sequelize.literal( + query.where[ 'id' ][ Op.and ].push({ + [ Op.in ]: Sequelize.literal( '(' + 'SELECT "videoId" FROM "videoTag" ' + 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + @@ -437,19 +444,19 @@ type AvailableForListIDsOptions = { if (options.categoryOneOf) { query.where[ 'category' ] = { - [ Sequelize.Op.or ]: options.categoryOneOf + [ Op.or ]: options.categoryOneOf } } if (options.licenceOneOf) { query.where[ 'licence' ] = { - [ Sequelize.Op.or ]: options.licenceOneOf + [ Op.or ]: options.licenceOneOf } } if (options.languageOneOf) { query.where[ 'language' ] = { - [ Sequelize.Op.or ]: options.languageOneOf + [ Op.or ]: options.languageOneOf } } @@ -498,7 +505,7 @@ type AvailableForListIDsOptions = { } ] } - ] + ] as any // FIXME: sequelize typings }, [ ScopeNames.WITH_ACCOUNT_DETAILS ]: { include: [ @@ -550,7 +557,7 @@ type AvailableForListIDsOptions = { } ] } - ] + ] as any // FIXME: sequelize typings }, [ ScopeNames.WITH_TAGS ]: { include: [ () => TagModel ] @@ -656,19 +663,19 @@ export class VideoModel extends Model { @AllowNull(true) @Default(null) - @Is('VideoCategory', value => throwIfNotValid(value, isVideoCategoryValid, 'category')) + @Is('VideoCategory', value => throwIfNotValid(value, isVideoCategoryValid, 'category', true)) @Column category: number @AllowNull(true) @Default(null) - @Is('VideoLicence', value => throwIfNotValid(value, isVideoLicenceValid, 'licence')) + @Is('VideoLicence', value => throwIfNotValid(value, isVideoLicenceValid, 'licence', true)) @Column licence: number @AllowNull(true) @Default(null) - @Is('VideoLanguage', value => throwIfNotValid(value, isVideoLanguageValid, 'language')) + @Is('VideoLanguage', value => throwIfNotValid(value, isVideoLanguageValid, 'language', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.LANGUAGE.max)) language: string @@ -684,13 +691,13 @@ export class VideoModel extends Model { @AllowNull(true) @Default(null) - @Is('VideoDescription', value => throwIfNotValid(value, isVideoDescriptionValid, 'description')) + @Is('VideoDescription', value => throwIfNotValid(value, isVideoDescriptionValid, 'description', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max)) description: string @AllowNull(true) @Default(null) - @Is('VideoSupport', value => throwIfNotValid(value, isVideoSupportValid, 'support')) + @Is('VideoSupport', value => throwIfNotValid(value, isVideoSupportValid, 'support', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.SUPPORT.max)) support: string @@ -754,7 +761,7 @@ export class VideoModel extends Model { updatedAt: Date @AllowNull(false) - @Default(Sequelize.NOW) + @Default(DataType.NOW) @Column publishedAt: Date @@ -999,12 +1006,12 @@ export class VideoModel extends Model { distinct: true, offset: start, limit: count, - order: getVideoSort('createdAt', [ 'Tags', 'name', 'ASC' ]), + order: getVideoSort('createdAt', [ 'Tags', 'name', 'ASC' ] as any), // FIXME: sequelize typings where: { id: { - [ Sequelize.Op.in ]: Sequelize.literal('(' + rawQuery + ')') + [ Op.in ]: Sequelize.literal('(' + rawQuery + ')') }, - [ Sequelize.Op.or ]: [ + [ Op.or ]: [ { privacy: VideoPrivacy.PUBLIC }, { privacy: VideoPrivacy.UNLISTED } ] @@ -1021,10 +1028,10 @@ export class VideoModel extends Model { required: false, // We only want videos shared by this actor where: { - [ Sequelize.Op.and ]: [ + [ Op.and ]: [ { id: { - [ Sequelize.Op.not ]: null + [ Op.not ]: null } }, { @@ -1070,13 +1077,13 @@ export class VideoModel extends Model { return Bluebird.all([ // FIXME: typing issue VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findAll(query as any), - VideoModel.sequelize.query(rawCountQuery, { type: Sequelize.QueryTypes.SELECT }) + VideoModel.sequelize.query<{ total: number }>(rawCountQuery, { type: QueryTypes.SELECT }) ]).then(([ rows, totals ]) => { // totals: totalVideos + totalVideoShares let totalVideos = 0 let totalVideoShares = 0 - if (totals[ 0 ]) totalVideos = parseInt(totals[ 0 ].total, 10) - if (totals[ 1 ]) totalVideoShares = parseInt(totals[ 1 ].total, 10) + if (totals[ 0 ]) totalVideos = parseInt(totals[ 0 ].total + '', 10) + if (totals[ 1 ]) totalVideoShares = parseInt(totals[ 1 ].total + '', 10) const total = totalVideos + totalVideoShares return { @@ -1087,7 +1094,7 @@ export class VideoModel extends Model { } static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) { - const query: IFindOptions = { + const query: FindOptions = { offset: start, limit: count, order: getVideoSort(sort), @@ -1158,7 +1165,7 @@ export class VideoModel extends Model { throw new Error('Try to filter all-local but no user has not the see all videos right') } - const query: IFindOptions = { + const query: FindOptions = { offset: options.start, limit: options.count, order: getVideoSort(options.sort) @@ -1225,8 +1232,8 @@ export class VideoModel extends Model { if (options.startDate || options.endDate) { const publishedAtRange = {} - if (options.startDate) publishedAtRange[ Sequelize.Op.gte ] = options.startDate - if (options.endDate) publishedAtRange[ Sequelize.Op.lte ] = options.endDate + if (options.startDate) publishedAtRange[ Op.gte ] = options.startDate + if (options.endDate) publishedAtRange[ Op.lte ] = options.endDate whereAnd.push({ publishedAt: publishedAtRange }) } @@ -1234,8 +1241,8 @@ export class VideoModel extends Model { if (options.originallyPublishedStartDate || options.originallyPublishedEndDate) { const originallyPublishedAtRange = {} - if (options.originallyPublishedStartDate) originallyPublishedAtRange[ Sequelize.Op.gte ] = options.originallyPublishedStartDate - if (options.originallyPublishedEndDate) originallyPublishedAtRange[ Sequelize.Op.lte ] = options.originallyPublishedEndDate + if (options.originallyPublishedStartDate) originallyPublishedAtRange[ Op.gte ] = options.originallyPublishedStartDate + if (options.originallyPublishedEndDate) originallyPublishedAtRange[ Op.lte ] = options.originallyPublishedEndDate whereAnd.push({ originallyPublishedAt: originallyPublishedAtRange }) } @@ -1243,8 +1250,8 @@ export class VideoModel extends Model { if (options.durationMin || options.durationMax) { const durationRange = {} - if (options.durationMin) durationRange[ Sequelize.Op.gte ] = options.durationMin - if (options.durationMax) durationRange[ Sequelize.Op.lte ] = options.durationMax + if (options.durationMin) durationRange[ Op.gte ] = options.durationMin + if (options.durationMax) durationRange[ Op.lte ] = options.durationMax whereAnd.push({ duration: durationRange }) } @@ -1256,7 +1263,7 @@ export class VideoModel extends Model { whereAnd.push( { id: { - [ Sequelize.Op.in ]: Sequelize.literal( + [ Op.in ]: Sequelize.literal( '(' + 'SELECT "video"."id" FROM "video" ' + 'WHERE ' + @@ -1282,7 +1289,7 @@ export class VideoModel extends Model { ) } - const query: IFindOptions = { + const query: FindOptions = { attributes: { include: attributesInclude }, @@ -1290,7 +1297,7 @@ export class VideoModel extends Model { limit: options.count, order: getVideoSort(options.sort), where: { - [ Sequelize.Op.and ]: whereAnd + [ Op.and ]: whereAnd } } @@ -1312,7 +1319,7 @@ export class VideoModel extends Model { return VideoModel.getAvailableForApi(query, queryOptions) } - static load (id: number | string, t?: Sequelize.Transaction) { + static load (id: number | string, t?: Transaction) { const where = buildWhereIdOrUUID(id) const options = { where, @@ -1322,7 +1329,7 @@ export class VideoModel extends Model { return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) } - static loadWithRights (id: number | string, t?: Sequelize.Transaction) { + static loadWithRights (id: number | string, t?: Transaction) { const where = buildWhereIdOrUUID(id) const options = { where, @@ -1336,7 +1343,7 @@ export class VideoModel extends Model { ]).findOne(options) } - static loadOnlyId (id: number | string, t?: Sequelize.Transaction) { + static loadOnlyId (id: number | string, t?: Transaction) { const where = buildWhereIdOrUUID(id) const options = { @@ -1348,7 +1355,7 @@ export class VideoModel extends Model { return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) } - static loadWithFiles (id: number, t?: Sequelize.Transaction, logging?: boolean) { + static loadWithFiles (id: number, t?: Transaction, logging?: boolean) { return VideoModel.scope([ ScopeNames.WITH_FILES, ScopeNames.WITH_STREAMING_PLAYLISTS, @@ -1366,8 +1373,8 @@ export class VideoModel extends Model { return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) } - static loadByUrl (url: string, transaction?: Sequelize.Transaction) { - const query: IFindOptions = { + static loadByUrl (url: string, transaction?: Transaction) { + const query: FindOptions = { where: { url }, @@ -1377,8 +1384,8 @@ export class VideoModel extends Model { return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query) } - static loadByUrlAndPopulateAccount (url: string, transaction?: Sequelize.Transaction) { - const query: IFindOptions = { + static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction) { + const query: FindOptions = { where: { url }, @@ -1393,11 +1400,11 @@ export class VideoModel extends Model { ]).findOne(query) } - static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Sequelize.Transaction, userId?: number) { + static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number) { const where = buildWhereIdOrUUID(id) const options = { - order: [ [ 'Tags', 'name', 'ASC' ] ], + order: [ [ 'Tags', 'name', 'ASC' ] ] as any, // FIXME: sequelize typings where, transaction: t } @@ -1421,11 +1428,11 @@ export class VideoModel extends Model { .findOne(options) } - static loadForGetAPI (id: number | string, t?: Sequelize.Transaction, userId?: number) { + static loadForGetAPI (id: number | string, t?: Transaction, userId?: number) { const where = buildWhereIdOrUUID(id) const options = { - order: [ [ 'Tags', 'name', 'ASC' ] ], + order: [ [ 'Tags', 'name', 'ASC' ] ] as any, // FIXME: sequelize typings where, transaction: t } @@ -1489,7 +1496,7 @@ export class VideoModel extends Model { 'LIMIT 1' const options = { - type: Sequelize.QueryTypes.SELECT, + type: QueryTypes.SELECT, bind: { followerActorId, videoId }, raw: true } @@ -1509,14 +1516,14 @@ export class VideoModel extends Model { includeLocalVideos: true } - const query: IFindOptions = { + const query: FindOptions = { attributes: [ field ], limit: count, group: field, having: Sequelize.where(Sequelize.fn('COUNT', Sequelize.col(field)), { - [ Sequelize.Op.gte ]: threshold + [ Op.gte ]: threshold }) as any, // FIXME: typings - order: [ this.sequelize.random() ] + order: [ (this.sequelize as any).random() ] } return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST_IDS, scopeOptions ] }) @@ -1532,7 +1539,7 @@ export class VideoModel extends Model { required: false, where: { startDate: { - [ Sequelize.Op.gte ]: new Date(new Date().getTime() - (24 * 3600 * 1000) * trendingDays) + [ Op.gte ]: new Date(new Date().getTime() - (24 * 3600 * 1000) * trendingDays) } } } @@ -1549,11 +1556,11 @@ export class VideoModel extends Model { } private static async getAvailableForApi ( - query: IFindOptions, + query: FindOptions, options: AvailableForListIDsOptions, countVideos = true ) { - const idsScope = { + const idsScope: ScopeOptions = { method: [ ScopeNames.AVAILABLE_FOR_LIST_IDS, options ] @@ -1561,8 +1568,8 @@ export class VideoModel extends Model { // Remove trending sort on count, because it uses a group by const countOptions = Object.assign({}, options, { trendingDays: undefined }) - const countQuery = Object.assign({}, query, { attributes: undefined, group: undefined }) - const countScope = { + const countQuery: CountOptions = Object.assign({}, query, { attributes: undefined, group: undefined }) + const countScope: ScopeOptions = { method: [ ScopeNames.AVAILABLE_FOR_LIST_IDS, countOptions ] @@ -1576,7 +1583,7 @@ export class VideoModel extends Model { if (ids.length === 0) return { data: [], total: count } - const secondQuery: IFindOptions = { + const secondQuery: FindOptions = { offset: 0, limit: query.limit, attributes: query.attributes, -- cgit v1.2.3