X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Faccount%2Fuser.ts;h=e7503952138402d67b52e2ed56399dcf3e643bb6;hb=d1ab89deb79f70c439b58750d044d9cadf1194e5;hp=55ec14d0568a2b378630a142c93f16cff399d38c;hpb=cef534ed53e4518fe0acf581bfe880788d42fc36;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/account/user.ts b/server/models/account/user.ts index 55ec14d05..e75039521 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts @@ -1,4 +1,4 @@ -import * as Sequelize from 'sequelize' +import { FindOptions, literal, Op, QueryTypes } from 'sequelize' import { AfterDestroy, AfterUpdate, @@ -22,6 +22,7 @@ import { import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared' import { User, UserRole } from '../../../shared/models/users' import { + isUserAdminFlagsValid, isUserAutoPlayVideoValid, isUserBlockedReasonValid, isUserBlockedValid, @@ -42,44 +43,46 @@ import { VideoChannelModel } from '../video/video-channel' import { AccountModel } from './account' import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type' import { values } from 'lodash' -import { NSFW_POLICY_TYPES } from '../../initializers' +import { NSFW_POLICY_TYPES } from '../../initializers/constants' import { clearCacheByUserId } from '../../lib/oauth-model' import { UserNotificationSettingModel } from './user-notification-setting' import { VideoModel } from '../video/video' import { ActorModel } from '../activitypub/actor' import { ActorFollowModel } from '../activitypub/actor-follow' +import { VideoImportModel } from '../video/video-import' +import { UserAdminFlag } from '../../../shared/models/users/user-flag.model' enum ScopeNames { WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL' } -@DefaultScope({ +@DefaultScope(() => ({ include: [ { - model: () => AccountModel, + model: AccountModel, required: true }, { - model: () => UserNotificationSettingModel, + model: UserNotificationSettingModel, required: true } ] -}) -@Scopes({ +})) +@Scopes(() => ({ [ScopeNames.WITH_VIDEO_CHANNEL]: { include: [ { - model: () => AccountModel, + model: AccountModel, required: true, - include: [ () => VideoChannelModel ] + include: [ VideoChannelModel ] }, { - model: () => UserNotificationSettingModel, + model: UserNotificationSettingModel, required: true } ] } -}) +})) @Table({ tableName: 'user', indexes: [ @@ -110,15 +113,20 @@ export class UserModel extends Model { @Column(DataType.STRING(400)) email: string + @AllowNull(true) + @IsEmail + @Column(DataType.STRING(400)) + pendingEmail: string + @AllowNull(true) @Default(null) - @Is('UserEmailVerified', value => throwIfNotValid(value, isUserEmailVerifiedValid, 'email verified boolean')) + @Is('UserEmailVerified', value => throwIfNotValid(value, isUserEmailVerifiedValid, 'email verified boolean', true)) @Column emailVerified: boolean @AllowNull(false) @Is('UserNSFWPolicy', value => throwIfNotValid(value, isUserNSFWPolicyValid, 'NSFW policy')) - @Column(DataType.ENUM(values(NSFW_POLICY_TYPES))) + @Column(DataType.ENUM(...values(NSFW_POLICY_TYPES))) nsfwPolicy: NSFWPolicyType @AllowNull(false) @@ -139,6 +147,12 @@ export class UserModel extends Model { @Column autoPlayVideo: boolean + @AllowNull(false) + @Default(UserAdminFlag.NONE) + @Is('UserAdminFlags', value => throwIfNotValid(value, isUserAdminFlagsValid, 'user admin flags')) + @Column + adminFlags?: UserAdminFlag + @AllowNull(false) @Default(false) @Is('UserBlocked', value => throwIfNotValid(value, isUserBlockedValid, 'blocked boolean')) @@ -147,7 +161,7 @@ export class UserModel extends Model { @AllowNull(true) @Default(null) - @Is('UserBlockedReason', value => throwIfNotValid(value, isUserBlockedReasonValid, 'blocked reason')) + @Is('UserBlockedReason', value => throwIfNotValid(value, isUserBlockedReasonValid, 'blocked reason', true)) @Column blockedReason: string @@ -186,6 +200,12 @@ export class UserModel extends Model { }) NotificationSetting: UserNotificationSettingModel + @HasMany(() => VideoImportModel, { + foreignKey: 'userId', + onDelete: 'cascade' + }) + VideoImports: VideoImportModel[] + @HasMany(() => OAuthTokenModel, { foreignKey: 'userId', onDelete: 'cascade' @@ -218,26 +238,26 @@ export class UserModel extends Model { let where = undefined if (search) { where = { - [Sequelize.Op.or]: [ + [Op.or]: [ { email: { - [Sequelize.Op.iLike]: '%' + search + '%' + [Op.iLike]: '%' + search + '%' } }, { username: { - [ Sequelize.Op.iLike ]: '%' + search + '%' + [ Op.iLike ]: '%' + search + '%' } } ] } } - const query = { + const query: FindOptions = { attributes: { include: [ [ - Sequelize.literal( + literal( '(' + 'SELECT COALESCE(SUM("size"), 0) ' + 'FROM (' + @@ -250,7 +270,7 @@ export class UserModel extends Model { ')' ), 'videoQuotaUsed' - ] as any // FIXME: typings + ] ] }, offset: start, @@ -276,7 +296,7 @@ export class UserModel extends Model { const query = { where: { role: { - [Sequelize.Op.in]: roles + [Op.in]: roles } } } @@ -323,8 +343,18 @@ export class UserModel extends Model { return UserModel.unscoped().findAll(query) } + static listByUsernames (usernames: string[]) { + const query = { + where: { + username: usernames + } + } + + return UserModel.findAll(query) + } + static loadById (id: number) { - return UserModel.findById(id) + return UserModel.findByPk(id) } static loadByUsername (username: string) { @@ -362,7 +392,7 @@ export class UserModel extends Model { const query = { where: { - [ Sequelize.Op.or ]: [ { username }, { email } ] + [ Op.or ]: [ { username }, { email } ] } } @@ -400,6 +430,64 @@ export class UserModel extends Model { return UserModel.findOne(query) } + static loadByVideoImportId (videoImportId: number) { + const query = { + include: [ + { + required: true, + attributes: [ 'id' ], + model: VideoImportModel.unscoped(), + where: { + id: videoImportId + } + } + ] + } + + return UserModel.findOne(query) + } + + static loadByChannelActorId (videoChannelActorId: number) { + const query = { + include: [ + { + required: true, + attributes: [ 'id' ], + model: AccountModel.unscoped(), + include: [ + { + required: true, + attributes: [ 'id' ], + model: VideoChannelModel.unscoped(), + where: { + actorId: videoChannelActorId + } + } + ] + } + ] + } + + return UserModel.findOne(query) + } + + static loadByAccountActorId (accountActorId: number) { + const query = { + include: [ + { + required: true, + attributes: [ 'id' ], + model: AccountModel.unscoped(), + where: { + actorId: accountActorId + } + } + ] + } + + return UserModel.findOne(query) + } + static getOriginalVideoFileTotalFromUser (user: UserModel) { // Don't use sequelize because we need to use a sub query const query = UserModel.generateUserQuotaBaseSQL() @@ -427,7 +515,7 @@ export class UserModel extends Model { const query = { where: { username: { - [ Sequelize.Op.like ]: `%${search}%` + [ Op.like ]: `%${search}%` } }, limit: 10 @@ -441,11 +529,15 @@ export class UserModel extends Model { return hasUserRight(this.role, right) } + hasAdminFlag (flag: UserAdminFlag) { + return this.adminFlags & flag + } + isPasswordMatch (password: string) { return comparePassword(password, this.password) } - toFormattedJSON (): User { + toFormattedJSON (parameters: { withAdminFlags?: boolean } = {}): User { const videoQuotaUsed = this.get('videoQuotaUsed') const videoQuotaUsedDaily = this.get('videoQuotaUsedDaily') @@ -453,6 +545,7 @@ export class UserModel extends Model { id: this.id, username: this.username, email: this.email, + pendingEmail: this.pendingEmail, emailVerified: this.emailVerified, nsfwPolicy: this.nsfwPolicy, webTorrentEnabled: this.webTorrentEnabled, @@ -469,13 +562,17 @@ export class UserModel extends Model { notificationSettings: this.NotificationSetting ? this.NotificationSetting.toFormattedJSON() : undefined, videoChannels: [], videoQuotaUsed: videoQuotaUsed !== undefined - ? parseInt(videoQuotaUsed, 10) + ? parseInt(videoQuotaUsed + '', 10) : undefined, videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined - ? parseInt(videoQuotaUsedDaily, 10) + ? parseInt(videoQuotaUsedDaily + '', 10) : undefined } + if (parameters.withAdminFlags) { + Object.assign(json, { adminFlags: this.adminFlags }) + } + if (Array.isArray(this.Account.VideoChannels) === true) { json.videoChannels = this.Account.VideoChannels .map(c => c.toFormattedJSON()) @@ -500,15 +597,11 @@ export class UserModel extends Model { const uploadedTotal = videoFile.size + totalBytes const uploadedDaily = videoFile.size + totalBytesDaily - if (this.videoQuotaDaily === -1) { - return uploadedTotal < this.videoQuota - } - if (this.videoQuota === -1) { - return uploadedDaily < this.videoQuotaDaily - } - return (uploadedTotal < this.videoQuota) && - (uploadedDaily < this.videoQuotaDaily) + if (this.videoQuotaDaily === -1) return uploadedTotal < this.videoQuota + if (this.videoQuota === -1) return uploadedDaily < this.videoQuotaDaily + + return uploadedTotal < this.videoQuota && uploadedDaily < this.videoQuotaDaily } private static generateUserQuotaBaseSQL (where?: string) { @@ -528,10 +621,10 @@ export class UserModel extends Model { private static getTotalRawQuery (query: string, userId: number) { const options = { bind: { userId }, - type: Sequelize.QueryTypes.SELECT + type: QueryTypes.SELECT as QueryTypes.SELECT } - return UserModel.sequelize.query(query, options) + return UserModel.sequelize.query<{ total: string }>(query, options) .then(([ { total } ]) => { if (total === null) return 0