-import * as Sequelize from 'sequelize'
import {
- AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Model, Table,
+ AllowNull,
+ BeforeDestroy,
+ BelongsTo,
+ Column,
+ CreatedAt, DataType,
+ Default,
+ DefaultScope,
+ ForeignKey,
+ HasMany,
+ Is,
+ Model,
+ Scopes,
+ Table,
UpdatedAt
} from 'sequelize-typescript'
-import { Account } from '../../../shared/models/actors'
-import { logger } from '../../helpers/logger'
+import { Account, AccountSummary } from '../../../shared/models/actors'
+import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts'
import { sendDeleteActor } from '../../lib/activitypub/send'
import { ActorModel } from '../activitypub/actor'
import { ApplicationModel } from '../application/application'
-import { AvatarModel } from '../avatar/avatar'
import { ServerModel } from '../server/server'
-import { getSort } from '../utils'
+import { getSort, throwIfNotValid } from '../utils'
import { VideoChannelModel } from '../video/video-channel'
import { VideoCommentModel } from '../video/video-comment'
import { UserModel } from './user'
+import { AvatarModel } from '../avatar/avatar'
+import { VideoPlaylistModel } from '../video/video-playlist'
+import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants'
+import { Op, Transaction, WhereOptions } from 'sequelize'
-@DefaultScope({
+export enum ScopeNames {
+ SUMMARY = 'SUMMARY'
+}
+
+@DefaultScope(() => ({
include: [
{
- model: () => ActorModel,
- required: true,
+ model: ActorModel, // Default scope includes avatar and server
+ required: true
+ }
+ ]
+}))
+@Scopes(() => ({
+ [ ScopeNames.SUMMARY ]: (whereActor?: WhereOptions) => {
+ return {
+ attributes: [ 'id', 'name' ],
include: [
{
- model: () => ServerModel,
- required: false
- },
- {
- model: () => AvatarModel,
- required: false
+ attributes: [ 'id', 'uuid', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
+ model: ActorModel.unscoped(),
+ required: true,
+ where: whereActor,
+ include: [
+ {
+ attributes: [ 'host' ],
+ model: ServerModel.unscoped(),
+ required: false
+ },
+ {
+ model: AvatarModel.unscoped(),
+ required: false
+ }
+ ]
}
]
}
- ]
-})
+ }
+}))
@Table({
- tableName: 'account'
+ tableName: 'account',
+ indexes: [
+ {
+ fields: [ 'actorId' ],
+ unique: true
+ },
+ {
+ fields: [ 'applicationId' ]
+ },
+ {
+ fields: [ 'userId' ]
+ }
+ ]
})
export class AccountModel extends Model<AccountModel> {
@Column
name: string
+ @AllowNull(true)
+ @Default(null)
+ @Is('AccountDescription', value => throwIfNotValid(value, isAccountDescriptionValid, 'description', true))
+ @Column(DataType.STRING(CONSTRAINTS_FIELDS.USERS.DESCRIPTION.max))
+ description: string
+
@CreatedAt
createdAt: Date
})
VideoChannels: VideoChannelModel[]
+ @HasMany(() => VideoPlaylistModel, {
+ foreignKey: {
+ allowNull: false
+ },
+ onDelete: 'cascade',
+ hooks: true
+ })
+ VideoPlaylists: VideoPlaylistModel[]
+
@HasMany(() => VideoCommentModel, {
foreignKey: {
allowNull: false
}
if (instance.isOwned()) {
- logger.debug('Sending delete of actor of account %s.', instance.Actor.url)
return sendDeleteActor(instance.Actor, options.transaction)
}
return undefined
}
- static load (id: number) {
- return AccountModel.findById(id)
+ static load (id: number, transaction?: Transaction) {
+ return AccountModel.findByPk(id, { transaction })
}
static loadByUUID (uuid: string) {
return AccountModel.findOne(query)
}
+ static loadByNameWithHost (nameWithHost: string) {
+ const [ accountName, host ] = nameWithHost.split('@')
+
+ if (!host || host === WEBSERVER.HOST) return AccountModel.loadLocalByName(accountName)
+
+ return AccountModel.loadByNameAndHost(accountName, host)
+ }
+
static loadLocalByName (name: string) {
const query = {
where: {
- name,
- [ Sequelize.Op.or ]: [
+ [ Op.or ]: [
{
userId: {
- [ Sequelize.Op.ne ]: null
+ [ Op.ne ]: null
}
},
{
applicationId: {
- [ Sequelize.Op.ne ]: null
+ [ Op.ne ]: null
}
}
]
- }
+ },
+ include: [
+ {
+ model: ActorModel,
+ required: true,
+ where: {
+ preferredUsername: name
+ }
+ }
+ ]
}
return AccountModel.findOne(query)
}
- static loadByUrl (url: string, transaction?: Sequelize.Transaction) {
+ static loadByNameAndHost (name: string, host: string) {
+ const query = {
+ include: [
+ {
+ model: ActorModel,
+ required: true,
+ where: {
+ preferredUsername: name
+ },
+ include: [
+ {
+ model: ServerModel,
+ required: true,
+ where: {
+ host
+ }
+ }
+ ]
+ }
+ ]
+ }
+
+ return AccountModel.findOne(query)
+ }
+
+ static loadByUrl (url: string, transaction?: Transaction) {
const query = {
include: [
{
const query = {
offset: start,
limit: count,
- order: [ getSort(sort) ]
+ order: getSort(sort)
}
return AccountModel.findAndCountAll(query)
})
}
+ static listLocalsForSitemap (sort: string) {
+ const query = {
+ attributes: [ ],
+ offset: 0,
+ order: getSort(sort),
+ include: [
+ {
+ attributes: [ 'preferredUsername', 'serverId' ],
+ model: ActorModel.unscoped(),
+ where: {
+ serverId: null
+ }
+ }
+ ]
+ }
+
+ return AccountModel
+ .unscoped()
+ .findAll(query)
+ }
+
toFormattedJSON (): Account {
const actor = this.Actor.toFormattedJSON()
const account = {
id: this.id,
- displayName: this.name,
+ displayName: this.getDisplayName(),
+ description: this.description,
createdAt: this.createdAt,
- updatedAt: this.updatedAt
+ updatedAt: this.updatedAt,
+ userId: this.userId ? this.userId : undefined
}
return Object.assign(actor, account)
}
+ toFormattedSummaryJSON (): AccountSummary {
+ const actor = this.Actor.toFormattedJSON()
+
+ return {
+ id: this.id,
+ uuid: actor.uuid,
+ name: actor.name,
+ displayName: this.getDisplayName(),
+ url: actor.url,
+ host: actor.host,
+ avatar: actor.avatar
+ }
+ }
+
toActivityPubObject () {
- return this.Actor.toActivityPubObject(this.name, 'Account')
+ const obj = this.Actor.toActivityPubObject(this.name, 'Account')
+
+ return Object.assign(obj, {
+ summary: this.description
+ })
}
isOwned () {
return this.Actor.isOwned()
}
+
+ isOutdated () {
+ return this.Actor.isOutdated()
+ }
+
+ getDisplayName () {
+ return this.name
+ }
}