Stricter models typing

This commit is contained in:
Chocobozzz 2021-05-12 14:09:04 +02:00
parent 9a320a06b6
commit 16c016e8b1
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
54 changed files with 148 additions and 95 deletions

View File

@ -3,7 +3,6 @@ import * as express from 'express'
function activityPubResponse (data: any, res: express.Response) {
return res.type('application/activity+json; charset=utf-8')
.json(data)
.end()
}
export {

View File

@ -323,14 +323,20 @@ async function updateUser (req: express.Request, res: express.Response) {
const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON())
const roleChanged = body.role !== undefined && body.role !== userToUpdate.role
if (body.password !== undefined) userToUpdate.password = body.password
if (body.email !== undefined) userToUpdate.email = body.email
if (body.emailVerified !== undefined) userToUpdate.emailVerified = body.emailVerified
if (body.videoQuota !== undefined) userToUpdate.videoQuota = body.videoQuota
if (body.videoQuotaDaily !== undefined) userToUpdate.videoQuotaDaily = body.videoQuotaDaily
if (body.role !== undefined) userToUpdate.role = body.role
if (body.adminFlags !== undefined) userToUpdate.adminFlags = body.adminFlags
if (body.pluginAuth !== undefined) userToUpdate.pluginAuth = body.pluginAuth
const keysToUpdate: (keyof UserUpdate)[] = [
'password',
'email',
'emailVerified',
'videoQuota',
'videoQuotaDaily',
'role',
'adminFlags',
'pluginAuth'
]
for (const key of keysToUpdate) {
if (body[key] !== undefined) userToUpdate.set(key, body[key])
}
const user = await userToUpdate.save()

View File

@ -5,6 +5,7 @@ import * as parseTorrent from 'parse-torrent'
import { join } from 'path'
import { getEnabledResolutions } from '@server/lib/config'
import { setVideoTags } from '@server/lib/video'
import { FilteredModelAttributes } from '@server/types'
import {
MChannelAccountDefault,
MThumbnail,
@ -15,7 +16,7 @@ import {
MVideoThumbnail,
MVideoWithBlacklistLight
} from '@server/types/models'
import { MVideoImport, MVideoImportFormattable } from '@server/types/models/video/video-import'
import { MVideoImportFormattable } from '@server/types/models/video/video-import'
import { ServerErrorCode, VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '../../../../shared'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
@ -253,7 +254,9 @@ function buildVideo (channelId: number, body: VideoImportCreate, importData: You
privacy: body.privacy || VideoPrivacy.PRIVATE,
duration: 0, // duration will be set by the import job
channelId: channelId,
originallyPublishedAt: body.originallyPublishedAt || importData.originallyPublishedAt
originallyPublishedAt: body.originallyPublishedAt
? new Date(body.originallyPublishedAt)
: importData.originallyPublishedAt
}
const video = new VideoModel(videoData)
video.url = getLocalVideoActivityPubUrl(video)
@ -317,7 +320,7 @@ async function insertIntoDB (parameters: {
previewModel: MThumbnail
videoChannel: MChannelAccountDefault
tags: string[]
videoImportAttributes: Partial<MVideoImport>
videoImportAttributes: FilteredModelAttributes<VideoImportModel>
user: MUser
}): Promise<MVideoImportFormattable> {
const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters

View File

@ -308,7 +308,7 @@ async function addVideo (options: {
if (videoInfo.scheduleUpdate) {
await ScheduleVideoUpdateModel.create({
videoId: video.id,
updateAt: videoInfo.scheduleUpdate.updateAt,
updateAt: new Date(videoInfo.scheduleUpdate.updateAt),
privacy: videoInfo.scheduleUpdate.privacy || null
}, { transaction: t })
}
@ -435,7 +435,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
if (videoInfoToUpdate.scheduleUpdate) {
await ScheduleVideoUpdateModel.upsert({
videoId: videoInstanceUpdated.id,
updateAt: videoInfoToUpdate.scheduleUpdate.updateAt,
updateAt: new Date(videoInfoToUpdate.scheduleUpdate.updateAt),
privacy: videoInfoToUpdate.scheduleUpdate.privacy || null
}, { transaction: t })
} else if (videoInfoToUpdate.scheduleUpdate === null) {

View File

@ -68,7 +68,7 @@ function transactionRetryer <T> (func: (err: any, data: T) => any) {
})
}
function updateInstanceWithAnother <T extends Model<T>> (instanceToUpdate: Model<T>, baseInstance: Model<T>) {
function updateInstanceWithAnother <M, T extends U, U extends Model<M>> (instanceToUpdate: T, baseInstance: U) {
const obj = baseInstance.toJSON()
for (const key of Object.keys(obj)) {
@ -88,7 +88,7 @@ function afterCommitIfTransaction (t: Transaction, fn: Function) {
return fn()
}
function deleteNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean } & Model<T>> (
function deleteNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean } & Pick<Model, 'destroy'>> (
fromDatabase: T[],
newModels: T[],
t: Transaction

View File

@ -132,12 +132,11 @@ async function getOrCreateActorAndServerAndModel (
return actorRefreshed
}
function buildActorInstance (type: ActivityPubActorType, url: string, preferredUsername: string, uuid?: string) {
function buildActorInstance (type: ActivityPubActorType, url: string, preferredUsername: string) {
return new ActorModel({
type,
url,
preferredUsername,
uuid,
publicKey: null,
privateKey: null,
followersCount: 0,

View File

@ -36,8 +36,8 @@ async function processVideosViews () {
}
await VideoViewModel.create({
startDate,
endDate,
startDate: new Date(startDate),
endDate: new Date(endDate),
views,
videoId
})

View File

@ -1,5 +1,4 @@
import * as Sequelize from 'sequelize'
import { v4 as uuidv4 } from 'uuid'
import { VideoChannelCreate } from '../../shared/models'
import { VideoModel } from '../models/video/video'
import { VideoChannelModel } from '../models/video/video-channel'
@ -9,9 +8,8 @@ import { getLocalVideoChannelActivityPubUrl } from './activitypub/url'
import { federateVideoIfNeeded } from './activitypub/videos'
async function createLocalVideoChannel (videoChannelInfo: VideoChannelCreate, account: MAccountId, t: Sequelize.Transaction) {
const uuid = uuidv4()
const url = getLocalVideoChannelActivityPubUrl(videoChannelInfo.name)
const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid)
const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name)
const actorInstanceCreated = await actorInstance.save({ transaction: t })

View File

@ -28,6 +28,8 @@ function buildLocalVideoFromReq (videoInfo: VideoCreate, channelId: number): Fil
privacy: videoInfo.privacy || VideoPrivacy.PRIVATE,
channelId: channelId,
originallyPublishedAt: videoInfo.originallyPublishedAt
? new Date(videoInfo.originallyPublishedAt)
: null
}
}

View File

@ -1,6 +1,7 @@
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { isAbuseMessageValid } from '@server/helpers/custom-validators/abuses'
import { MAbuseMessage, MAbuseMessageFormattable } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { AbuseMessage } from '@shared/models'
import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account'
import { getSort, throwIfNotValid } from '../utils'
@ -17,7 +18,7 @@ import { AbuseModel } from './abuse'
}
]
})
export class AbuseMessageModel extends Model {
export class AbuseMessageModel extends Model<Partial<AttributesOnly<AbuseMessageModel>>> {
@AllowNull(false)
@Is('AbuseMessage', value => throwIfNotValid(value, isAbuseMessageValid, 'message'))

View File

@ -16,6 +16,7 @@ import {
UpdatedAt
} from 'sequelize-typescript'
import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses'
import { AttributesOnly } from '@shared/core-utils'
import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
import {
AbuseFilter,
@ -187,7 +188,7 @@ export enum ScopeNames {
}
]
})
export class AbuseModel extends Model {
export class AbuseModel extends Model<Partial<AttributesOnly<AbuseModel>>> {
@AllowNull(false)
@Default(null)

View File

@ -1,4 +1,5 @@
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { AttributesOnly } from '@shared/core-utils'
import { VideoDetails } from '@shared/models'
import { VideoModel } from '../video/video'
import { AbuseModel } from './abuse'
@ -14,7 +15,7 @@ import { AbuseModel } from './abuse'
}
]
})
export class VideoAbuseModel extends Model {
export class VideoAbuseModel extends Model<Partial<AttributesOnly<VideoAbuseModel>>> {
@CreatedAt
createdAt: Date

View File

@ -1,4 +1,5 @@
import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { AttributesOnly } from '@shared/core-utils'
import { VideoCommentModel } from '../video/video-comment'
import { AbuseModel } from './abuse'
@ -13,7 +14,7 @@ import { AbuseModel } from './abuse'
}
]
})
export class VideoCommentAbuseModel extends Model {
export class VideoCommentAbuseModel extends Model<Partial<AttributesOnly<VideoCommentAbuseModel>>> {
@CreatedAt
createdAt: Date

View File

@ -1,6 +1,7 @@
import { Op } from 'sequelize'
import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { AccountBlock } from '../../../shared/models'
import { ActorModel } from '../actor/actor'
import { ServerModel } from '../server/server'
@ -40,7 +41,7 @@ enum ScopeNames {
}
]
})
export class AccountBlocklistModel extends Model {
export class AccountBlocklistModel extends Model<Partial<AttributesOnly<AccountBlocklistModel>>> {
@CreatedAt
createdAt: Date

View File

@ -7,6 +7,7 @@ import {
MAccountVideoRateAccountVideo,
MAccountVideoRateFormattable
} from '@server/types/models/video/video-rate'
import { AttributesOnly } from '@shared/core-utils'
import { AccountVideoRate } from '../../../shared'
import { VideoRateType } from '../../../shared/models/videos'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
@ -42,7 +43,7 @@ import { AccountModel } from './account'
}
]
})
export class AccountVideoRateModel extends Model {
export class AccountVideoRateModel extends Model<Partial<AttributesOnly<AccountVideoRateModel>>> {
@AllowNull(false)
@Column(DataType.ENUM(...values(VIDEO_RATE_TYPES)))

View File

@ -17,6 +17,7 @@ import {
UpdatedAt
} from 'sequelize-typescript'
import { ModelCache } from '@server/models/model-cache'
import { AttributesOnly } from '@shared/core-utils'
import { Account, AccountSummary } from '../../../shared/models/actors'
import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts'
import { CONSTRAINTS_FIELDS, SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants'
@ -141,7 +142,7 @@ export type SummaryOptions = {
}
]
})
export class AccountModel extends Model {
export class AccountModel extends Model<Partial<AttributesOnly<AccountModel>>> {
@AllowNull(false)
@Column

View File

@ -28,6 +28,7 @@ import {
MActorFollowFormattable,
MActorFollowSubscriptions
} from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { ActivityPubActorType } from '@shared/models'
import { FollowState } from '../../../shared/models/actors'
import { ActorFollow } from '../../../shared/models/actors/follow.model'
@ -61,7 +62,7 @@ import { ActorModel, unusedActorAttributesForAPI } from './actor'
}
]
})
export class ActorFollowModel extends Model {
export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowModel>>> {
@AllowNull(false)
@Column(DataType.ENUM(...values(FOLLOW_STATES)))
@ -619,7 +620,7 @@ export class ActorFollowModel extends Model {
if (serverIds.length === 0) return
const me = await getServerActor()
const serverIdsString = createSafeIn(ActorFollowModel, serverIds)
const serverIdsString = createSafeIn(ActorFollowModel.sequelize, serverIds)
const query = `UPDATE "actorFollow" SET "score" = LEAST("score" + ${value}, ${ACTOR_FOLLOW_SCORE.MAX}) ` +
'WHERE id IN (' +

View File

@ -2,6 +2,7 @@ import { remove } from 'fs-extra'
import { join } from 'path'
import { AfterDestroy, AllowNull, Column, CreatedAt, Default, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { MActorImageFormattable } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { ActorImageType } from '@shared/models'
import { ActorImage } from '../../../shared/models/actors/actor-image.model'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
@ -19,7 +20,7 @@ import { throwIfNotValid } from '../utils'
}
]
})
export class ActorImageModel extends Model {
export class ActorImageModel extends Model<Partial<AttributesOnly<ActorImageModel>>> {
@AllowNull(false)
@Column

View File

@ -18,6 +18,7 @@ import {
UpdatedAt
} from 'sequelize-typescript'
import { ModelCache } from '@server/models/model-cache'
import { AttributesOnly } from '@shared/core-utils'
import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub'
import { ActorImage } from '../../../shared/models/actors/actor-image.model'
import { activityPubContextify } from '../../helpers/activitypub'
@ -159,7 +160,7 @@ export const unusedActorAttributesForAPI = [
}
]
})
export class ActorModel extends Model {
export class ActorModel extends Model<Partial<AttributesOnly<ActorModel>>> {
@AllowNull(false)
@Column(DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES)))

