mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-02-25 18:55:32 -06:00
Add rejected state to follows
Prevent reprocessing already rejected follows
This commit is contained in:
parent
0f58b11f5c
commit
927fa4b11f
@ -1,5 +1,6 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { getServerActor } from '@server/models/application/application'
|
import { getServerActor } from '@server/models/application/application'
|
||||||
|
import { ServerFollowCreate } from '@shared/models'
|
||||||
import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
|
import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
|
||||||
import { UserRight } from '../../../../shared/models/users'
|
import { UserRight } from '../../../../shared/models/users'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
@ -20,16 +21,16 @@ import {
|
|||||||
setDefaultSort
|
setDefaultSort
|
||||||
} from '../../../middlewares'
|
} from '../../../middlewares'
|
||||||
import {
|
import {
|
||||||
acceptOrRejectFollowerValidator,
|
acceptFollowerValidator,
|
||||||
instanceFollowersSortValidator,
|
|
||||||
instanceFollowingSortValidator,
|
|
||||||
followValidator,
|
followValidator,
|
||||||
getFollowerValidator,
|
getFollowerValidator,
|
||||||
|
instanceFollowersSortValidator,
|
||||||
|
instanceFollowingSortValidator,
|
||||||
listFollowsValidator,
|
listFollowsValidator,
|
||||||
|
rejectFollowerValidator,
|
||||||
removeFollowingValidator
|
removeFollowingValidator
|
||||||
} from '../../../middlewares/validators'
|
} from '../../../middlewares/validators'
|
||||||
import { ActorFollowModel } from '../../../models/actor/actor-follow'
|
import { ActorFollowModel } from '../../../models/actor/actor-follow'
|
||||||
import { ServerFollowCreate } from '@shared/models'
|
|
||||||
|
|
||||||
const serverFollowsRouter = express.Router()
|
const serverFollowsRouter = express.Router()
|
||||||
serverFollowsRouter.get('/following',
|
serverFollowsRouter.get('/following',
|
||||||
@ -69,22 +70,22 @@ serverFollowsRouter.delete('/followers/:nameWithHost',
|
|||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
|
ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
|
||||||
asyncMiddleware(getFollowerValidator),
|
asyncMiddleware(getFollowerValidator),
|
||||||
asyncMiddleware(removeOrRejectFollower)
|
asyncMiddleware(removeFollower)
|
||||||
)
|
)
|
||||||
|
|
||||||
serverFollowsRouter.post('/followers/:nameWithHost/reject',
|
serverFollowsRouter.post('/followers/:nameWithHost/reject',
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
|
ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
|
||||||
asyncMiddleware(getFollowerValidator),
|
asyncMiddleware(getFollowerValidator),
|
||||||
acceptOrRejectFollowerValidator,
|
rejectFollowerValidator,
|
||||||
asyncMiddleware(removeOrRejectFollower)
|
asyncMiddleware(rejectFollower)
|
||||||
)
|
)
|
||||||
|
|
||||||
serverFollowsRouter.post('/followers/:nameWithHost/accept',
|
serverFollowsRouter.post('/followers/:nameWithHost/accept',
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
|
ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
|
||||||
asyncMiddleware(getFollowerValidator),
|
asyncMiddleware(getFollowerValidator),
|
||||||
acceptOrRejectFollowerValidator,
|
acceptFollowerValidator,
|
||||||
asyncMiddleware(acceptFollower)
|
asyncMiddleware(acceptFollower)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -176,10 +177,23 @@ async function removeFollowing (req: express.Request, res: express.Response) {
|
|||||||
return res.status(HttpStatusCode.NO_CONTENT_204).end()
|
return res.status(HttpStatusCode.NO_CONTENT_204).end()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeOrRejectFollower (req: express.Request, res: express.Response) {
|
async function rejectFollower (req: express.Request, res: express.Response) {
|
||||||
const follow = res.locals.follow
|
const follow = res.locals.follow
|
||||||
|
|
||||||
await sendReject(follow.url, follow.ActorFollower, follow.ActorFollowing)
|
follow.state = 'rejected'
|
||||||
|
await follow.save()
|
||||||
|
|
||||||
|
sendReject(follow.url, follow.ActorFollower, follow.ActorFollowing)
|
||||||
|
|
||||||
|
return res.status(HttpStatusCode.NO_CONTENT_204).end()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeFollower (req: express.Request, res: express.Response) {
|
||||||
|
const follow = res.locals.follow
|
||||||
|
|
||||||
|
if (follow.state === 'accepted' || follow.state === 'pending') {
|
||||||
|
sendReject(follow.url, follow.ActorFollower, follow.ActorFollowing)
|
||||||
|
}
|
||||||
|
|
||||||
await follow.destroy()
|
await follow.destroy()
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { FollowState } from '@shared/models'
|
|||||||
function isFollowStateValid (value: FollowState) {
|
function isFollowStateValid (value: FollowState) {
|
||||||
if (!exists(value)) return false
|
if (!exists(value)) return false
|
||||||
|
|
||||||
return value === 'pending' || value === 'accepted'
|
return value === 'pending' || value === 'accepted' || value === 'rejected'
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRemoteHandleValid (value: string) {
|
function isRemoteHandleValid (value: string) {
|
||||||
|
@ -129,7 +129,8 @@ const ACTOR_FOLLOW_SCORE = {
|
|||||||
|
|
||||||
const FOLLOW_STATES: { [ id: string ]: FollowState } = {
|
const FOLLOW_STATES: { [ id: string ]: FollowState } = {
|
||||||
PENDING: 'pending',
|
PENDING: 'pending',
|
||||||
ACCEPTED: 'accepted'
|
ACCEPTED: 'accepted',
|
||||||
|
REJECTED: 'rejected'
|
||||||
}
|
}
|
||||||
|
|
||||||
const REMOTE_SCHEME = {
|
const REMOTE_SCHEME = {
|
||||||
|
@ -58,6 +58,11 @@ async function processFollow (byActor: MActorSignature, activityId: string, targ
|
|||||||
transaction: t
|
transaction: t
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Already rejected
|
||||||
|
if (actorFollow.state === 'rejected') {
|
||||||
|
return { actorFollow: undefined as MActorFollowActors }
|
||||||
|
}
|
||||||
|
|
||||||
// Set the follow as accepted if the remote actor follows a channel or account
|
// Set the follow as accepted if the remote actor follows a channel or account
|
||||||
// Or if the instance automatically accepts followers
|
// Or if the instance automatically accepts followers
|
||||||
if (actorFollow.state !== 'accepted' && (isFollowingInstance === false || CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false)) {
|
if (actorFollow.state !== 'accepted' && (isFollowingInstance === false || CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false)) {
|
||||||
|
@ -25,7 +25,8 @@ async function processReject (follower: MActor, targetActor: MActor) {
|
|||||||
|
|
||||||
if (!actorFollow) throw new Error(`'Unknown actor follow ${follower.id} -> ${targetActor.id}.`)
|
if (!actorFollow) throw new Error(`'Unknown actor follow ${follower.id} -> ${targetActor.id}.`)
|
||||||
|
|
||||||
await actorFollow.destroy({ transaction: t })
|
actorFollow.state = 'rejected'
|
||||||
|
await actorFollow.save({ transaction: t })
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
})
|
})
|
||||||
|
@ -81,7 +81,11 @@ const removeFollowingValidator = [
|
|||||||
const serverActor = await getServerActor()
|
const serverActor = await getServerActor()
|
||||||
|
|
||||||
const { name, host } = getRemoteNameAndHost(req.params.hostOrHandle)
|
const { name, host } = getRemoteNameAndHost(req.params.hostOrHandle)
|
||||||
const follow = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI(serverActor.id, name, host)
|
const follow = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI({
|
||||||
|
actorId: serverActor.id,
|
||||||
|
targetName: name,
|
||||||
|
targetHost: host
|
||||||
|
})
|
||||||
|
|
||||||
if (!follow) {
|
if (!follow) {
|
||||||
return res.fail({
|
return res.fail({
|
||||||
@ -126,13 +130,26 @@ const getFollowerValidator = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const acceptOrRejectFollowerValidator = [
|
const acceptFollowerValidator = [
|
||||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
logger.debug('Checking accept/reject follower parameters', { parameters: req.params })
|
logger.debug('Checking accept follower parameters', { parameters: req.params })
|
||||||
|
|
||||||
const follow = res.locals.follow
|
const follow = res.locals.follow
|
||||||
if (follow.state !== 'pending') {
|
if (follow.state !== 'pending' && follow.state !== 'rejected') {
|
||||||
return res.fail({ message: 'Follow is not in pending state.' })
|
return res.fail({ message: 'Follow is not in pending/rejected state.' })
|
||||||
|
}
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const rejectFollowerValidator = [
|
||||||
|
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
|
logger.debug('Checking reject follower parameters', { parameters: req.params })
|
||||||
|
|
||||||
|
const follow = res.locals.follow
|
||||||
|
if (follow.state !== 'pending' && follow.state !== 'accepted') {
|
||||||
|
return res.fail({ message: 'Follow is not in pending/accepted state.' })
|
||||||
}
|
}
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
@ -145,6 +162,7 @@ export {
|
|||||||
followValidator,
|
followValidator,
|
||||||
removeFollowingValidator,
|
removeFollowingValidator,
|
||||||
getFollowerValidator,
|
getFollowerValidator,
|
||||||
acceptOrRejectFollowerValidator,
|
acceptFollowerValidator,
|
||||||
|
rejectFollowerValidator,
|
||||||
listFollowsValidator
|
listFollowsValidator
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,12 @@ const userSubscriptionGetValidator = [
|
|||||||
if (host === WEBSERVER.HOST) host = null
|
if (host === WEBSERVER.HOST) host = null
|
||||||
|
|
||||||
const user = res.locals.oauth.token.User
|
const user = res.locals.oauth.token.User
|
||||||
const subscription = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI(user.Account.Actor.id, name, host)
|
const subscription = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI({
|
||||||
|
actorId: user.Account.Actor.id,
|
||||||
|
targetName: name,
|
||||||
|
targetHost: host,
|
||||||
|
state: 'accepted'
|
||||||
|
})
|
||||||
|
|
||||||
if (!subscription || !subscription.ActorFollowing.VideoChannel) {
|
if (!subscription || !subscription.ActorFollowing.VideoChannel) {
|
||||||
return res.fail({
|
return res.fail({
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { difference, values } from 'lodash'
|
import { difference, values } from 'lodash'
|
||||||
import { Includeable, IncludeOptions, Op, QueryTypes, Transaction } from 'sequelize'
|
import { Attributes, FindOptions, Includeable, IncludeOptions, Op, QueryTypes, Transaction, WhereAttributeHash } from 'sequelize'
|
||||||
import {
|
import {
|
||||||
AfterCreate,
|
AfterCreate,
|
||||||
AfterDestroy,
|
AfterDestroy,
|
||||||
@ -209,7 +209,9 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
|
|||||||
}
|
}
|
||||||
|
|
||||||
static isFollowedBy (actorId: number, followerActorId: number) {
|
static isFollowedBy (actorId: number, followerActorId: number) {
|
||||||
const query = 'SELECT 1 FROM "actorFollow" WHERE "actorId" = $followerActorId AND "targetActorId" = $actorId LIMIT 1'
|
const query = `SELECT 1 FROM "actorFollow" ` +
|
||||||
|
`WHERE "actorId" = $followerActorId AND "targetActorId" = $actorId AND "state" = 'accepted' ` +
|
||||||
|
`LIMIT 1`
|
||||||
|
|
||||||
return doesExist(query, { actorId, followerActorId })
|
return doesExist(query, { actorId, followerActorId })
|
||||||
}
|
}
|
||||||
@ -238,12 +240,15 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
|
|||||||
return ActorFollowModel.findOne(query)
|
return ActorFollowModel.findOne(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
static loadByActorAndTargetNameAndHostForAPI (
|
static loadByActorAndTargetNameAndHostForAPI (options: {
|
||||||
actorId: number,
|
actorId: number
|
||||||
targetName: string,
|
targetName: string
|
||||||
targetHost: string,
|
targetHost: string
|
||||||
t?: Transaction
|
state?: FollowState
|
||||||
): Promise<MActorFollowActorsDefaultSubscription> {
|
transaction?: Transaction
|
||||||
|
}): Promise<MActorFollowActorsDefaultSubscription> {
|
||||||
|
const { actorId, targetHost, targetName, state, transaction } = options
|
||||||
|
|
||||||
const actorFollowingPartInclude: IncludeOptions = {
|
const actorFollowingPartInclude: IncludeOptions = {
|
||||||
model: ActorModel,
|
model: ActorModel,
|
||||||
required: true,
|
required: true,
|
||||||
@ -271,10 +276,11 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = {
|
const where: WhereAttributeHash<Attributes<ActorFollowModel>> = { actorId}
|
||||||
where: {
|
if (state) where.state = state
|
||||||
actorId
|
|
||||||
},
|
const query: FindOptions<Attributes<ActorFollowModel>> = {
|
||||||
|
where,
|
||||||
include: [
|
include: [
|
||||||
actorFollowingPartInclude,
|
actorFollowingPartInclude,
|
||||||
{
|
{
|
||||||
@ -283,7 +289,7 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
|
|||||||
as: 'ActorFollower'
|
as: 'ActorFollower'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
transaction: t
|
transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
return ActorFollowModel.findOne(query)
|
return ActorFollowModel.findOne(query)
|
||||||
@ -325,6 +331,7 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
|
|||||||
[Op.or]: whereTab
|
[Op.or]: whereTab
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
state: 'accepted',
|
||||||
actorId
|
actorId
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -372,6 +379,7 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
|
|||||||
}) {
|
}) {
|
||||||
const { actorId, start, count, sort } = options
|
const { actorId, start, count, sort } = options
|
||||||
const where = {
|
const where = {
|
||||||
|
state: 'accepted',
|
||||||
actorId
|
actorId
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,13 +520,15 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
|
|||||||
|
|
||||||
const totalInstanceFollowing = await ActorFollowModel.count({
|
const totalInstanceFollowing = await ActorFollowModel.count({
|
||||||
where: {
|
where: {
|
||||||
actorId: serverActor.id
|
actorId: serverActor.id,
|
||||||
|
state: 'accepted'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const totalInstanceFollowers = await ActorFollowModel.count({
|
const totalInstanceFollowers = await ActorFollowModel.count({
|
||||||
where: {
|
where: {
|
||||||
targetActorId: serverActor.id
|
targetActorId: serverActor.id,
|
||||||
|
state: 'accepted'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -462,7 +462,7 @@ export class ActorModel extends Model<Partial<AttributesOnly<ActorModel>>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ActorModel.update({
|
return ActorModel.update({
|
||||||
[columnToUpdate]: literal(`(SELECT COUNT(*) FROM "actorFollow" WHERE "${columnOfCount}" = ${sanitizedOfId})`)
|
[columnToUpdate]: literal(`(SELECT COUNT(*) FROM "actorFollow" WHERE "${columnOfCount}" = ${sanitizedOfId} AND "state" = 'accepted')`)
|
||||||
}, { where, transaction })
|
}, { where, transaction })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import 'mocha'
|
import 'mocha'
|
||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
|
import { expectStartWith } from '@server/tests/shared'
|
||||||
|
import { ActorFollow, FollowState } from '@shared/models'
|
||||||
import {
|
import {
|
||||||
cleanupTests,
|
cleanupTests,
|
||||||
createMultipleServers,
|
createMultipleServers,
|
||||||
@ -25,8 +27,51 @@ async function checkServer1And2HasFollowers (servers: PeerTubeServer[], state =
|
|||||||
|
|
||||||
const follow = body.data[0]
|
const follow = body.data[0]
|
||||||
expect(follow.state).to.equal(state)
|
expect(follow.state).to.equal(state)
|
||||||
expect(follow.follower.url).to.equal('http://localhost:' + servers[0].port + '/accounts/peertube')
|
expect(follow.follower.url).to.equal(servers[0].url + '/accounts/peertube')
|
||||||
expect(follow.following.url).to.equal('http://localhost:' + servers[1].port + '/accounts/peertube')
|
expect(follow.following.url).to.equal(servers[1].url + '/accounts/peertube')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkFollows (options: {
|
||||||
|
follower: {
|
||||||
|
server: PeerTubeServer
|
||||||
|
state?: FollowState // if not provided, it means it does not exist
|
||||||
|
}
|
||||||
|
following: {
|
||||||
|
server: PeerTubeServer
|
||||||
|
state?: FollowState // if not provided, it means it does not exist
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
const { follower, following } = options
|
||||||
|
|
||||||
|
const followerUrl = follower.server.url + '/accounts/peertube'
|
||||||
|
const followingUrl = following.server.url + '/accounts/peertube'
|
||||||
|
const finder = (d: ActorFollow) => d.follower.url === followerUrl && d.following.url === followingUrl
|
||||||
|
|
||||||
|
{
|
||||||
|
const { data } = await follower.server.follows.getFollowings()
|
||||||
|
const follow = data.find(finder)
|
||||||
|
|
||||||
|
if (!follower.state) {
|
||||||
|
expect(follow).to.not.exist
|
||||||
|
} else {
|
||||||
|
expect(follow.state).to.equal(follower.state)
|
||||||
|
expect(follow.follower.url).to.equal(followerUrl)
|
||||||
|
expect(follow.following.url).to.equal(followingUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const { data } = await following.server.follows.getFollowers()
|
||||||
|
const follow = data.find(finder)
|
||||||
|
|
||||||
|
if (!following.state) {
|
||||||
|
expect(follow).to.not.exist
|
||||||
|
} else {
|
||||||
|
expect(follow.state).to.equal(following.state)
|
||||||
|
expect(follow.follower.url).to.equal(followerUrl)
|
||||||
|
expect(follow.following.url).to.equal(followingUrl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +82,7 @@ async function checkNoFollowers (servers: PeerTubeServer[]) {
|
|||||||
]
|
]
|
||||||
|
|
||||||
for (const fn of fns) {
|
for (const fn of fns) {
|
||||||
const body = await fn({ start: 0, count: 5, sort: 'createdAt' })
|
const body = await fn({ start: 0, count: 5, sort: 'createdAt', state: 'accepted' })
|
||||||
expect(body.total).to.equal(0)
|
expect(body.total).to.equal(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,7 +169,7 @@ describe('Test follows moderation', function () {
|
|||||||
it('Should manually approve followers', async function () {
|
it('Should manually approve followers', async function () {
|
||||||
this.timeout(20000)
|
this.timeout(20000)
|
||||||
|
|
||||||
await commands[1].removeFollower({ follower: servers[0] })
|
await commands[0].unfollow({ target: servers[1] })
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
const subConfig = {
|
const subConfig = {
|
||||||
@ -148,7 +193,7 @@ describe('Test follows moderation', function () {
|
|||||||
it('Should accept a follower', async function () {
|
it('Should accept a follower', async function () {
|
||||||
this.timeout(10000)
|
this.timeout(10000)
|
||||||
|
|
||||||
await commands[1].acceptFollower({ follower: 'peertube@localhost:' + servers[0].port })
|
await commands[1].acceptFollower({ follower: 'peertube@' + servers[0].host })
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await checkServer1And2HasFollowers(servers)
|
await checkServer1And2HasFollowers(servers)
|
||||||
@ -161,29 +206,142 @@ describe('Test follows moderation', function () {
|
|||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
{
|
{
|
||||||
const body = await commands[0].getFollowings({ start: 0, count: 5, sort: 'createdAt' })
|
const body = await commands[0].getFollowings()
|
||||||
expect(body.total).to.equal(2)
|
expect(body.total).to.equal(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const body = await commands[1].getFollowers({ start: 0, count: 5, sort: 'createdAt' })
|
const body = await commands[1].getFollowers()
|
||||||
expect(body.total).to.equal(1)
|
expect(body.total).to.equal(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const body = await commands[2].getFollowers({ start: 0, count: 5, sort: 'createdAt' })
|
const body = await commands[2].getFollowers()
|
||||||
expect(body.total).to.equal(1)
|
expect(body.total).to.equal(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
await commands[2].rejectFollower({ follower: 'peertube@localhost:' + servers[0].port })
|
await commands[2].rejectFollower({ follower: 'peertube@' + servers[0].host })
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await checkServer1And2HasFollowers(servers)
|
{ // server 1
|
||||||
|
{
|
||||||
|
const { data } = await commands[0].getFollowings({ state: 'accepted' })
|
||||||
|
expect(data).to.have.lengthOf(1)
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const body = await commands[2].getFollowers({ start: 0, count: 5, sort: 'createdAt' })
|
const { data } = await commands[0].getFollowings({ state: 'rejected' })
|
||||||
expect(body.total).to.equal(0)
|
expect(data).to.have.lengthOf(1)
|
||||||
|
expectStartWith(data[0].following.url, servers[2].url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ // server 3
|
||||||
|
{
|
||||||
|
const { data } = await commands[2].getFollowers({ state: 'accepted' })
|
||||||
|
expect(data).to.have.lengthOf(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const { data } = await commands[2].getFollowers({ state: 'rejected' })
|
||||||
|
expect(data).to.have.lengthOf(1)
|
||||||
|
expectStartWith(data[0].follower.url, servers[0].url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should not change the follow on refollow with and without auto accept', async function () {
|
||||||
|
const run = async () => {
|
||||||
|
await commands[0].follow({ hosts: [ servers[2].url ] })
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
await checkFollows({
|
||||||
|
follower: {
|
||||||
|
server: servers[0],
|
||||||
|
state: 'rejected'
|
||||||
|
},
|
||||||
|
following: {
|
||||||
|
server: servers[2],
|
||||||
|
state: 'rejected'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await servers[2].config.updateExistingSubConfig({ newConfig: { followers: { instance: { manualApproval: false } } } })
|
||||||
|
await run()
|
||||||
|
|
||||||
|
await servers[2].config.updateExistingSubConfig({ newConfig: { followers: { instance: { manualApproval: true } } } })
|
||||||
|
await run()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should not change the rejected status on unfollow', async function () {
|
||||||
|
await commands[0].unfollow({ target: servers[2] })
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
await checkFollows({
|
||||||
|
follower: {
|
||||||
|
server: servers[0]
|
||||||
|
},
|
||||||
|
following: {
|
||||||
|
server: servers[2],
|
||||||
|
state: 'rejected'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should delete the follower and add again the follower', async function () {
|
||||||
|
await commands[2].removeFollower({ follower: servers[0] })
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
await commands[0].follow({ hosts: [ servers[2].url ] })
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
await checkFollows({
|
||||||
|
follower: {
|
||||||
|
server: servers[0],
|
||||||
|
state: 'pending'
|
||||||
|
},
|
||||||
|
following: {
|
||||||
|
server: servers[2],
|
||||||
|
state: 'pending'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should be able to reject a previously accepted follower', async function () {
|
||||||
|
await commands[1].rejectFollower({ follower: 'peertube@' + servers[0].host })
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
await checkFollows({
|
||||||
|
follower: {
|
||||||
|
server: servers[0],
|
||||||
|
state: 'rejected'
|
||||||
|
},
|
||||||
|
following: {
|
||||||
|
server: servers[1],
|
||||||
|
state: 'rejected'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should be able to re accept a previously rejected follower', async function () {
|
||||||
|
await commands[1].acceptFollower({ follower: 'peertube@' + servers[0].host })
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
await checkFollows({
|
||||||
|
follower: {
|
||||||
|
server: servers[0],
|
||||||
|
state: 'accepted'
|
||||||
|
},
|
||||||
|
following: {
|
||||||
|
server: servers[1],
|
||||||
|
state: 'accepted'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should ignore follow requests of muted servers', async function () {
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
after(async function () {
|
after(async function () {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Actor } from './actor.model'
|
import { Actor } from './actor.model'
|
||||||
|
|
||||||
export type FollowState = 'pending' | 'accepted'
|
export type FollowState = 'pending' | 'accepted' | 'rejected'
|
||||||
|
|
||||||
export interface ActorFollow {
|
export interface ActorFollow {
|
||||||
id: number
|
id: number
|
||||||
|
@ -6,13 +6,13 @@ import { PeerTubeServer } from './server'
|
|||||||
export class FollowsCommand extends AbstractCommand {
|
export class FollowsCommand extends AbstractCommand {
|
||||||
|
|
||||||
getFollowers (options: OverrideCommandOptions & {
|
getFollowers (options: OverrideCommandOptions & {
|
||||||
start: number
|
start?: number
|
||||||
count: number
|
count?: number
|
||||||
sort: string
|
sort?: string
|
||||||
search?: string
|
search?: string
|
||||||
actorType?: ActivityPubActorType
|
actorType?: ActivityPubActorType
|
||||||
state?: FollowState
|
state?: FollowState
|
||||||
}) {
|
} = {}) {
|
||||||
const path = '/api/v1/server/followers'
|
const path = '/api/v1/server/followers'
|
||||||
|
|
||||||
const query = pick(options, [ 'start', 'count', 'sort', 'search', 'state', 'actorType' ])
|
const query = pick(options, [ 'start', 'count', 'sort', 'search', 'state', 'actorType' ])
|
||||||
|
Loading…
Reference in New Issue
Block a user