X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Faccount%2Faccount.ts;h=61a88524c2d39662557d9ba524450f01b0566981;hb=25ed141c7c7631ef21d8764c1163fbf8a6591391;hp=7ce97b2fd97c1ce747dbd054934c34aa209e13d5;hpb=ce548a10db3822c415b30ea0edb59e02a460734a;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/account/account.ts b/server/models/account/account.ts index 7ce97b2fd..61a88524c 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts @@ -1,42 +1,26 @@ import * as Sequelize from 'sequelize' - import { - isUserUsernameValid, - isAccountPublicKeyValid, - isAccountUrlValid, - isAccountPrivateKeyValid, + activityPubContextify, isAccountFollowersCountValid, isAccountFollowingCountValid, - isAccountInboxValid, - isAccountOutboxValid, - isAccountSharedInboxValid, - isAccountFollowersValid, - isAccountFollowingValid, - activityPubContextify + isAccountPrivateKeyValid, + isAccountPublicKeyValid, + isUserUsernameValid } from '../../helpers' - -import { addMethodsToModel, getSort } from '../utils' -import { - AccountInstance, - AccountAttributes, - - AccountMethods -} from './account-interface' -import LoadApplication = AccountMethods.LoadApplication -import { sendDeleteAccount } from '../../lib/activitypub/send-request' +import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' +import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants' +import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete' +import { addMethodsToModel } from '../utils' +import { AccountAttributes, AccountInstance, AccountMethods } from './account-interface' let Account: Sequelize.Model -let loadAccountByPodAndUUID: AccountMethods.LoadAccountByPodAndUUID let load: AccountMethods.Load let loadApplication: AccountMethods.LoadApplication let loadByUUID: AccountMethods.LoadByUUID let loadByUrl: AccountMethods.LoadByUrl -let loadLocalAccountByNameAndPod: AccountMethods.LoadLocalAccountByNameAndPod -let listOwned: AccountMethods.ListOwned -let listFollowerUrlsForApi: AccountMethods.ListFollowerUrlsForApi -let listFollowingUrlsForApi: AccountMethods.ListFollowingUrlsForApi -let listFollowingForApi: AccountMethods.ListFollowingForApi -let listFollowersForApi: AccountMethods.ListFollowersForApi +let loadLocalByName: AccountMethods.LoadLocalByName +let loadByNameAndHost: AccountMethods.LoadByNameAndHost +let listByFollowersUrls: AccountMethods.ListByFollowersUrls let isOwned: AccountMethods.IsOwned let toActivityPubObject: AccountMethods.ToActivityPubObject let toFormattedJSON: AccountMethods.ToFormattedJSON @@ -60,25 +44,25 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes type: DataTypes.STRING, allowNull: false, validate: { - usernameValid: value => { + nameValid: value => { const res = isUserUsernameValid(value) - if (res === false) throw new Error('Username is not valid.') + if (res === false) throw new Error('Name is not valid.') } } }, url: { - type: DataTypes.STRING, + type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max), allowNull: false, validate: { urlValid: value => { - const res = isAccountUrlValid(value) + const res = isActivityPubUrlValid(value) if (res === false) throw new Error('URL is not valid.') } } }, publicKey: { - type: DataTypes.STRING, - allowNull: false, + type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY.max), + allowNull: true, validate: { publicKeyValid: value => { const res = isAccountPublicKeyValid(value) @@ -87,8 +71,8 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes } }, privateKey: { - type: DataTypes.STRING, - allowNull: false, + type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PRIVATE_KEY.max), + allowNull: true, validate: { privateKeyValid: value => { const res = isAccountPrivateKeyValid(value) @@ -110,58 +94,58 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes type: DataTypes.INTEGER, allowNull: false, validate: { - followersCountValid: value => { + followingCountValid: value => { const res = isAccountFollowingCountValid(value) if (res === false) throw new Error('Following count is not valid.') } } }, inboxUrl: { - type: DataTypes.STRING, + type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max), allowNull: false, validate: { inboxUrlValid: value => { - const res = isAccountInboxValid(value) + const res = isActivityPubUrlValid(value) if (res === false) throw new Error('Inbox URL is not valid.') } } }, outboxUrl: { - type: DataTypes.STRING, + type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max), allowNull: false, validate: { outboxUrlValid: value => { - const res = isAccountOutboxValid(value) + const res = isActivityPubUrlValid(value) if (res === false) throw new Error('Outbox URL is not valid.') } } }, sharedInboxUrl: { - type: DataTypes.STRING, + type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max), allowNull: false, validate: { sharedInboxUrlValid: value => { - const res = isAccountSharedInboxValid(value) + const res = isActivityPubUrlValid(value) if (res === false) throw new Error('Shared inbox URL is not valid.') } } }, followersUrl: { - type: DataTypes.STRING, + type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max), allowNull: false, validate: { followersUrlValid: value => { - const res = isAccountFollowersValid(value) + const res = isActivityPubUrlValid(value) if (res === false) throw new Error('Followers URL is not valid.') } } }, followingUrl: { - type: DataTypes.STRING, + type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max), allowNull: false, validate: { followingUrlValid: value => { - const res = isAccountFollowingValid(value) + const res = isActivityPubUrlValid(value) if (res === false) throw new Error('Following URL is not valid.') } } @@ -173,7 +157,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes fields: [ 'name' ] }, { - fields: [ 'podId' ] + fields: [ 'serverId' ] }, { fields: [ 'userId' ], @@ -184,7 +168,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes unique: true }, { - fields: [ 'name', 'podId' ], + fields: [ 'name', 'serverId', 'applicationId' ], unique: true } ], @@ -194,17 +178,13 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes const classMethods = [ associate, - loadAccountByPodAndUUID, loadApplication, load, loadByUUID, loadByUrl, - loadLocalAccountByNameAndPod, - listOwned, - listFollowerUrlsForApi, - listFollowingUrlsForApi, - listFollowingForApi, - listFollowersForApi + loadLocalByName, + loadByNameAndHost, + listByFollowersUrls ] const instanceMethods = [ isOwned, @@ -223,9 +203,9 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes // --------------------------------------------------------------------------- function associate (models) { - Account.belongsTo(models.Pod, { + Account.belongsTo(models.Server, { foreignKey: { - name: 'podId', + name: 'serverId', allowNull: true }, onDelete: 'cascade' @@ -241,7 +221,7 @@ function associate (models) { Account.belongsTo(models.Application, { foreignKey: { - name: 'userId', + name: 'applicationId', allowNull: true }, onDelete: 'cascade' @@ -256,16 +236,15 @@ function associate (models) { hooks: true }) - Account.hasMany(models.AccountFollower, { + Account.hasMany(models.AccountFollow, { foreignKey: { name: 'accountId', allowNull: false }, - as: 'following', onDelete: 'cascade' }) - Account.hasMany(models.AccountFollower, { + Account.hasMany(models.AccountFollow, { foreignKey: { name: 'targetAccountId', allowNull: false @@ -284,17 +263,28 @@ function afterDestroy (account: AccountInstance) { } toFormattedJSON = function (this: AccountInstance) { + let host = CONFIG.WEBSERVER.HOST + let score: number + + if (this.Server) { + host = this.Server.host + score = this.Server.score as number + } + const json = { id: this.id, - host: this.Pod.host, - name: this.name + host, + score, + name: this.name, + createdAt: this.createdAt, + updatedAt: this.updatedAt } return json } toActivityPubObject = function (this: AccountInstance) { - const type = this.podId ? 'Application' as 'Application' : 'Person' as 'Person' + const type = this.serverId ? 'Application' as 'Application' : 'Person' as 'Person' const json = { type, @@ -321,20 +311,23 @@ toActivityPubObject = function (this: AccountInstance) { } isOwned = function (this: AccountInstance) { - return this.podId === null + return this.serverId === null } -getFollowerSharedInboxUrls = function (this: AccountInstance) { +getFollowerSharedInboxUrls = function (this: AccountInstance, t: Sequelize.Transaction) { const query: Sequelize.FindOptions = { attributes: [ 'sharedInboxUrl' ], include: [ { - model: Account['sequelize'].models.AccountFollower, + model: Account['sequelize'].models.AccountFollow, + required: true, + as: 'followers', where: { targetAccountId: this.id } } - ] + ], + transaction: t } return Account.findAll(query) @@ -342,7 +335,7 @@ getFollowerSharedInboxUrls = function (this: AccountInstance) { } getFollowingUrl = function (this: AccountInstance) { - return this.url + '/followers' + return this.url + '/following' } getFollowersUrl = function (this: AccountInstance) { @@ -355,91 +348,11 @@ getPublicKeyUrl = function (this: AccountInstance) { // ------------------------------ STATICS ------------------------------ -listOwned = function () { - const query: Sequelize.FindOptions = { - where: { - podId: null - } - } - - return Account.findAll(query) -} - -listFollowerUrlsForApi = function (id: number, start: number, count?: number) { - return createListFollowForApiQuery('followers', id, start, count) -} - -listFollowingUrlsForApi = function (id: number, start: number, count?: number) { - return createListFollowForApiQuery('following', id, start, count) -} - -listFollowingForApi = function (id: number, start: number, count: number, sort: string) { - const query = { - distinct: true, - offset: start, - limit: count, - order: [ getSort(sort) ], - include: [ - { - model: Account['sequelize'].models.AccountFollow, - required: true, - as: 'following', - include: [ - { - model: Account['sequelize'].models.Account, - as: 'following', - required: true, - include: [ Account['sequelize'].models.Pod ] - } - ] - } - ] - } - - return Account.findAndCountAll(query).then(({ rows, count }) => { - return { - data: rows, - total: count - } - }) -} - -listFollowersForApi = function (id: number, start: number, count: number, sort: string) { - const query = { - distinct: true, - offset: start, - limit: count, - order: [ getSort(sort) ], - include: [ - { - model: Account['sequelize'].models.AccountFollow, - required: true, - as: 'followers', - include: [ - { - model: Account['sequelize'].models.Account, - as: 'followers', - required: true, - include: [ Account['sequelize'].models.Pod ] - } - ] - } - ] - } - - return Account.findAndCountAll(query).then(({ rows, count }) => { - return { - data: rows, - total: count - } - }) -} - loadApplication = function () { return Account.findOne({ include: [ { - model: Account['sequelize'].model.Application, + model: Account['sequelize'].models.Application, required: true } ] @@ -460,17 +373,37 @@ loadByUUID = function (uuid: string) { return Account.findOne(query) } -loadLocalAccountByNameAndPod = function (name: string, host: string) { +loadLocalByName = function (name: string) { const query: Sequelize.FindOptions = { where: { name, - userId: { - [Sequelize.Op.ne]: null - } + [Sequelize.Op.or]: [ + { + userId: { + [Sequelize.Op.ne]: null + } + }, + { + applicationId: { + [Sequelize.Op.ne]: null + } + } + ] + } + } + + return Account.findOne(query) +} + +loadByNameAndHost = function (name: string, host: string) { + const query: Sequelize.FindOptions = { + where: { + name }, include: [ { - model: Account['sequelize'].models.Pod, + model: Account['sequelize'].models.Server, + required: true, where: { host } @@ -492,56 +425,15 @@ loadByUrl = function (url: string, transaction?: Sequelize.Transaction) { return Account.findOne(query) } -loadAccountByPodAndUUID = function (uuid: string, podId: number, transaction: Sequelize.Transaction) { +listByFollowersUrls = function (followersUrls: string[], transaction?: Sequelize.Transaction) { const query: Sequelize.FindOptions = { where: { - podId, - uuid + followersUrl: { + [Sequelize.Op.in]: followersUrls + } }, transaction } - return Account.find(query) -} - -// ------------------------------ UTILS ------------------------------ - -async function createListFollowForApiQuery (type: 'followers' | 'following', id: number, start: number, count?: number) { - let firstJoin: string - let secondJoin: string - - if (type === 'followers') { - firstJoin = 'targetAccountId' - secondJoin = 'accountId' - } else { - firstJoin = 'accountId' - secondJoin = 'targetAccountId' - } - - const selections = [ '"Followers"."url" AS "url"', 'COUNT(*) AS "total"' ] - const tasks: Promise[] = [] - - for (const selection of selections) { - let query = 'SELECT ' + selection + ' FROM "Account" ' + - 'INNER JOIN "AccountFollower" ON "AccountFollower"."' + firstJoin + '" = "Account"."id" ' + - 'INNER JOIN "Account" AS "Follows" ON "Followers"."id" = "Follows"."' + secondJoin + '" ' + - 'WHERE "Account"."id" = $id ' + - 'LIMIT ' + start - - if (count !== undefined) query += ', ' + count - - const options = { - bind: { id }, - type: Sequelize.QueryTypes.SELECT - } - tasks.push(Account['sequelize'].query(query, options)) - } - - const [ followers, [ { total } ]] = await Promise.all(tasks) - const urls: string[] = followers.map(f => f.url) - - return { - data: urls, - total: parseInt(total, 10) - } + return Account.findAll(query) }