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) { function activityPubResponse (data: any, res: express.Response) {
return res.type('application/activity+json; charset=utf-8') return res.type('application/activity+json; charset=utf-8')
.json(data) .json(data)
.end()
} }
export { export {

View File

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

View File

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

View File

@ -308,7 +308,7 @@ async function addVideo (options: {
if (videoInfo.scheduleUpdate) { if (videoInfo.scheduleUpdate) {
await ScheduleVideoUpdateModel.create({ await ScheduleVideoUpdateModel.create({
videoId: video.id, videoId: video.id,
updateAt: videoInfo.scheduleUpdate.updateAt, updateAt: new Date(videoInfo.scheduleUpdate.updateAt),
privacy: videoInfo.scheduleUpdate.privacy || null privacy: videoInfo.scheduleUpdate.privacy || null
}, { transaction: t }) }, { transaction: t })
} }
@ -435,7 +435,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
if (videoInfoToUpdate.scheduleUpdate) { if (videoInfoToUpdate.scheduleUpdate) {
await ScheduleVideoUpdateModel.upsert({ await ScheduleVideoUpdateModel.upsert({
videoId: videoInstanceUpdated.id, videoId: videoInstanceUpdated.id,
updateAt: videoInfoToUpdate.scheduleUpdate.updateAt, updateAt: new Date(videoInfoToUpdate.scheduleUpdate.updateAt),
privacy: videoInfoToUpdate.scheduleUpdate.privacy || null privacy: videoInfoToUpdate.scheduleUpdate.privacy || null
}, { transaction: t }) }, { transaction: t })
} else if (videoInfoToUpdate.scheduleUpdate === null) { } 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() const obj = baseInstance.toJSON()
for (const key of Object.keys(obj)) { for (const key of Object.keys(obj)) {
@ -88,7 +88,7 @@ function afterCommitIfTransaction (t: Transaction, fn: Function) {
return fn() 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[], fromDatabase: T[],
newModels: T[], newModels: T[],
t: Transaction t: Transaction

View File

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

View File

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

View File

@ -1,5 +1,4 @@
import * as Sequelize from 'sequelize' import * as Sequelize from 'sequelize'
import { v4 as uuidv4 } from 'uuid'
import { VideoChannelCreate } from '../../shared/models' import { VideoChannelCreate } from '../../shared/models'
import { VideoModel } from '../models/video/video' import { VideoModel } from '../models/video/video'
import { VideoChannelModel } from '../models/video/video-channel' import { VideoChannelModel } from '../models/video/video-channel'
@ -9,9 +8,8 @@ import { getLocalVideoChannelActivityPubUrl } from './activitypub/url'
import { federateVideoIfNeeded } from './activitypub/videos' import { federateVideoIfNeeded } from './activitypub/videos'
async function createLocalVideoChannel (videoChannelInfo: VideoChannelCreate, account: MAccountId, t: Sequelize.Transaction) { async function createLocalVideoChannel (videoChannelInfo: VideoChannelCreate, account: MAccountId, t: Sequelize.Transaction) {
const uuid = uuidv4()
const url = getLocalVideoChannelActivityPubUrl(videoChannelInfo.name) 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 }) 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, privacy: videoInfo.privacy || VideoPrivacy.PRIVATE,
channelId: channelId, channelId: channelId,
originallyPublishedAt: videoInfo.originallyPublishedAt 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 { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { isAbuseMessageValid } from '@server/helpers/custom-validators/abuses' import { isAbuseMessageValid } from '@server/helpers/custom-validators/abuses'
import { MAbuseMessage, MAbuseMessageFormattable } from '@server/types/models' import { MAbuseMessage, MAbuseMessageFormattable } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { AbuseMessage } from '@shared/models' import { AbuseMessage } from '@shared/models'
import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account' import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account'
import { getSort, throwIfNotValid } from '../utils' 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) @AllowNull(false)
@Is('AbuseMessage', value => throwIfNotValid(value, isAbuseMessageValid, 'message')) @Is('AbuseMessage', value => throwIfNotValid(value, isAbuseMessageValid, 'message'))

View File

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

View File

@ -1,4 +1,5 @@
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' 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 { VideoDetails } from '@shared/models'
import { VideoModel } from '../video/video' import { VideoModel } from '../video/video'
import { AbuseModel } from './abuse' 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
createdAt: Date createdAt: Date

View File

@ -1,4 +1,5 @@
import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { AttributesOnly } from '@shared/core-utils'
import { VideoCommentModel } from '../video/video-comment' import { VideoCommentModel } from '../video/video-comment'
import { AbuseModel } from './abuse' 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
createdAt: Date createdAt: Date

View File

@ -1,6 +1,7 @@
import { Op } from 'sequelize' import { Op } from 'sequelize'
import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/types/models' import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { AccountBlock } from '../../../shared/models' import { AccountBlock } from '../../../shared/models'
import { ActorModel } from '../actor/actor' import { ActorModel } from '../actor/actor'
import { ServerModel } from '../server/server' 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
createdAt: Date createdAt: Date

View File

@ -7,6 +7,7 @@ import {
MAccountVideoRateAccountVideo, MAccountVideoRateAccountVideo,
MAccountVideoRateFormattable MAccountVideoRateFormattable
} from '@server/types/models/video/video-rate' } from '@server/types/models/video/video-rate'
import { AttributesOnly } from '@shared/core-utils'
import { AccountVideoRate } from '../../../shared' import { AccountVideoRate } from '../../../shared'
import { VideoRateType } from '../../../shared/models/videos' import { VideoRateType } from '../../../shared/models/videos'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 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) @AllowNull(false)
@Column(DataType.ENUM(...values(VIDEO_RATE_TYPES))) @Column(DataType.ENUM(...values(VIDEO_RATE_TYPES)))

View File

@ -17,6 +17,7 @@ import {
UpdatedAt UpdatedAt
} from 'sequelize-typescript' } from 'sequelize-typescript'
import { ModelCache } from '@server/models/model-cache' import { ModelCache } from '@server/models/model-cache'
import { AttributesOnly } from '@shared/core-utils'
import { Account, AccountSummary } from '../../../shared/models/actors' import { Account, AccountSummary } from '../../../shared/models/actors'
import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts' import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts'
import { CONSTRAINTS_FIELDS, SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' 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) @AllowNull(false)
@Column @Column

View File

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

View File

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

View File

@ -18,6 +18,7 @@ import {
UpdatedAt UpdatedAt
} from 'sequelize-typescript' } from 'sequelize-typescript'
import { ModelCache } from '@server/models/model-cache' import { ModelCache } from '@server/models/model-cache'
import { AttributesOnly } from '@shared/core-utils'
import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub' import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub'
import { ActorImage } from '../../../shared/models/actors/actor-image.model' import { ActorImage } from '../../../shared/models/actors/actor-image.model'
import { activityPubContextify } from '../../helpers/activitypub' 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) @AllowNull(false)
@Column(DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES))) @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 * 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 () { export const getServerActor = memoizee(async function () {
const application = await ApplicationModel.load() const application = await ApplicationModel.load()
@ -24,7 +25,7 @@ export const getServerActor = memoizee(async function () {
tableName: 'application', tableName: 'application',
timestamps: false timestamps: false
}) })
export class ApplicationModel extends Model { export class ApplicationModel extends Model<Partial<AttributesOnly<ApplicationModel>>> {
@AllowNull(false) @AllowNull(false)
@Default(0) @Default(0)

View File

@ -1,4 +1,5 @@
import { AllowNull, Column, CreatedAt, DataType, HasMany, Model, Table, UpdatedAt } from 'sequelize-typescript' import { AllowNull, Column, CreatedAt, DataType, HasMany, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { AttributesOnly } from '@shared/core-utils'
import { OAuthTokenModel } from './oauth-token' import { OAuthTokenModel } from './oauth-token'
@Table({ @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) @AllowNull(false)
@Column @Column

View File

@ -15,6 +15,7 @@ import {
import { TokensCache } from '@server/lib/auth/tokens-cache' import { TokensCache } from '@server/lib/auth/tokens-cache'
import { MUserAccountId } from '@server/types/models' import { MUserAccountId } from '@server/types/models'
import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token' import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token'
import { AttributesOnly } from '@shared/core-utils'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
import { AccountModel } from '../account/account' import { AccountModel } from '../account/account'
import { ActorModel } from '../actor/actor' 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) @AllowNull(false)
@Column @Column

View File

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

View File

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

View File

@ -1,6 +1,7 @@
import { Op } from 'sequelize' import { Op } from 'sequelize'
import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { MServerBlocklist, MServerBlocklistAccountServer, MServerBlocklistFormattable } from '@server/types/models' import { MServerBlocklist, MServerBlocklistAccountServer, MServerBlocklistFormattable } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { ServerBlock } from '@shared/models' import { ServerBlock } from '@shared/models'
import { AccountModel } from '../account/account' import { AccountModel } from '../account/account'
import { getSort, searchAttribute } from '../utils' 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
createdAt: Date createdAt: Date

View File

@ -1,5 +1,6 @@
import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { MServer, MServerFormattable } from '@server/types/models/server' import { MServer, MServerFormattable } from '@server/types/models/server'
import { AttributesOnly } from '@shared/core-utils'
import { isHostValid } from '../../helpers/custom-validators/servers' import { isHostValid } from '../../helpers/custom-validators/servers'
import { ActorModel } from '../actor/actor' import { ActorModel } from '../actor/actor'
import { throwIfNotValid } from '../utils' 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) @AllowNull(false)
@Is('Host', value => throwIfNotValid(value, isHostValid, 'valid host')) @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 { AllowNull, BelongsToMany, Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { Transaction } from 'sequelize/types' import { Transaction } from 'sequelize/types'
import { MTracker } from '@server/types/models/server/tracker' import { MTracker } from '@server/types/models/server/tracker'
import { AttributesOnly } from '@shared/core-utils'
import { VideoModel } from '../video/video' import { VideoModel } from '../video/video'
import { VideoTrackerModel } from './video-tracker' 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) @AllowNull(false)
@Column @Column

View File

@ -1,4 +1,5 @@
import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { AttributesOnly } from '@shared/core-utils'
import { VideoModel } from '../video/video' import { VideoModel } from '../video/video'
import { TrackerModel } from './tracker' 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
createdAt: Date createdAt: Date

View File

@ -14,6 +14,7 @@ import {
} from 'sequelize-typescript' } from 'sequelize-typescript'
import { TokensCache } from '@server/lib/auth/tokens-cache' import { TokensCache } from '@server/lib/auth/tokens-cache'
import { MNotificationSettingFormattable } from '@server/types/models' import { MNotificationSettingFormattable } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { UserNotificationSetting, UserNotificationSettingValue } from '../../../shared/models/users/user-notification-setting.model' import { UserNotificationSetting, UserNotificationSettingValue } from '../../../shared/models/users/user-notification-setting.model'
import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications' import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications'
import { throwIfNotValid } from '../utils' 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) @AllowNull(false)
@Default(null) @Default(null)

View File

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

View File

@ -31,6 +31,7 @@ import {
MUserWithNotificationSetting, MUserWithNotificationSetting,
MVideoWithRights MVideoWithRights
} from '@server/types/models' } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users' import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users'
import { AbuseState, MyUser, UserRight, VideoPlaylistType, VideoPrivacy } from '../../../shared/models' import { AbuseState, MyUser, UserRight, VideoPlaylistType, VideoPrivacy } from '../../../shared/models'
import { User, UserRole } from '../../../shared/models/users' 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) @AllowNull(true)
@Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password', true)) @Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password', true))