View File

@ -1,6 +1,7 @@
import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript'
import { AccountModel } from '../account/account'
import * as memoizee from 'memoizee'
import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript'
import { AttributesOnly } from '@shared/core-utils'
import { AccountModel } from '../account/account'
export const getServerActor = memoizee(async function () {
const application = await ApplicationModel.load()
@ -24,7 +25,7 @@ export const getServerActor = memoizee(async function () {
tableName: 'application',
timestamps: false
})
export class ApplicationModel extends Model {
export class ApplicationModel extends Model<Partial<AttributesOnly<ApplicationModel>>> {
@AllowNull(false)
@Default(0)

View File

@ -1,4 +1,5 @@
import { AllowNull, Column, CreatedAt, DataType, HasMany, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { AttributesOnly } from '@shared/core-utils'
import { OAuthTokenModel } from './oauth-token'
@Table({
@ -14,7 +15,7 @@ import { OAuthTokenModel } from './oauth-token'
}
]
})
export class OAuthClientModel extends Model {
export class OAuthClientModel extends Model<Partial<AttributesOnly<OAuthClientModel>>> {
@AllowNull(false)
@Column

View File

@ -15,6 +15,7 @@ import {
import { TokensCache } from '@server/lib/auth/tokens-cache'
import { MUserAccountId } from '@server/types/models'
import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token'
import { AttributesOnly } from '@shared/core-utils'
import { logger } from '../../helpers/logger'
import { AccountModel } from '../account/account'
import { ActorModel } from '../actor/actor'
@ -78,7 +79,7 @@ enum ScopeNames {
}
]
})
export class OAuthTokenModel extends Model {
export class OAuthTokenModel extends Model<Partial<AttributesOnly<OAuthTokenModel>>> {
@AllowNull(false)
@Column

View File

@ -16,6 +16,7 @@ import {
} from 'sequelize-typescript'
import { getServerActor } from '@server/models/application/application'
import { MActor, MVideoForRedundancyAPI, MVideoRedundancy, MVideoRedundancyAP, MVideoRedundancyVideo } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { VideoRedundanciesTarget } from '@shared/models/redundancy/video-redundancies-filters.model'
import {
FileRedundancyInformation,
@ -84,7 +85,7 @@ export enum ScopeNames {
}
]
})
export class VideoRedundancyModel extends Model {
export class VideoRedundancyModel extends Model<Partial<AttributesOnly<VideoRedundancyModel>>> {
@CreatedAt
createdAt: Date

View File

@ -1,6 +1,7 @@
import { FindAndCountOptions, json, QueryTypes } from 'sequelize'
import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { MPlugin, MPluginFormattable } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { PeerTubePlugin, PluginType, RegisterServerSettingOptions } from '../../../shared/models'
import {
isPluginDescriptionValid,
@ -26,7 +27,7 @@ import { getSort, throwIfNotValid } from '../utils'
}
]
})
export class PluginModel extends Model {
export class PluginModel extends Model<Partial<AttributesOnly<PluginModel>>> {
@AllowNull(false)
@Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name'))

View File

@ -1,6 +1,7 @@
import { Op } from 'sequelize'
import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { MServerBlocklist, MServerBlocklistAccountServer, MServerBlocklistFormattable } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { ServerBlock } from '@shared/models'
import { AccountModel } from '../account/account'
import { getSort, searchAttribute } from '../utils'
@ -42,7 +43,7 @@ enum ScopeNames {
}
]
})
export class ServerBlocklistModel extends Model {
export class ServerBlocklistModel extends Model<Partial<AttributesOnly<ServerBlocklistModel>>> {
@CreatedAt
createdAt: Date

View File

@ -1,5 +1,6 @@
import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { MServer, MServerFormattable } from '@server/types/models/server'
import { AttributesOnly } from '@shared/core-utils'
import { isHostValid } from '../../helpers/custom-validators/servers'
import { ActorModel } from '../actor/actor'
import { throwIfNotValid } from '../utils'
@ -14,7 +15,7 @@ import { ServerBlocklistModel } from './server-blocklist'
}
]
})
export class ServerModel extends Model {
export class ServerModel extends Model<Partial<AttributesOnly<ServerModel>>> {
@AllowNull(false)
@Is('Host', value => throwIfNotValid(value, isHostValid, 'valid host'))

View File

@ -1,6 +1,7 @@
import { AllowNull, BelongsToMany, Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { Transaction } from 'sequelize/types'
import { MTracker } from '@server/types/models/server/tracker'
import { AttributesOnly } from '@shared/core-utils'
import { VideoModel } from '../video/video'
import { VideoTrackerModel } from './video-tracker'
@ -13,7 +14,7 @@ import { VideoTrackerModel } from './video-tracker'
}
]
})
export class TrackerModel extends Model {
export class TrackerModel extends Model<Partial<AttributesOnly<TrackerModel>>> {
@AllowNull(false)
@Column

View File

@ -1,4 +1,5 @@
import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { AttributesOnly } from '@shared/core-utils'
import { VideoModel } from '../video/video'
import { TrackerModel } from './tracker'
@ -13,7 +14,7 @@ import { TrackerModel } from './tracker'
}
]
})
export class VideoTrackerModel extends Model {
export class VideoTrackerModel extends Model<Partial<AttributesOnly<VideoTrackerModel>>> {
@CreatedAt
createdAt: Date

View File

@ -14,6 +14,7 @@ import {
} from 'sequelize-typescript'
import { TokensCache } from '@server/lib/auth/tokens-cache'
import { MNotificationSettingFormattable } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { UserNotificationSetting, UserNotificationSettingValue } from '../../../shared/models/users/user-notification-setting.model'
import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications'
import { throwIfNotValid } from '../utils'
@ -28,7 +29,7 @@ import { UserModel } from './user'
}
]
})
export class UserNotificationSettingModel extends Model {
export class UserNotificationSettingModel extends Model<Partial<AttributesOnly<UserNotificationSettingModel>>> {
@AllowNull(false)
@Default(null)

View File

@ -1,6 +1,7 @@
import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize'
import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user'
import { AttributesOnly } from '@shared/core-utils'
import { UserNotification, UserNotificationType } from '../../../shared'
import { isBooleanValid } from '../../helpers/custom-validators/misc'
import { isUserNotificationTypeValid } from '../../helpers/custom-validators/user-notifications'
@ -286,7 +287,7 @@ function buildAccountInclude (required: boolean, withActor = false) {
}
] as (ModelIndexesOptions & { where?: WhereOptions })[]
})
export class UserNotificationModel extends Model {
export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNotificationModel>>> {
@AllowNull(false)
@Default(null)

View File

@ -1,8 +1,9 @@
import { DestroyOptions, Op, Transaction } from 'sequelize'
import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { MUserAccountId, MUserId } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { VideoModel } from '../video/video'
import { UserModel } from './user'
import { DestroyOptions, Op, Transaction } from 'sequelize'
import { MUserAccountId, MUserId } from '@server/types/models'
@Table({
tableName: 'userVideoHistory',
@ -19,7 +20,7 @@ import { MUserAccountId, MUserId } from '@server/types/models'
}
]
})
export class UserVideoHistoryModel extends Model {
export class UserVideoHistoryModel extends Model<Partial<AttributesOnly<UserVideoHistoryModel>>> {
@CreatedAt
createdAt: Date

View File

@ -31,6 +31,7 @@ import {
MUserWithNotificationSetting,
MVideoWithRights
} from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users'
import { AbuseState, MyUser, UserRight, VideoPlaylistType, VideoPrivacy } from '../../../shared/models'
import { User, UserRole } from '../../../shared/models/users'
@ -233,7 +234,7 @@ enum ScopeNames {
}
]
})
export class UserModel extends Model {
export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
@AllowNull(true)
@Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password', true))

View File

@ -1,5 +1,4 @@
import { literal, Op, OrderItem } from 'sequelize'
import { Model, Sequelize } from 'sequelize-typescript'
import { literal, Op, OrderItem, Sequelize } from 'sequelize'
import { Col } from 'sequelize/types/lib/utils'
import validator from 'validator'
@ -195,11 +194,11 @@ function parseAggregateResult (result: any) {
return total
}
const createSafeIn = (model: typeof Model, stringArr: (string | number)[]) => {
function createSafeIn (sequelize: Sequelize, stringArr: (string | number)[]) {
return stringArr.map(t => {
return t === null
? null
: model.sequelize.escape('' + t)
: sequelize.escape('' + t)
}).join(', ')
}

View File

@ -1,8 +1,9 @@
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 { Op, Transaction } from 'sequelize'
import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { MScheduleVideoUpdateFormattable, MScheduleVideoUpdateVideoAll } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { VideoPrivacy } from '../../../shared/models/videos'
import { ScopeNames as VideoScopeNames, VideoModel } from './video'
@Table({
tableName: 'scheduleVideoUpdate',
@ -16,7 +17,7 @@ import { MScheduleVideoUpdateFormattable, MScheduleVideoUpdateVideoAll } from '@
}
]
})
export class ScheduleVideoUpdateModel extends Model {
export class ScheduleVideoUpdateModel extends Model<Partial<AttributesOnly<ScheduleVideoUpdateModel>>> {
@AllowNull(false)
@Default(null)

View File

@ -1,6 +1,7 @@
import { col, fn, QueryTypes, Transaction } from 'sequelize'
import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { MTag } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { VideoPrivacy, VideoState } from '../../../shared/models/videos'
import { isVideoTagValid } from '../../helpers/custom-validators/videos'
import { throwIfNotValid } from '../utils'
@ -21,7 +22,7 @@ import { VideoTagModel } from './video-tag'
}
]
})
export class TagModel extends Model {
export class TagModel extends Model<Partial<AttributesOnly<TagModel>>> {
@AllowNull(false)
@Is('VideoTag', value => throwIfNotValid(value, isVideoTagValid, 'tag'))

View File

@ -17,6 +17,7 @@ import {
} from 'sequelize-typescript'
import { afterCommitIfTransaction } from '@server/helpers/database-utils'
import { MThumbnail, MThumbnailVideo, MVideo } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
import { logger } from '../../helpers/logger'
import { CONFIG } from '../../initializers/config'
@ -40,7 +41,7 @@ import { VideoPlaylistModel } from './video-playlist'
}
]
})
export class ThumbnailModel extends Model {
export class ThumbnailModel extends Model<Partial<AttributesOnly<ThumbnailModel>>> {
@AllowNull(false)
@Column

View File

@ -1,6 +1,7 @@
import { FindOptions } from 'sequelize'
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { MVideoBlacklist, MVideoBlacklistFormattable } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos'
import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist'
import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
@ -18,7 +19,7 @@ import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel
}
]
})
export class VideoBlacklistModel extends Model {
export class VideoBlacklistModel extends Model<Partial<AttributesOnly<VideoBlacklistModel>>> {
@AllowNull(true)
@Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason', true))

View File

@ -17,6 +17,7 @@ import {
} from 'sequelize-typescript'
import { v4 as uuidv4 } from 'uuid'
import { MVideo, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model'
import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions'
import { logger } from '../../helpers/logger'
@ -57,7 +58,7 @@ export enum ScopeNames {
}
]
})
export class VideoCaptionModel extends Model {
export class VideoCaptionModel extends Model<Partial<AttributesOnly<VideoCaptionModel>>> {
@CreatedAt
createdAt: Date

View File

@ -1,5 +1,6 @@
import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { MVideoChangeOwnershipFormattable, MVideoChangeOwnershipFull } from '@server/types/models/video/video-change-ownership'
import { AttributesOnly } from '@shared/core-utils'
import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos'
import { AccountModel } from '../account/account'
import { getSort } from '../utils'
@ -53,7 +54,7 @@ enum ScopeNames {
]
}
}))
export class VideoChangeOwnershipModel extends Model {
export class VideoChangeOwnershipModel extends Model<Partial<AttributesOnly<VideoChangeOwnershipModel>>> {
@CreatedAt
createdAt: Date

View File

@ -19,6 +19,7 @@ import {
} from 'sequelize-typescript'
import { setAsUpdated } from '@server/helpers/database-utils'
import { MAccountActor } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { ActivityPubActor } from '../../../shared/models/activitypub'
import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos'
import {
@ -246,7 +247,7 @@ export type SummaryOptions = {
}
]
})
export class VideoChannelModel extends Model {
export class VideoChannelModel extends Model<Partial<AttributesOnly<VideoChannelModel>>> {
@AllowNull(false)
@Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelNameValid, 'name'))

