validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACTORS.PUBLIC_KEY)
}
-const actorNameAlphabet = '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\-_.]'
+const actorNameAlphabet = '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\-_.:]'
const actorNameRegExp = new RegExp(`^${actorNameAlphabet}+$`)
function isActorPreferredUsernameValid (preferredUsername: string) {
return exists(preferredUsername) && validator.matches(preferredUsername, actorNameRegExp)
return exists(actor) &&
isActivityPubUrlValid(actor.id) &&
isActorTypeValid(actor.type) &&
- isActivityPubUrlValid(actor.following) &&
- isActivityPubUrlValid(actor.followers) &&
isActivityPubUrlValid(actor.inbox) &&
- isActivityPubUrlValid(actor.outbox) &&
isActorPreferredUsernameValid(actor.preferredUsername) &&
isActivityPubUrlValid(actor.url) &&
isActorPublicKeyObjectValid(actor.publicKey) &&
isActorEndpointsObjectValid(actor.endpoints) &&
- setValidAttributedTo(actor) &&
- // If this is not an account, it should be attributed to an account
+ (!actor.outbox || isActivityPubUrlValid(actor.outbox)) &&
+ (!actor.following || isActivityPubUrlValid(actor.following)) &&
+ (!actor.followers || isActivityPubUrlValid(actor.followers)) &&
+
+ setValidAttributedTo(actor) &&
+ // If this is a group (a channel), it should be attributed to an account
// In PeerTube we use this to attach a video channel to a specific account
- (actor.type === 'Person' || actor.attributedTo.length !== 0)
+ (actor.type !== 'Group' || actor.attributedTo.length !== 0)
}
function isActorFollowingCountValid (value: string) {
// ---------------------------------------------------------------------------
-const LAST_MIGRATION_VERSION = 420
+const LAST_MIGRATION_VERSION = 425
// ---------------------------------------------------------------------------
--- /dev/null
+import * as Sequelize from 'sequelize'
+
+async function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize,
+ db: any
+}): Promise<void> {
+ const data = {
+ type: Sequelize.STRING,
+ allowNull: true
+ }
+
+ await utils.queryInterface.changeColumn('actor', 'outboxUrl', data)
+ await utils.queryInterface.changeColumn('actor', 'followersUrl', data)
+ await utils.queryInterface.changeColumn('actor', 'followingUrl', data)
+}
+
+function down (options) {
+ throw new Error('Not implemented.')
+}
+
+export {
+ up,
+ down
+}
async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: MActorSignature) {
const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject)
- logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object))
-
const account = byActor.Account
if (!account) throw new Error('Cannot create video abuse with the non account actor ' + byActor.url)
- const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: flag.object })
+ const objects = Array.isArray(flag.object) ? flag.object : [ flag.object ]
- const videoAbuse = await sequelizeTypescript.transaction(async t => {
- const videoAbuseData = {
- reporterAccountId: account.id,
- reason: flag.content,
- videoId: video.id,
- state: VideoAbuseState.PENDING
- }
+ for (const object of objects) {
+ try {
+ logger.debug('Reporting remote abuse for video %s.', getAPId(object))
+
+ const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: object })
- const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) as MVideoAbuseVideo
- videoAbuseInstance.Video = video
+ const videoAbuse = await sequelizeTypescript.transaction(async t => {
+ const videoAbuseData = {
+ reporterAccountId: account.id,
+ reason: flag.content,
+ videoId: video.id,
+ state: VideoAbuseState.PENDING
+ }
- logger.info('Remote abuse for video uuid %s created', flag.object)
+ const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) as MVideoAbuseVideo
+ videoAbuseInstance.Video = video
- return videoAbuseInstance
- })
+ logger.info('Remote abuse for video uuid %s created', flag.object)
- Notifier.Instance.notifyOnNewVideoAbuse(videoAbuse)
+ return videoAbuseInstance
+ })
+
+ Notifier.Instance.notifyOnNewVideoAbuse(videoAbuse)
+ } catch (err) {
+ logger.debug('Cannot process report of %s. (Maybe not a video abuse).', getAPId(object), { err })
+ }
+ }
}
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max))
inboxUrl: string
- @AllowNull(false)
- @Is('ActorOutboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'outbox url'))
+ @AllowNull(true)
+ @Is('ActorOutboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'outbox url', true))
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max))
outboxUrl: string
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max))
sharedInboxUrl: string
- @AllowNull(false)
- @Is('ActorFollowersUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'followers url'))
+ @AllowNull(true)
+ @Is('ActorFollowersUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'followers url', true))
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max))
followersUrl: string
- @AllowNull(false)
- @Is('ActorFollowingUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'following url'))
+ @AllowNull(true)
+ @Is('ActorFollowingUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'following url', true))
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max))
followingUrl: string
export interface ActivityFlag extends BaseActivity {
type: 'Flag',
content: string,
- object: APObject
+ object: APObject | APObject[]
}
export interface VideoAbuseObject {
type: 'Flag',
content: string
- object: string
+ object: string | string[]
}