mirror of https://github.com/Chocobozzz/PeerTube
Save
parent
7efe153b0b
commit
fadf619ad6
|
@ -40,14 +40,14 @@ async function outboxController (req: express.Request, res: express.Response, ne
|
||||||
// This is a shared video
|
// This is a shared video
|
||||||
const videoChannel = video.VideoChannel
|
const videoChannel = video.VideoChannel
|
||||||
if (video.VideoShares !== undefined && video.VideoShares.length !== 0) {
|
if (video.VideoShares !== undefined && video.VideoShares.length !== 0) {
|
||||||
const addActivity = await addActivityData(video.url, videoChannel.Account, video, videoChannel.url, videoObject, undefined)
|
const addActivity = await addActivityData(video.url, videoChannel.Account, video, videoChannel.Actor.url, videoObject, undefined)
|
||||||
|
|
||||||
const url = getAnnounceActivityPubUrl(video.url, account)
|
const url = getAnnounceActivityPubUrl(video.url, account)
|
||||||
const announceActivity = await announceActivityData(url, account, addActivity, undefined)
|
const announceActivity = await announceActivityData(url, account, addActivity, undefined)
|
||||||
|
|
||||||
activities.push(announceActivity)
|
activities.push(announceActivity)
|
||||||
} else {
|
} else {
|
||||||
const addActivity = await addActivityData(video.url, account, video, videoChannel.url, videoObject, undefined)
|
const addActivity = await addActivityData(video.url, account, video, videoChannel.Actor.url, videoObject, undefined)
|
||||||
|
|
||||||
activities.push(addActivity)
|
activities.push(addActivity)
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,7 @@ async function removeFollow (req: express.Request, res: express.Response, next:
|
||||||
// This could be long so don't wait this task
|
// This could be long so don't wait this task
|
||||||
const following = follow.AccountFollowing
|
const following = follow.AccountFollowing
|
||||||
following.destroy()
|
following.destroy()
|
||||||
.catch(err => logger.error('Cannot destroy account that we do not follow anymore %s.', following.url, err))
|
.catch(err => logger.error('Cannot destroy account that we do not follow anymore %s.', following.Actor.url, err))
|
||||||
|
|
||||||
return res.status(204).end()
|
return res.status(204).end()
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,16 +92,15 @@ async function addVideoChannelRetryWrapper (req: express.Request, res: express.R
|
||||||
return res.type('json').status(204).end()
|
return res.type('json').status(204).end()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addVideoChannel (req: express.Request, res: express.Response) {
|
function addVideoChannel (req: express.Request, res: express.Response) {
|
||||||
const videoChannelInfo: VideoChannelCreate = req.body
|
const videoChannelInfo: VideoChannelCreate = req.body
|
||||||
const account: AccountModel = res.locals.oauth.token.User.Account
|
const account: AccountModel = res.locals.oauth.token.User.Account
|
||||||
let videoChannelCreated: VideoChannelModel
|
|
||||||
|
|
||||||
await sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
videoChannelCreated = await createVideoChannel(videoChannelInfo, account, t)
|
const videoChannelCreated = await createVideoChannel(videoChannelInfo, account, t)
|
||||||
|
|
||||||
|
logger.info('Video channel with uuid %s created.', videoChannelCreated.uuid)
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.info('Video channel with uuid %s created.', videoChannelCreated.uuid)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
|
|
|
@ -23,12 +23,12 @@ function webfingerController (req: express.Request, res: express.Response, next:
|
||||||
|
|
||||||
const json = {
|
const json = {
|
||||||
subject: req.query.resource,
|
subject: req.query.resource,
|
||||||
aliases: [ account.url ],
|
aliases: [ account.Actor.url ],
|
||||||
links: [
|
links: [
|
||||||
{
|
{
|
||||||
rel: 'self',
|
rel: 'self',
|
||||||
type: 'application/activity+json',
|
type: 'application/activity+json',
|
||||||
href: account.url
|
href: account.Actor.url
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,92 +0,0 @@
|
||||||
import * as validator from 'validator'
|
|
||||||
import { CONSTRAINTS_FIELDS } from '../../../initializers'
|
|
||||||
import { isAccountNameValid } from '../accounts'
|
|
||||||
import { exists, isUUIDValid } from '../misc'
|
|
||||||
import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
|
|
||||||
|
|
||||||
function isAccountEndpointsObjectValid (endpointObject: any) {
|
|
||||||
return isActivityPubUrlValid(endpointObject.sharedInbox)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAccountPublicKeyObjectValid (publicKeyObject: any) {
|
|
||||||
return isActivityPubUrlValid(publicKeyObject.id) &&
|
|
||||||
isActivityPubUrlValid(publicKeyObject.owner) &&
|
|
||||||
isAccountPublicKeyValid(publicKeyObject.publicKeyPem)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAccountTypeValid (type: string) {
|
|
||||||
return type === 'Person' || type === 'Application'
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAccountPublicKeyValid (publicKey: string) {
|
|
||||||
return exists(publicKey) &&
|
|
||||||
typeof publicKey === 'string' &&
|
|
||||||
publicKey.startsWith('-----BEGIN PUBLIC KEY-----') &&
|
|
||||||
publicKey.endsWith('-----END PUBLIC KEY-----') &&
|
|
||||||
validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAccountPreferredUsernameValid (preferredUsername: string) {
|
|
||||||
return isAccountNameValid(preferredUsername)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAccountPrivateKeyValid (privateKey: string) {
|
|
||||||
return exists(privateKey) &&
|
|
||||||
typeof privateKey === 'string' &&
|
|
||||||
privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----') &&
|
|
||||||
privateKey.endsWith('-----END RSA PRIVATE KEY-----') &&
|
|
||||||
validator.isLength(privateKey, CONSTRAINTS_FIELDS.ACCOUNTS.PRIVATE_KEY)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isRemoteAccountValid (remoteAccount: any) {
|
|
||||||
return isActivityPubUrlValid(remoteAccount.id) &&
|
|
||||||
isUUIDValid(remoteAccount.uuid) &&
|
|
||||||
isAccountTypeValid(remoteAccount.type) &&
|
|
||||||
isActivityPubUrlValid(remoteAccount.following) &&
|
|
||||||
isActivityPubUrlValid(remoteAccount.followers) &&
|
|
||||||
isActivityPubUrlValid(remoteAccount.inbox) &&
|
|
||||||
isActivityPubUrlValid(remoteAccount.outbox) &&
|
|
||||||
isAccountPreferredUsernameValid(remoteAccount.preferredUsername) &&
|
|
||||||
isActivityPubUrlValid(remoteAccount.url) &&
|
|
||||||
isAccountPublicKeyObjectValid(remoteAccount.publicKey) &&
|
|
||||||
isAccountEndpointsObjectValid(remoteAccount.endpoints)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAccountFollowingCountValid (value: string) {
|
|
||||||
return exists(value) && validator.isInt('' + value, { min: 0 })
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAccountFollowersCountValid (value: string) {
|
|
||||||
return exists(value) && validator.isInt('' + value, { min: 0 })
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAccountDeleteActivityValid (activity: any) {
|
|
||||||
return isBaseActivityValid(activity, 'Delete')
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAccountFollowActivityValid (activity: any) {
|
|
||||||
return isBaseActivityValid(activity, 'Follow') &&
|
|
||||||
isActivityPubUrlValid(activity.object)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAccountAcceptActivityValid (activity: any) {
|
|
||||||
return isBaseActivityValid(activity, 'Accept')
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export {
|
|
||||||
isAccountEndpointsObjectValid,
|
|
||||||
isAccountPublicKeyObjectValid,
|
|
||||||
isAccountTypeValid,
|
|
||||||
isAccountPublicKeyValid,
|
|
||||||
isAccountPreferredUsernameValid,
|
|
||||||
isAccountPrivateKeyValid,
|
|
||||||
isRemoteAccountValid,
|
|
||||||
isAccountFollowingCountValid,
|
|
||||||
isAccountFollowersCountValid,
|
|
||||||
isAccountNameValid,
|
|
||||||
isAccountFollowActivityValid,
|
|
||||||
isAccountAcceptActivityValid,
|
|
||||||
isAccountDeleteActivityValid
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as validator from 'validator'
|
import * as validator from 'validator'
|
||||||
import { Activity, ActivityType } from '../../../../shared/models/activitypub'
|
import { Activity, ActivityType } from '../../../../shared/models/activitypub'
|
||||||
import { isAccountAcceptActivityValid, isAccountDeleteActivityValid, isAccountFollowActivityValid } from './account'
|
import { isAccountAcceptActivityValid, isAccountDeleteActivityValid, isAccountFollowActivityValid } from './actor'
|
||||||
import { isAnnounceActivityValid } from './announce'
|
import { isAnnounceActivityValid } from './announce'
|
||||||
import { isActivityPubUrlValid } from './misc'
|
import { isActivityPubUrlValid } from './misc'
|
||||||
import { isDislikeActivityValid, isLikeActivityValid } from './rate'
|
import { isDislikeActivityValid, isLikeActivityValid } from './rate'
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
import * as validator from 'validator'
|
||||||
|
import { CONSTRAINTS_FIELDS } from '../../../initializers'
|
||||||
|
import { isAccountNameValid } from '../accounts'
|
||||||
|
import { exists, isUUIDValid } from '../misc'
|
||||||
|
import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
|
||||||
|
|
||||||
|
function isActorEndpointsObjectValid (endpointObject: any) {
|
||||||
|
return isActivityPubUrlValid(endpointObject.sharedInbox)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isActorPublicKeyObjectValid (publicKeyObject: any) {
|
||||||
|
return isActivityPubUrlValid(publicKeyObject.id) &&
|
||||||
|
isActivityPubUrlValid(publicKeyObject.owner) &&
|
||||||
|
isActorPublicKeyValid(publicKeyObject.publicKeyPem)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isActorTypeValid (type: string) {
|
||||||
|
return type === 'Person' || type === 'Application' || type === 'Group'
|
||||||
|
}
|
||||||
|
|
||||||
|
function isActorPublicKeyValid (publicKey: string) {
|
||||||
|
return exists(publicKey) &&
|
||||||
|
typeof publicKey === 'string' &&
|
||||||
|
publicKey.startsWith('-----BEGIN PUBLIC KEY-----') &&
|
||||||
|
publicKey.endsWith('-----END PUBLIC KEY-----') &&
|
||||||
|
validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACTOR.PUBLIC_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isActorPreferredUsernameValid (preferredUsername: string) {
|
||||||
|
return isAccountNameValid(preferredUsername)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isActorPrivateKeyValid (privateKey: string) {
|
||||||
|
return exists(privateKey) &&
|
||||||
|
typeof privateKey === 'string' &&
|
||||||
|
privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----') &&
|
||||||
|
privateKey.endsWith('-----END RSA PRIVATE KEY-----') &&
|
||||||
|
validator.isLength(privateKey, CONSTRAINTS_FIELDS.ACTOR.PRIVATE_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRemoteActorValid (remoteActor: any) {
|
||||||
|
return isActivityPubUrlValid(remoteActor.id) &&
|
||||||
|
isUUIDValid(remoteActor.uuid) &&
|
||||||
|
isActorTypeValid(remoteActor.type) &&
|
||||||
|
isActivityPubUrlValid(remoteActor.following) &&
|
||||||
|
isActivityPubUrlValid(remoteActor.followers) &&
|
||||||
|
isActivityPubUrlValid(remoteActor.inbox) &&
|
||||||
|
isActivityPubUrlValid(remoteActor.outbox) &&
|
||||||
|
isActorPreferredUsernameValid(remoteActor.preferredUsername) &&
|
||||||
|
isActivityPubUrlValid(remoteActor.url) &&
|
||||||
|
isActorPublicKeyObjectValid(remoteActor.publicKey) &&
|
||||||
|
isActorEndpointsObjectValid(remoteActor.endpoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isActorFollowingCountValid (value: string) {
|
||||||
|
return exists(value) && validator.isInt('' + value, { min: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
function isActorFollowersCountValid (value: string) {
|
||||||
|
return exists(value) && validator.isInt('' + value, { min: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
function isActorDeleteActivityValid (activity: any) {
|
||||||
|
return isBaseActivityValid(activity, 'Delete')
|
||||||
|
}
|
||||||
|
|
||||||
|
function isActorFollowActivityValid (activity: any) {
|
||||||
|
return isBaseActivityValid(activity, 'Follow') &&
|
||||||
|
isActivityPubUrlValid(activity.object)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isActorAcceptActivityValid (activity: any) {
|
||||||
|
return isBaseActivityValid(activity, 'Accept')
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
isActorEndpointsObjectValid,
|
||||||
|
isActorPublicKeyObjectValid,
|
||||||
|
isActorTypeValid,
|
||||||
|
isActorPublicKeyValid,
|
||||||
|
isActorPreferredUsernameValid,
|
||||||
|
isActorPrivateKeyValid,
|
||||||
|
isRemoteActorValid,
|
||||||
|
isActorFollowingCountValid,
|
||||||
|
isActorFollowersCountValid,
|
||||||
|
isActorFollowActivityValid,
|
||||||
|
isActorAcceptActivityValid,
|
||||||
|
isActorDeleteActivityValid
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
export * from './account'
|
export * from './actor'
|
||||||
export * from './activity'
|
export * from './activity'
|
||||||
export * from './misc'
|
export * from './misc'
|
||||||
export * from './signature'
|
export * from './signature'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { isAccountFollowActivityValid } from './account'
|
import { isAccountFollowActivityValid } from './actor'
|
||||||
import { isBaseActivityValid } from './misc'
|
import { isBaseActivityValid } from './misc'
|
||||||
import { isDislikeActivityValid, isLikeActivityValid } from './rate'
|
import { isDislikeActivityValid, isLikeActivityValid } from './rate'
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@ const CONSTRAINTS_FIELDS = {
|
||||||
FILE_SIZE: { min: 10 },
|
FILE_SIZE: { min: 10 },
|
||||||
URL: { min: 3, max: 2000 } // Length
|
URL: { min: 3, max: 2000 } // Length
|
||||||
},
|
},
|
||||||
ACCOUNTS: {
|
ACTOR: {
|
||||||
PUBLIC_KEY: { min: 10, max: 5000 }, // Length
|
PUBLIC_KEY: { min: 10, max: 5000 }, // Length
|
||||||
PRIVATE_KEY: { min: 10, max: 5000 }, // Length
|
PRIVATE_KEY: { min: 10, max: 5000 }, // Length
|
||||||
URL: { min: 3, max: 2000 } // Length
|
URL: { min: 3, max: 2000 } // Length
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { createPrivateAndPublicKeys, logger } from '../helpers'
|
||||||
import { CONFIG, sequelizeTypescript } from '../initializers'
|
import { CONFIG, sequelizeTypescript } from '../initializers'
|
||||||
import { AccountModel } from '../models/account/account'
|
import { AccountModel } from '../models/account/account'
|
||||||
import { UserModel } from '../models/account/user'
|
import { UserModel } from '../models/account/user'
|
||||||
|
import { ActorModel } from '../models/activitypub/actor'
|
||||||
import { getAccountActivityPubUrl } from './activitypub'
|
import { getAccountActivityPubUrl } from './activitypub'
|
||||||
import { createVideoChannel } from './video-channel'
|
import { createVideoChannel } from './video-channel'
|
||||||
|
|
||||||
|
@ -27,9 +28,10 @@ async function createUserAccountAndChannel (user: UserModel, validateUser = true
|
||||||
|
|
||||||
// Set account keys, this could be long so process after the account creation and do not block the client
|
// Set account keys, this could be long so process after the account creation and do not block the client
|
||||||
const { publicKey, privateKey } = await createPrivateAndPublicKeys()
|
const { publicKey, privateKey } = await createPrivateAndPublicKeys()
|
||||||
account.set('publicKey', publicKey)
|
const actor = account.Actor
|
||||||
account.set('privateKey', privateKey)
|
actor.set('publicKey', publicKey)
|
||||||
account.save().catch(err => logger.error('Cannot set public/private keys of local account %d.', account.id, err))
|
actor.set('privateKey', privateKey)
|
||||||
|
actor.save().catch(err => logger.error('Cannot set public/private keys of actor %d.', actor.uuid, err))
|
||||||
|
|
||||||
return { account, videoChannel }
|
return { account, videoChannel }
|
||||||
}
|
}
|
||||||
|
@ -37,8 +39,7 @@ async function createUserAccountAndChannel (user: UserModel, validateUser = true
|
||||||
async function createLocalAccountWithoutKeys (name: string, userId: number, applicationId: number, t: Sequelize.Transaction) {
|
async function createLocalAccountWithoutKeys (name: string, userId: number, applicationId: number, t: Sequelize.Transaction) {
|
||||||
const url = getAccountActivityPubUrl(name)
|
const url = getAccountActivityPubUrl(name)
|
||||||
|
|
||||||
const accountInstance = new AccountModel({
|
const actorInstance = new ActorModel({
|
||||||
name,
|
|
||||||
url,
|
url,
|
||||||
publicKey: null,
|
publicKey: null,
|
||||||
privateKey: null,
|
privateKey: null,
|
||||||
|
@ -48,13 +49,22 @@ async function createLocalAccountWithoutKeys (name: string, userId: number, appl
|
||||||
outboxUrl: url + '/outbox',
|
outboxUrl: url + '/outbox',
|
||||||
sharedInboxUrl: CONFIG.WEBSERVER.URL + '/inbox',
|
sharedInboxUrl: CONFIG.WEBSERVER.URL + '/inbox',
|
||||||
followersUrl: url + '/followers',
|
followersUrl: url + '/followers',
|
||||||
followingUrl: url + '/following',
|
followingUrl: url + '/following'
|
||||||
|
})
|
||||||
|
const actorInstanceCreated = await actorInstance.save({ transaction: t })
|
||||||
|
|
||||||
|
const accountInstance = new AccountModel({
|
||||||
|
name,
|
||||||
userId,
|
userId,
|
||||||
applicationId,
|
applicationId,
|
||||||
|
actorId: actorInstanceCreated.id,
|
||||||
serverId: null // It is our server
|
serverId: null // It is our server
|
||||||
})
|
})
|
||||||
|
|
||||||
return accountInstance.save({ transaction: t })
|
const accountInstanceCreated = await accountInstance.save({ transaction: t })
|
||||||
|
accountInstanceCreated.Actor = actorInstanceCreated
|
||||||
|
|
||||||
|
return accountInstanceCreated
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { join } from 'path'
|
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import {
|
import {
|
||||||
AfterDestroy,
|
AfterDestroy,
|
||||||
|
@ -16,24 +15,13 @@ import {
|
||||||
Table,
|
Table,
|
||||||
UpdatedAt
|
UpdatedAt
|
||||||
} from 'sequelize-typescript'
|
} from 'sequelize-typescript'
|
||||||
import { Avatar } from '../../../shared/models/avatars/avatar.model'
|
|
||||||
import { activityPubContextify } from '../../helpers'
|
|
||||||
import {
|
|
||||||
isAccountFollowersCountValid,
|
|
||||||
isAccountFollowingCountValid,
|
|
||||||
isAccountPrivateKeyValid,
|
|
||||||
isAccountPublicKeyValid,
|
|
||||||
isActivityPubUrlValid
|
|
||||||
} from '../../helpers/custom-validators/activitypub'
|
|
||||||
import { isUserUsernameValid } from '../../helpers/custom-validators/users'
|
import { isUserUsernameValid } from '../../helpers/custom-validators/users'
|
||||||
import { AVATARS_DIR, CONFIG, CONSTRAINTS_FIELDS } from '../../initializers'
|
|
||||||
import { sendDeleteAccount } from '../../lib/activitypub/send'
|
import { sendDeleteAccount } from '../../lib/activitypub/send'
|
||||||
|
import { ActorModel } from '../activitypub/actor'
|
||||||
import { ApplicationModel } from '../application/application'
|
import { ApplicationModel } from '../application/application'
|
||||||
import { AvatarModel } from '../avatar/avatar'
|
|
||||||
import { ServerModel } from '../server/server'
|
import { ServerModel } from '../server/server'
|
||||||
import { throwIfNotValid } from '../utils'
|
import { throwIfNotValid } from '../utils'
|
||||||
import { VideoChannelModel } from '../video/video-channel'
|
import { VideoChannelModel } from '../video/video-channel'
|
||||||
import { AccountFollowModel } from './account-follow'
|
|
||||||
import { UserModel } from './user'
|
import { UserModel } from './user'
|
||||||
|
|
||||||
@Table({
|
@Table({
|
||||||
|
@ -59,68 +47,7 @@ import { UserModel } from './user'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AccountModel extends Model<Account> {
|
export class AccountModel extends Model<AccountModel> {
|
||||||
|
|
||||||
@AllowNull(false)
|
|
||||||
@Default(DataType.UUIDV4)
|
|
||||||
@IsUUID(4)
|
|
||||||
@Column(DataType.UUID)
|
|
||||||
uuid: string
|
|
||||||
|
|
||||||
@AllowNull(false)
|
|
||||||
@Is('AccountName', value => throwIfNotValid(value, isUserUsernameValid, 'account name'))
|
|
||||||
@Column
|
|
||||||
name: string
|
|
||||||
|
|
||||||
@AllowNull(false)
|
|
||||||
@Is('AccountUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
|
|
||||||
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
|
|
||||||
url: string
|
|
||||||
|
|
||||||
@AllowNull(true)
|
|
||||||
@Is('AccountPublicKey', value => throwIfNotValid(value, isAccountPublicKeyValid, 'public key'))
|
|
||||||
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY.max))
|
|
||||||
publicKey: string
|
|
||||||
|
|
||||||
@AllowNull(true)
|
|
||||||
@Is('AccountPublicKey', value => throwIfNotValid(value, isAccountPrivateKeyValid, 'private key'))
|
|
||||||
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PRIVATE_KEY.max))
|
|
||||||
privateKey: string
|
|
||||||
|
|
||||||
@AllowNull(false)
|
|
||||||
@Is('AccountFollowersCount', value => throwIfNotValid(value, isAccountFollowersCountValid, 'followers count'))
|
|
||||||
@Column
|
|
||||||
followersCount: number
|
|
||||||
|
|
||||||
@AllowNull(false)
|
|
||||||
@Is('AccountFollowersCount', value => throwIfNotValid(value, isAccountFollowingCountValid, 'following count'))
|
|
||||||
@Column
|
|
||||||
followingCount: number
|
|
||||||
|
|
||||||
@AllowNull(false)
|
|
||||||
@Is('AccountInboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'inbox url'))
|
|
||||||
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
|
|
||||||
inboxUrl: string
|
|
||||||
|
|
||||||
@AllowNull(false)
|
|
||||||
@Is('AccountOutboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'outbox url'))
|
|
||||||
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
|
|
||||||
outboxUrl: string
|
|
||||||
|
|
||||||
@AllowNull(false)
|
|
||||||
@Is('AccountSharedInboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'shared inbox url'))
|
|
||||||
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
|
|
||||||
sharedInboxUrl: string
|
|
||||||
|
|
||||||
@AllowNull(false)
|
|
||||||
@Is('AccountFollowersUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'followers url'))
|
|
||||||
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
|
|
||||||
followersUrl: string
|
|
||||||
|
|
||||||
@AllowNull(false)
|
|
||||||
@Is('AccountFollowingUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'following url'))
|
|
||||||
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
|
|
||||||
followingUrl: string
|
|
||||||
|
|
||||||
@CreatedAt
|
@CreatedAt
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
@ -128,29 +55,17 @@ export class AccountModel extends Model<Account> {
|
||||||
@UpdatedAt
|
@UpdatedAt
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
|
|
||||||
@ForeignKey(() => AvatarModel)
|
@ForeignKey(() => ActorModel)
|
||||||
@Column
|
@Column
|
||||||
avatarId: number
|
actorId: number
|
||||||
|
|
||||||
@BelongsTo(() => AvatarModel, {
|
@BelongsTo(() => ActorModel, {
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
allowNull: true
|
allowNull: false
|
||||||
},
|
},
|
||||||
onDelete: 'cascade'
|
onDelete: 'cascade'
|
||||||
})
|
})
|
||||||
Avatar: AvatarModel
|
Actor: ActorModel
|
||||||
|
|
||||||
@ForeignKey(() => ServerModel)
|
|
||||||
@Column
|
|
||||||
serverId: number
|
|
||||||
|
|
||||||
@BelongsTo(() => ServerModel, {
|
|
||||||
foreignKey: {
|
|
||||||
allowNull: true
|
|
||||||
},
|
|
||||||
onDelete: 'cascade'
|
|
||||||
})
|
|
||||||
Server: ServerModel
|
|
||||||
|
|
||||||
@ForeignKey(() => UserModel)
|
@ForeignKey(() => UserModel)
|
||||||
@Column
|
@Column
|
||||||
|
@ -185,25 +100,6 @@ export class AccountModel extends Model<Account> {
|
||||||
})
|
})
|
||||||
VideoChannels: VideoChannelModel[]
|
VideoChannels: VideoChannelModel[]
|
||||||
|
|
||||||
@HasMany(() => AccountFollowModel, {
|
|
||||||
foreignKey: {
|
|
||||||
name: 'accountId',
|
|
||||||
allowNull: false
|
|
||||||
},
|
|
||||||
onDelete: 'cascade'
|
|
||||||
})
|
|
||||||
AccountFollowing: AccountFollowModel[]
|
|
||||||
|
|
||||||
@HasMany(() => AccountFollowModel, {
|
|
||||||
foreignKey: {
|
|
||||||
name: 'targetAccountId',
|
|
||||||
allowNull: false
|
|
||||||
},
|
|
||||||
as: 'followers',
|
|
||||||
onDelete: 'cascade'
|
|
||||||
})
|
|
||||||
AccountFollowers: AccountFollowModel[]
|
|
||||||
|
|
||||||
@AfterDestroy
|
@AfterDestroy
|
||||||
static sendDeleteIfOwned (instance: AccountModel) {
|
static sendDeleteIfOwned (instance: AccountModel) {
|
||||||
if (instance.isOwned()) {
|
if (instance.isOwned()) {
|
||||||
|
@ -281,9 +177,15 @@ export class AccountModel extends Model<Account> {
|
||||||
|
|
||||||
static loadByUrl (url: string, transaction?: Sequelize.Transaction) {
|
static loadByUrl (url: string, transaction?: Sequelize.Transaction) {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
include: [
|
||||||
url
|
{
|
||||||
},
|
model: ActorModel,
|
||||||
|
required: true,
|
||||||
|
where: {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
transaction
|
transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,11 +194,17 @@ export class AccountModel extends Model<Account> {
|
||||||
|
|
||||||
static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) {
|
static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
include: [
|
||||||
followersUrl: {
|
{
|
||||||
[ Sequelize.Op.in ]: followersUrls
|
model: ActorModel,
|
||||||
|
required: true,
|
||||||
|
where: {
|
||||||
|
followersUrl: {
|
||||||
|
[ Sequelize.Op.in ]: followersUrls
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
transaction
|
transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,97 +212,21 @@ export class AccountModel extends Model<Account> {
|
||||||
}
|
}
|
||||||
|
|
||||||
toFormattedJSON () {
|
toFormattedJSON () {
|
||||||
let host = CONFIG.WEBSERVER.HOST
|
const actor = this.Actor.toFormattedJSON()
|
||||||
let score: number
|
const account = {
|
||||||
let avatar: Avatar = null
|
|
||||||
|
|
||||||
if (this.Avatar) {
|
|
||||||
avatar = {
|
|
||||||
path: join(AVATARS_DIR.ACCOUNT, this.Avatar.filename),
|
|
||||||
createdAt: this.Avatar.createdAt,
|
|
||||||
updatedAt: this.Avatar.updatedAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.Server) {
|
|
||||||
host = this.Server.host
|
|
||||||
score = this.Server.score
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: this.id,
|
id: this.id,
|
||||||
uuid: this.uuid,
|
|
||||||
host,
|
|
||||||
score,
|
|
||||||
name: this.name,
|
|
||||||
followingCount: this.followingCount,
|
|
||||||
followersCount: this.followersCount,
|
|
||||||
createdAt: this.createdAt,
|
createdAt: this.createdAt,
|
||||||
updatedAt: this.updatedAt,
|
updatedAt: this.updatedAt
|
||||||
avatar
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Object.assign(actor, account)
|
||||||
}
|
}
|
||||||
|
|
||||||
toActivityPubObject () {
|
toActivityPubObject () {
|
||||||
const type = this.serverId ? 'Application' as 'Application' : 'Person' as 'Person'
|
return this.Actor.toActivityPubObject(this.name, this.uuid, 'Account')
|
||||||
|
|
||||||
const json = {
|
|
||||||
type,
|
|
||||||
id: this.url,
|
|
||||||
following: this.getFollowingUrl(),
|
|
||||||
followers: this.getFollowersUrl(),
|
|
||||||
inbox: this.inboxUrl,
|
|
||||||
outbox: this.outboxUrl,
|
|
||||||
preferredUsername: this.name,
|
|
||||||
url: this.url,
|
|
||||||
name: this.name,
|
|
||||||
endpoints: {
|
|
||||||
sharedInbox: this.sharedInboxUrl
|
|
||||||
},
|
|
||||||
uuid: this.uuid,
|
|
||||||
publicKey: {
|
|
||||||
id: this.getPublicKeyUrl(),
|
|
||||||
owner: this.url,
|
|
||||||
publicKeyPem: this.publicKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return activityPubContextify(json)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isOwned () {
|
isOwned () {
|
||||||
return this.serverId === null
|
return this.Actor.isOwned()
|
||||||
}
|
|
||||||
|
|
||||||
getFollowerSharedInboxUrls (t: Sequelize.Transaction) {
|
|
||||||
const query = {
|
|
||||||
attributes: [ 'sharedInboxUrl' ],
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
model: AccountFollowModel,
|
|
||||||
required: true,
|
|
||||||
as: 'followers',
|
|
||||||
where: {
|
|
||||||
targetAccountId: this.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
transaction: t
|
|
||||||
}
|
|
||||||
|
|
||||||
return AccountModel.findAll(query)
|
|
||||||
.then(accounts => accounts.map(a => a.sharedInboxUrl))
|
|
||||||
}
|
|
||||||
|
|
||||||
getFollowingUrl () {
|
|
||||||
return this.url + '/following'
|
|
||||||
}
|
|
||||||
|
|
||||||
getFollowersUrl () {
|
|
||||||
return this.url + '/followers'
|
|
||||||
}
|
|
||||||
|
|
||||||
getPublicKeyUrl () {
|
|
||||||
return this.url + '#main-key'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,245 @@
|
||||||
|
import { join } from 'path'
|
||||||
|
import * as Sequelize from 'sequelize'
|
||||||
|
import {
|
||||||
|
AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, HasMany, Is, IsUUID, Model, Table,
|
||||||
|
UpdatedAt
|
||||||
|
} from 'sequelize-typescript'
|
||||||
|
import { Avatar } from '../../../shared/models/avatars/avatar.model'
|
||||||
|
import { activityPubContextify } from '../../helpers'
|
||||||
|
import {
|
||||||
|
isActivityPubUrlValid,
|
||||||
|
isActorFollowersCountValid,
|
||||||
|
isActorFollowingCountValid, isActorPreferredUsernameValid,
|
||||||
|
isActorPrivateKeyValid,
|
||||||
|
isActorPublicKeyValid
|
||||||
|
} from '../../helpers/custom-validators/activitypub'
|
||||||
|
import { isUserUsernameValid } from '../../helpers/custom-validators/users'
|
||||||
|
import { AVATARS_DIR, CONFIG, CONSTRAINTS_FIELDS } from '../../initializers'
|
||||||
|
import { AccountFollowModel } from '../account/account-follow'
|
||||||
|
import { AvatarModel } from '../avatar/avatar'
|
||||||
|
import { ServerModel } from '../server/server'
|
||||||
|
import { throwIfNotValid } from '../utils'
|
||||||
|
|
||||||
|
@Table({
|
||||||
|
tableName: 'actor'
|
||||||
|
})
|
||||||
|
export class ActorModel extends Model<ActorModel> {
|
||||||
|
|
||||||
|
@AllowNull(false)
|
||||||
|
@Default(DataType.UUIDV4)
|
||||||
|
@IsUUID(4)
|
||||||
|
@Column(DataType.UUID)
|
||||||
|
uuid: string
|
||||||
|
|
||||||
|
@AllowNull(false)
|
||||||
|
@Is('ActorName', value => throwIfNotValid(value, isActorPreferredUsernameValid, 'actor name'))
|
||||||
|
@Column
|
||||||
|
name: string
|
||||||
|
|
||||||
|
@AllowNull(false)
|
||||||
|
@Is('ActorUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
|
||||||
|
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTOR.URL.max))
|
||||||
|
url: string
|
||||||
|
|
||||||
|
@AllowNull(true)
|
||||||
|
@Is('ActorPublicKey', value => throwIfNotValid(value, isActorPublicKeyValid, 'public key'))
|
||||||
|
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTOR.PUBLIC_KEY.max))
|
||||||
|
publicKey: string
|
||||||
|
|
||||||
|
@AllowNull(true)
|
||||||
|
@Is('ActorPublicKey', value => throwIfNotValid(value, isActorPrivateKeyValid, 'private key'))
|
||||||
|
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTOR.PRIVATE_KEY.max))
|
||||||
|
privateKey: string
|
||||||
|
|
||||||
|
@AllowNull(false)
|
||||||
|
@Is('ActorFollowersCount', value => throwIfNotValid(value, isActorFollowersCountValid, 'followers count'))
|
||||||
|
@Column
|
||||||
|
followersCount: number
|
||||||
|
|
||||||
|
@AllowNull(false)
|
||||||
|
@Is('ActorFollowersCount', value => throwIfNotValid(value, isActorFollowingCountValid, 'following count'))
|
||||||
|
@Column
|
||||||
|
followingCount: number
|
||||||
|
|
||||||
|
@AllowNull(false)
|
||||||
|
@Is('ActorInboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'inbox url'))
|
||||||
|
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTOR.URL.max))
|
||||||
|
inboxUrl: string
|
||||||
|
|
||||||
|
@AllowNull(false)
|
||||||
|
@Is('ActorOutboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'outbox url'))
|
||||||
|
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTOR.URL.max))
|
||||||
|
outboxUrl: string
|
||||||
|
|
||||||
|
@AllowNull(false)
|
||||||
|
@Is('ActorSharedInboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'shared inbox url'))
|
||||||
|
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTOR.URL.max))
|
||||||
|
sharedInboxUrl: string
|
||||||
|
|
||||||
|
@AllowNull(false)
|
||||||
|
@Is('ActorFollowersUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'followers url'))
|
||||||
|
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTOR.URL.max))
|
||||||
|
followersUrl: string
|
||||||
|
|
||||||
|
@AllowNull(false)
|
||||||
|
@Is('ActorFollowingUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'following url'))
|
||||||
|
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTOR.URL.max))
|
||||||
|
followingUrl: string
|
||||||
|
|
||||||
|
@CreatedAt
|
||||||
|
createdAt: Date
|
||||||
|
|
||||||
|
@UpdatedAt
|
||||||
|
updatedAt: Date
|
||||||
|
|
||||||
|
@ForeignKey(() => AvatarModel)
|
||||||
|
@Column
|
||||||
|
avatarId: number
|
||||||
|
|
||||||
|
@BelongsTo(() => AvatarModel, {
|
||||||
|
foreignKey: {
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
onDelete: 'cascade'
|
||||||
|
})
|
||||||
|
Avatar: AvatarModel
|
||||||
|
|
||||||
|
@HasMany(() => AccountFollowModel, {
|
||||||
|
foreignKey: {
|
||||||
|
name: 'accountId',
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
onDelete: 'cascade'
|
||||||
|
})
|
||||||
|
AccountFollowing: AccountFollowModel[]
|
||||||
|
|
||||||
|
@HasMany(() => AccountFollowModel, {
|
||||||
|
foreignKey: {
|
||||||
|
name: 'targetAccountId',
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
as: 'followers',
|
||||||
|
onDelete: 'cascade'
|
||||||
|
})
|
||||||
|
AccountFollowers: AccountFollowModel[]
|
||||||
|
|
||||||
|
@ForeignKey(() => ServerModel)
|
||||||
|
@Column
|
||||||
|
serverId: number
|
||||||
|
|
||||||
|
@BelongsTo(() => ServerModel, {
|
||||||
|
foreignKey: {
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
onDelete: 'cascade'
|
||||||
|
})
|
||||||
|
Server: ServerModel
|
||||||
|
|
||||||
|
static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) {
|
||||||
|
const query = {
|
||||||
|
where: {
|
||||||
|
followersUrl: {
|
||||||
|
[ Sequelize.Op.in ]: followersUrls
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActorModel.findAll(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
toFormattedJSON () {
|
||||||
|
let avatar: Avatar = null
|
||||||
|
if (this.Avatar) {
|
||||||
|
avatar = {
|
||||||
|
path: join(AVATARS_DIR.ACCOUNT, this.Avatar.filename),
|
||||||
|
createdAt: this.Avatar.createdAt,
|
||||||
|
updatedAt: this.Avatar.updatedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let host = CONFIG.WEBSERVER.HOST
|
||||||
|
let score: number
|
||||||
|
if (this.Server) {
|
||||||
|
host = this.Server.host
|
||||||
|
score = this.Server.score
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
host,
|
||||||
|
score,
|
||||||
|
followingCount: this.followingCount,
|
||||||
|
followersCount: this.followersCount,
|
||||||
|
avatar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toActivityPubObject (name: string, uuid: string, type: 'Account' | 'VideoChannel') {
|
||||||
|
let activityPubType
|
||||||
|
if (type === 'Account') {
|
||||||
|
activityPubType = this.serverId ? 'Application' as 'Application' : 'Person' as 'Person'
|
||||||
|
} else { // VideoChannel
|
||||||
|
activityPubType = 'Group'
|
||||||
|
}
|
||||||
|
|
||||||
|
const json = {
|
||||||
|
type,
|
||||||
|
id: this.url,
|
||||||
|
following: this.getFollowingUrl(),
|
||||||
|
followers: this.getFollowersUrl(),
|
||||||
|
inbox: this.inboxUrl,
|
||||||
|
outbox: this.outboxUrl,
|
||||||
|
preferredUsername: name,
|
||||||
|
url: this.url,
|
||||||
|
name,
|
||||||
|
endpoints: {
|
||||||
|
sharedInbox: this.sharedInboxUrl
|
||||||
|
},
|
||||||
|
uuid,
|
||||||
|
publicKey: {
|
||||||
|
id: this.getPublicKeyUrl(),
|
||||||
|
owner: this.url,
|
||||||
|
publicKeyPem: this.publicKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return activityPubContextify(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
getFollowerSharedInboxUrls (t: Sequelize.Transaction) {
|
||||||
|
const query = {
|
||||||
|
attributes: [ 'sharedInboxUrl' ],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: AccountFollowModel,
|
||||||
|
required: true,
|
||||||
|
as: 'followers',
|
||||||
|
where: {
|
||||||
|
targetAccountId: this.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
transaction: t
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActorModel.findAll(query)
|
||||||
|
.then(accounts => accounts.map(a => a.sharedInboxUrl))
|
||||||
|
}
|
||||||
|
|
||||||
|
getFollowingUrl () {
|
||||||
|
return this.url + '/following'
|
||||||
|
}
|
||||||
|
|
||||||
|
getFollowersUrl () {
|
||||||
|
return this.url + '/followers'
|
||||||
|
}
|
||||||
|
|
||||||
|
getPublicKeyUrl () {
|
||||||
|
return this.url + '#main-key'
|
||||||
|
}
|
||||||
|
|
||||||
|
isOwned () {
|
||||||
|
return this.serverId === null
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,18 +11,16 @@ import {
|
||||||
HasMany,
|
HasMany,
|
||||||
Is,
|
Is,
|
||||||
IsUUID,
|
IsUUID,
|
||||||
Model, Scopes,
|
Model,
|
||||||
|
Scopes,
|
||||||
Table,
|
Table,
|
||||||
UpdatedAt
|
UpdatedAt
|
||||||
} from 'sequelize-typescript'
|
} from 'sequelize-typescript'
|
||||||
import { IFindOptions } from 'sequelize-typescript/lib/interfaces/IFindOptions'
|
import { IFindOptions } from 'sequelize-typescript/lib/interfaces/IFindOptions'
|
||||||
import { activityPubCollection } from '../../helpers'
|
|
||||||
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub'
|
|
||||||
import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels'
|
import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels'
|
||||||
import { CONSTRAINTS_FIELDS } from '../../initializers'
|
|
||||||
import { getAnnounceActivityPubUrl } from '../../lib/activitypub'
|
|
||||||
import { sendDeleteVideoChannel } from '../../lib/activitypub/send'
|
import { sendDeleteVideoChannel } from '../../lib/activitypub/send'
|
||||||
import { AccountModel } from '../account/account'
|
import { AccountModel } from '../account/account'
|
||||||
|
import { ActorModel } from '../activitypub/actor'
|
||||||
import { ServerModel } from '../server/server'
|
import { ServerModel } from '../server/server'
|
||||||
import { getSort, throwIfNotValid } from '../utils'
|
import { getSort, throwIfNotValid } from '../utils'
|
||||||
import { VideoModel } from './video'
|
import { VideoModel } from './video'
|
||||||
|
@ -78,17 +76,24 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
|
||||||
@Column
|
@Column
|
||||||
remote: boolean
|
remote: boolean
|
||||||
|
|
||||||
@AllowNull(false)
|
|
||||||
@Is('VideoChannelUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
|
|
||||||
@Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.URL.max))
|
|
||||||
url: string
|
|
||||||
|
|
||||||
@CreatedAt
|
@CreatedAt
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
|
||||||
@UpdatedAt
|
@UpdatedAt
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
|
|
||||||
|
@ForeignKey(() => ActorModel)
|
||||||
|
@Column
|
||||||
|
actorId: number
|
||||||
|
|
||||||
|
@BelongsTo(() => ActorModel, {
|
||||||
|
foreignKey: {
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
onDelete: 'cascade'
|
||||||
|
})
|
||||||
|
Actor: ActorModel
|
||||||
|
|
||||||
@ForeignKey(() => AccountModel)
|
@ForeignKey(() => AccountModel)
|
||||||
@Column
|
@Column
|
||||||
accountId: number
|
accountId: number
|
||||||
|
@ -174,9 +179,15 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
|
||||||
|
|
||||||
static loadByUrl (url: string, t?: Sequelize.Transaction) {
|
static loadByUrl (url: string, t?: Sequelize.Transaction) {
|
||||||
const query: IFindOptions<VideoChannelModel> = {
|
const query: IFindOptions<VideoChannelModel> = {
|
||||||
where: {
|
include: [
|
||||||
url
|
{
|
||||||
}
|
model: ActorModel,
|
||||||
|
required: true,
|
||||||
|
where: {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t !== undefined) query.transaction = t
|
if (t !== undefined) query.transaction = t
|
||||||
|
@ -264,27 +275,6 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
|
||||||
}
|
}
|
||||||
|
|
||||||
toActivityPubObject () {
|
toActivityPubObject () {
|
||||||
let sharesObject
|
return this.Actor.toActivityPubObject(this.name, this.uuid, 'VideoChannel')
|
||||||
if (Array.isArray(this.VideoChannelShares)) {
|
|
||||||
const shares: string[] = []
|
|
||||||
|
|
||||||
for (const videoChannelShare of this.VideoChannelShares) {
|
|
||||||
const shareUrl = getAnnounceActivityPubUrl(this.url, videoChannelShare.Account)
|
|
||||||
shares.push(shareUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
sharesObject = activityPubCollection(shares)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'VideoChannel' as 'VideoChannel',
|
|
||||||
id: this.url,
|
|
||||||
uuid: this.uuid,
|
|
||||||
content: this.description,
|
|
||||||
name: this.name,
|
|
||||||
published: this.createdAt.toISOString(),
|
|
||||||
updated: this.updatedAt.toISOString(),
|
|
||||||
shares: sharesObject
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export interface ActivityPubActor {
|
export interface ActivityPubActor {
|
||||||
'@context': any[]
|
'@context': any[]
|
||||||
type: 'Person' | 'Application'
|
type: 'Person' | 'Application' | 'Group'
|
||||||
id: string
|
id: string
|
||||||
following: string
|
following: string
|
||||||
followers: string
|
followers: string
|
||||||
|
|
Loading…
Reference in New Issue