View File

@ -16,6 +16,7 @@ import {
} from 'sequelize-typescript'
import { getServerActor } from '@server/models/application/application'
import { MAccount, MAccountId, MUserAccountId } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { VideoPrivacy } from '@shared/models'
import { ActivityTagObject, ActivityTombstoneObject } from '../../../shared/models/activitypub/objects/common-objects'
import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object'
@ -173,7 +174,7 @@ export enum ScopeNames {
}
]
})
export class VideoCommentModel extends Model {
export class VideoCommentModel extends Model<Partial<AttributesOnly<VideoCommentModel>>> {
@CreatedAt
createdAt: Date

View File

@ -25,6 +25,7 @@ import { logger } from '@server/helpers/logger'
import { extractVideo } from '@server/helpers/video'
import { getTorrentFilePath } from '@server/lib/video-paths'
import { MStreamingPlaylistVideo, MVideo, MVideoWithHost } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import {
isVideoFileExtnameValid,
isVideoFileInfoHashValid,
@ -149,7 +150,7 @@ export enum ScopeNames {
}
]
})
export class VideoFileModel extends Model {
export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>>> {
@CreatedAt
createdAt: Date

View File

@ -15,6 +15,7 @@ import {
} from 'sequelize-typescript'
import { afterCommitIfTransaction } from '@server/helpers/database-utils'
import { MVideoImportDefault, MVideoImportFormattable } from '@server/types/models/video/video-import'
import { AttributesOnly } from '@shared/core-utils'
import { VideoImport, VideoImportState } from '../../../shared'
import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports'
import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos'
@ -52,7 +53,7 @@ import { ScopeNames as VideoModelScopeNames, VideoModel } from './video'
}
]
})
export class VideoImportModel extends Model {
export class VideoImportModel extends Model<Partial<AttributesOnly<VideoImportModel>>> {
@CreatedAt
createdAt: Date

View File

@ -1,6 +1,7 @@
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, DefaultScope, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { WEBSERVER } from '@server/initializers/constants'
import { MVideoLive, MVideoLiveVideo } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { LiveVideo, VideoState } from '@shared/models'
import { VideoModel } from './video'
import { VideoBlacklistModel } from './video-blacklist'
@ -28,7 +29,7 @@ import { VideoBlacklistModel } from './video-blacklist'
}
]
})
export class VideoLiveModel extends Model {
export class VideoLiveModel extends Model<Partial<AttributesOnly<VideoLiveModel>>> {
@AllowNull(true)
@Column(DataType.STRING)

View File

@ -32,6 +32,7 @@ import { AccountModel } from '../account/account'
import { getSort, throwIfNotValid } from '../utils'
import { ForAPIOptions, ScopeNames as VideoScopeNames, VideoModel } from './video'
import { VideoPlaylistModel } from './video-playlist'
import { AttributesOnly } from '@shared/core-utils'
@Table({
tableName: 'videoPlaylistElement',
@ -48,7 +49,7 @@ import { VideoPlaylistModel } from './video-playlist'
}
]
})
export class VideoPlaylistElementModel extends Model {
export class VideoPlaylistElementModel extends Model<Partial<AttributesOnly<VideoPlaylistElementModel>>> {
@CreatedAt
createdAt: Date
@ -274,7 +275,8 @@ export class VideoPlaylistElementModel extends Model {
validate: false // We use a literal to update the position
}
return VideoPlaylistElementModel.update({ position: Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`) }, query)
const positionQuery = Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`)
return VideoPlaylistElementModel.update({ position: positionQuery as any }, query)
}
static increasePositionOf (

View File

@ -19,6 +19,7 @@ import {
} from 'sequelize-typescript'
import { v4 as uuidv4 } from 'uuid'
import { MAccountId, MChannelId } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
@ -221,7 +222,7 @@ type AvailableForListOptions = {
}
]
})
export class VideoPlaylistModel extends Model {
export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlaylistModel>>> {
@CreatedAt
createdAt: Date

View File

@ -1,9 +1,9 @@
import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models'
import { buildDirectionAndField, createSafeIn } from '@server/models/utils'
import { Model } from 'sequelize-typescript'
import { MUserAccountId, MUserId } from '@server/types/models'
import { Sequelize } from 'sequelize/types'
import validator from 'validator'
import { exists } from '@server/helpers/custom-validators/misc'
import { buildDirectionAndField, createSafeIn } from '@server/models/utils'
import { MUserAccountId, MUserId } from '@server/types/models'
import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models'
export type BuildVideosQueryOptions = {
attributes?: string[]
@ -55,7 +55,7 @@ export type BuildVideosQueryOptions = {
having?: string
}
function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) {
function buildListQuery (sequelize: Sequelize, options: BuildVideosQueryOptions) {
const and: string[] = []
const joins: string[] = []
const replacements: any = {}
@ -77,7 +77,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions)
const blockerIds = [ options.serverAccountId ]
if (options.user) blockerIds.push(options.user.Account.id)
const inClause = createSafeIn(model, blockerIds)
const inClause = createSafeIn(sequelize, blockerIds)
and.push(
'NOT EXISTS (' +
@ -179,7 +179,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions)
'EXISTS (' +
' SELECT 1 FROM "videoTag" ' +
' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' +
' WHERE lower("tag"."name") IN (' + createSafeIn(model, tagsOneOfLower) + ') ' +
' WHERE lower("tag"."name") IN (' + createSafeIn(sequelize, tagsOneOfLower) + ') ' +
' AND "video"."id" = "videoTag"."videoId"' +
')'
)
@ -192,7 +192,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions)
'EXISTS (' +
' SELECT 1 FROM "videoTag" ' +
' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' +
' WHERE lower("tag"."name") IN (' + createSafeIn(model, tagsAllOfLower) + ') ' +
' WHERE lower("tag"."name") IN (' + createSafeIn(sequelize, tagsAllOfLower) + ') ' +
' AND "video"."id" = "videoTag"."videoId" ' +
' GROUP BY "videoTag"."videoId" HAVING COUNT(*) = ' + tagsAllOfLower.length +
')'
@ -232,7 +232,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions)
languagesQueryParts.push(
'EXISTS (' +
' SELECT 1 FROM "videoCaption" WHERE "videoCaption"."language" ' +
' IN (' + createSafeIn(model, languages) + ') AND ' +
' IN (' + createSafeIn(sequelize, languages) + ') AND ' +
' "videoCaption"."videoId" = "video"."id"' +
')'
)
@ -345,8 +345,8 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions)
}
if (options.search) {
const escapedSearch = model.sequelize.escape(options.search)
const escapedLikeSearch = model.sequelize.escape('%' + options.search + '%')
const escapedSearch = sequelize.escape(options.search)
const escapedLikeSearch = sequelize.escape('%' + options.search + '%')
cte.push(
'"trigramSearch" AS (' +

View File

@ -1,5 +1,6 @@
import { literal, Op, QueryTypes, Transaction } from 'sequelize'
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { AttributesOnly } from '@shared/core-utils'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
import { MActorDefault } from '../../types/models'
@ -50,7 +51,7 @@ enum ScopeNames {
}
]
})
export class VideoShareModel extends Model {
export class VideoShareModel extends Model<Partial<AttributesOnly<VideoShareModel>>> {
@AllowNull(false)
@Is('VideoShareUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))

View File

@ -13,6 +13,7 @@ import { CONSTRAINTS_FIELDS, MEMOIZE_LENGTH, MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_
import { VideoRedundancyModel } from '../redundancy/video-redundancy'
import { throwIfNotValid } from '../utils'
import { VideoModel } from './video'
import { AttributesOnly } from '@shared/core-utils'
@Table({
tableName: 'videoStreamingPlaylist',
@ -30,7 +31,7 @@ import { VideoModel } from './video'
}
]
})
export class VideoStreamingPlaylistModel extends Model {
export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<VideoStreamingPlaylistModel>>> {
@CreatedAt
createdAt: Date

View File

@ -1,4 +1,5 @@
import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { AttributesOnly } from '@shared/core-utils'
import { TagModel } from './tag'
import { VideoModel } from './video'
@ -13,7 +14,7 @@ import { VideoModel } from './video'
}
]
})
export class VideoTagModel extends Model {
export class VideoTagModel extends Model<Partial<AttributesOnly<VideoTagModel>>> {
@CreatedAt
createdAt: Date

View File

@ -1,6 +1,7 @@
import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Table } from 'sequelize-typescript'
import { VideoModel } from './video'
import * as Sequelize from 'sequelize'
import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Table } from 'sequelize-typescript'
import { AttributesOnly } from '@shared/core-utils'
import { VideoModel } from './video'
@Table({
tableName: 'videoView',
@ -14,7 +15,7 @@ import * as Sequelize from 'sequelize'
}
]
})
export class VideoViewModel extends Model {
export class VideoViewModel extends Model<Partial<AttributesOnly<VideoViewModel>>> {
@CreatedAt
createdAt: Date

View File

@ -31,6 +31,7 @@ import { LiveManager } from '@server/lib/live-manager'
import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths'
import { getServerActor } from '@server/models/application/application'
import { ModelCache } from '@server/models/model-cache'
import { AttributesOnly } from '@shared/core-utils'
import { VideoFile } from '@shared/models/videos/video-file.model'
import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared'
import { VideoObject } from '../../../shared/models/activitypub/objects'
@ -489,7 +490,7 @@ export type AvailableForListIDsOptions = {
}
]
})
export class VideoModel extends Model {
export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
@AllowNull(false)
@Default(DataType.UUIDV4)
@ -1617,7 +1618,7 @@ export class VideoModel extends Model {
includeLocalVideos: true
}
const { query, replacements } = buildListQuery(VideoModel, queryOptions)
const { query, replacements } = buildListQuery(VideoModel.sequelize, queryOptions)
return this.sequelize.query<any>(query, { replacements, type: QueryTypes.SELECT })
.then(rows => rows.map(r => r[field]))
@ -1645,7 +1646,7 @@ export class VideoModel extends Model {
if (countVideos !== true) return Promise.resolve(undefined)
const countOptions = Object.assign({}, options, { isCount: true })
const { query: queryCount, replacements: replacementsCount } = buildListQuery(VideoModel, countOptions)
const { query: queryCount, replacements: replacementsCount } = buildListQuery(VideoModel.sequelize, countOptions)
return VideoModel.sequelize.query<any>(queryCount, { replacements: replacementsCount, type: QueryTypes.SELECT })
.then(rows => rows.length !== 0 ? rows[0].total : 0)
@ -1654,7 +1655,7 @@ export class VideoModel extends Model {
function getModels () {
if (options.count === 0) return Promise.resolve([])
const { query, replacements, order } = buildListQuery(VideoModel, options)
const { query, replacements, order } = buildListQuery(VideoModel.sequelize, options)
const queryModels = wrapForAPIResults(query, replacements, options, order)
return VideoModel.sequelize.query<any>(queryModels, { replacements, type: QueryTypes.SELECT, nest: true })

View File

@ -1,4 +1,5 @@
import { Model } from 'sequelize-typescript'
import { AttributesOnly } from '@shared/core-utils'
import { Model } from 'sequelize'
// Thanks to sequelize-typescript: https://github.com/RobinBuschmann/sequelize-typescript
@ -9,7 +10,7 @@ export type Omit<T, K extends keyof T> = { [P in Diff<keyof T, K>]: T[P] }
export type RecursivePartial<T> = { [P in keyof T]?: RecursivePartial<T[P]> }
export type FilteredModelAttributes<T extends Model<T>> = RecursivePartial<Omit<T, keyof Model<any>>> & {
export type FilteredModelAttributes<T extends Model<any>> = Partial<AttributesOnly<T>> & {
id?: number | any
createdAt?: Date | any
updatedAt?: Date | any

View File

@ -6,6 +6,10 @@ export type FunctionPropertyNames<T> = {
export type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>
export type AttributesOnly<T> = {
[K in keyof T]: T[K] extends Function ? never : T[K]
}
export type PickWith<T, KT extends keyof T, V> = {
[P in KT]: T[P] extends V ? V : never
}