View File

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

View File

@ -1,6 +1,7 @@
import { col, fn, QueryTypes, Transaction } from 'sequelize' import { col, fn, QueryTypes, Transaction } from 'sequelize'
import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { MTag } from '@server/types/models' import { MTag } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { VideoPrivacy, VideoState } from '../../../shared/models/videos' import { VideoPrivacy, VideoState } from '../../../shared/models/videos'
import { isVideoTagValid } from '../../helpers/custom-validators/videos' import { isVideoTagValid } from '../../helpers/custom-validators/videos'
import { throwIfNotValid } from '../utils' 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) @AllowNull(false)
@Is('VideoTag', value => throwIfNotValid(value, isVideoTagValid, 'tag')) @Is('VideoTag', value => throwIfNotValid(value, isVideoTagValid, 'tag'))

View File

@ -17,6 +17,7 @@ import {
} from 'sequelize-typescript' } from 'sequelize-typescript'
import { afterCommitIfTransaction } from '@server/helpers/database-utils' import { afterCommitIfTransaction } from '@server/helpers/database-utils'
import { MThumbnail, MThumbnailVideo, MVideo } from '@server/types/models' import { MThumbnail, MThumbnailVideo, MVideo } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
import { CONFIG } from '../../initializers/config' 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) @AllowNull(false)
@Column @Column

View File

@ -1,6 +1,7 @@
import { FindOptions } from 'sequelize' import { FindOptions } from 'sequelize'
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { MVideoBlacklist, MVideoBlacklistFormattable } from '@server/types/models' import { MVideoBlacklist, MVideoBlacklistFormattable } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos'
import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist'
import { CONSTRAINTS_FIELDS } from '../../initializers/constants' 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) @AllowNull(true)
@Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason', true)) @Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason', true))

View File

@ -17,6 +17,7 @@ import {
} from 'sequelize-typescript' } from 'sequelize-typescript'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { MVideo, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models' 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 { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model'
import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions' import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions'
import { logger } from '../../helpers/logger' 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
createdAt: Date createdAt: Date

View File

@ -1,5 +1,6 @@
import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 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 { MVideoChangeOwnershipFormattable, MVideoChangeOwnershipFull } from '@server/types/models/video/video-change-ownership'
import { AttributesOnly } from '@shared/core-utils'
import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos' import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos'
import { AccountModel } from '../account/account' import { AccountModel } from '../account/account'
import { getSort } from '../utils' 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
createdAt: Date createdAt: Date

View File

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

View File

@ -16,6 +16,7 @@ import {
} from 'sequelize-typescript' } from 'sequelize-typescript'
import { getServerActor } from '@server/models/application/application' import { getServerActor } from '@server/models/application/application'
import { MAccount, MAccountId, MUserAccountId } from '@server/types/models' import { MAccount, MAccountId, MUserAccountId } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { VideoPrivacy } from '@shared/models' import { VideoPrivacy } from '@shared/models'
import { ActivityTagObject, ActivityTombstoneObject } from '../../../shared/models/activitypub/objects/common-objects' import { ActivityTagObject, ActivityTombstoneObject } from '../../../shared/models/activitypub/objects/common-objects'
import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' 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
createdAt: Date createdAt: Date

View File

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

View File

@ -15,6 +15,7 @@ import {
} from 'sequelize-typescript' } from 'sequelize-typescript'
import { afterCommitIfTransaction } from '@server/helpers/database-utils' import { afterCommitIfTransaction } from '@server/helpers/database-utils'
import { MVideoImportDefault, MVideoImportFormattable } from '@server/types/models/video/video-import' import { MVideoImportDefault, MVideoImportFormattable } from '@server/types/models/video/video-import'
import { AttributesOnly } from '@shared/core-utils'
import { VideoImport, VideoImportState } from '../../../shared' import { VideoImport, VideoImportState } from '../../../shared'
import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports' import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports'
import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' 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
createdAt: Date createdAt: Date

View File

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

View File

@ -32,6 +32,7 @@ import { AccountModel } from '../account/account'
import { getSort, throwIfNotValid } from '../utils' import { getSort, throwIfNotValid } from '../utils'
import { ForAPIOptions, ScopeNames as VideoScopeNames, VideoModel } from './video' import { ForAPIOptions, ScopeNames as VideoScopeNames, VideoModel } from './video'
import { VideoPlaylistModel } from './video-playlist' import { VideoPlaylistModel } from './video-playlist'
import { AttributesOnly } from '@shared/core-utils'
@Table({ @Table({
tableName: 'videoPlaylistElement', 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
createdAt: Date createdAt: Date
@ -274,7 +275,8 @@ export class VideoPlaylistElementModel extends Model {
validate: false // We use a literal to update the position 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 ( static increasePositionOf (

View File

@ -19,6 +19,7 @@ import {
} from 'sequelize-typescript' } from 'sequelize-typescript'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { MAccountId, MChannelId } from '@server/types/models' import { MAccountId, MChannelId } from '@server/types/models'
import { AttributesOnly } from '@shared/core-utils'
import { ActivityIconObject } from '../../../shared/models/activitypub/objects' import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' 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
createdAt: Date createdAt: Date

View File

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

View File

@ -1,5 +1,6 @@
import { literal, Op, QueryTypes, Transaction } from 'sequelize' import { literal, Op, QueryTypes, Transaction } from 'sequelize'
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 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 { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
import { CONSTRAINTS_FIELDS } from '../../initializers/constants' import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
import { MActorDefault } from '../../types/models' 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) @AllowNull(false)
@Is('VideoShareUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url')) @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 { VideoRedundancyModel } from '../redundancy/video-redundancy'
import { throwIfNotValid } from '../utils' import { throwIfNotValid } from '../utils'
import { VideoModel } from './video' import { VideoModel } from './video'
import { AttributesOnly } from '@shared/core-utils'
@Table({ @Table({
tableName: 'videoStreamingPlaylist', 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
createdAt: Date createdAt: Date

View File

@ -1,4 +1,5 @@
import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { AttributesOnly } from '@shared/core-utils'
import { TagModel } from './tag' import { TagModel } from './tag'
import { VideoModel } from './video' 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
createdAt: Date 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 * 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({ @Table({
tableName: 'videoView', 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
createdAt: Date createdAt: Date

View File

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