Add ability for users to block an account/instance on server side
This commit is contained in:
parent
dffd5d127f
commit
7ad9b9846c
33 changed files with 1344 additions and 56 deletions
|
@ -1,7 +1,8 @@
|
|||
import * as express from 'express'
|
||||
import { getFormattedObjects } from '../../helpers/utils'
|
||||
import {
|
||||
asyncMiddleware, commonVideosFiltersValidator,
|
||||
asyncMiddleware,
|
||||
commonVideosFiltersValidator,
|
||||
listVideoAccountChannelsValidator,
|
||||
optionalAuthenticate,
|
||||
paginationValidator,
|
||||
|
@ -90,7 +91,7 @@ async function listAccountVideos (req: express.Request, res: express.Response, n
|
|||
nsfw: buildNSFWFilter(res, req.query.nsfw),
|
||||
withFiles: false,
|
||||
accountId: account.id,
|
||||
userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined
|
||||
user: res.locals.oauth ? res.locals.oauth.token.User : undefined
|
||||
})
|
||||
|
||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||
|
|
|
@ -119,7 +119,7 @@ async function searchVideosDB (query: VideosSearchQuery, res: express.Response)
|
|||
includeLocalVideos: true,
|
||||
nsfw: buildNSFWFilter(res, query.nsfw),
|
||||
filter: query.filter,
|
||||
userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined
|
||||
user: res.locals.oauth ? res.locals.oauth.token.User : undefined
|
||||
})
|
||||
const resultList = await VideoModel.searchAndPopulateAccountAndServer(options)
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ import { UserModel } from '../../../models/account/user'
|
|||
import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger'
|
||||
import { meRouter } from './me'
|
||||
import { deleteUserToken } from '../../../lib/oauth-model'
|
||||
import { myBlocklistRouter } from './my-blocklist'
|
||||
|
||||
const auditLogger = auditLoggerFactory('users')
|
||||
|
||||
|
@ -53,6 +54,7 @@ const askSendEmailLimiter = new RateLimit({
|
|||
})
|
||||
|
||||
const usersRouter = express.Router()
|
||||
usersRouter.use('/', myBlocklistRouter)
|
||||
usersRouter.use('/', meRouter)
|
||||
|
||||
usersRouter.get('/autocomplete',
|
||||
|
|
|
@ -238,7 +238,8 @@ async function getUserSubscriptionVideos (req: express.Request, res: express.Res
|
|||
nsfw: buildNSFWFilter(res, req.query.nsfw),
|
||||
filter: req.query.filter as VideoFilter,
|
||||
withFiles: false,
|
||||
actorId: user.Account.Actor.id
|
||||
actorId: user.Account.Actor.id,
|
||||
user
|
||||
})
|
||||
|
||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||
|
|
125
server/controllers/api/users/my-blocklist.ts
Normal file
125
server/controllers/api/users/my-blocklist.ts
Normal file
|
@ -0,0 +1,125 @@
|
|||
import * as express from 'express'
|
||||
import 'multer'
|
||||
import { getFormattedObjects } from '../../../helpers/utils'
|
||||
import {
|
||||
asyncMiddleware,
|
||||
asyncRetryTransactionMiddleware,
|
||||
authenticate,
|
||||
paginationValidator,
|
||||
serverGetValidator,
|
||||
setDefaultPagination,
|
||||
setDefaultSort,
|
||||
unblockAccountByAccountValidator
|
||||
} from '../../../middlewares'
|
||||
import {
|
||||
accountsBlocklistSortValidator,
|
||||
blockAccountByAccountValidator,
|
||||
serversBlocklistSortValidator,
|
||||
unblockServerByAccountValidator
|
||||
} from '../../../middlewares/validators'
|
||||
import { UserModel } from '../../../models/account/user'
|
||||
import { AccountModel } from '../../../models/account/account'
|
||||
import { AccountBlocklistModel } from '../../../models/account/account-blocklist'
|
||||
import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist'
|
||||
import { ServerBlocklistModel } from '../../../models/server/server-blocklist'
|
||||
import { ServerModel } from '../../../models/server/server'
|
||||
|
||||
const myBlocklistRouter = express.Router()
|
||||
|
||||
myBlocklistRouter.get('/me/blocklist/accounts',
|
||||
authenticate,
|
||||
paginationValidator,
|
||||
accountsBlocklistSortValidator,
|
||||
setDefaultSort,
|
||||
setDefaultPagination,
|
||||
asyncMiddleware(listBlockedAccounts)
|
||||
)
|
||||
|
||||
myBlocklistRouter.post('/me/blocklist/accounts',
|
||||
authenticate,
|
||||
asyncMiddleware(blockAccountByAccountValidator),
|
||||
asyncRetryTransactionMiddleware(blockAccount)
|
||||
)
|
||||
|
||||
myBlocklistRouter.delete('/me/blocklist/accounts/:accountName',
|
||||
authenticate,
|
||||
asyncMiddleware(unblockAccountByAccountValidator),
|
||||
asyncRetryTransactionMiddleware(unblockAccount)
|
||||
)
|
||||
|
||||
myBlocklistRouter.get('/me/blocklist/servers',
|
||||
authenticate,
|
||||
paginationValidator,
|
||||
serversBlocklistSortValidator,
|
||||
setDefaultSort,
|
||||
setDefaultPagination,
|
||||
asyncMiddleware(listBlockedServers)
|
||||
)
|
||||
|
||||
myBlocklistRouter.post('/me/blocklist/servers',
|
||||
authenticate,
|
||||
asyncMiddleware(serverGetValidator),
|
||||
asyncRetryTransactionMiddleware(blockServer)
|
||||
)
|
||||
|
||||
myBlocklistRouter.delete('/me/blocklist/servers/:host',
|
||||
authenticate,
|
||||
asyncMiddleware(unblockServerByAccountValidator),
|
||||
asyncRetryTransactionMiddleware(unblockServer)
|
||||
)
|
||||
|
||||
export {
|
||||
myBlocklistRouter
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function listBlockedAccounts (req: express.Request, res: express.Response) {
|
||||
const user: UserModel = res.locals.oauth.token.User
|
||||
|
||||
const resultList = await AccountBlocklistModel.listForApi(user.Account.id, req.query.start, req.query.count, req.query.sort)
|
||||
|
||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||
}
|
||||
|
||||
async function blockAccount (req: express.Request, res: express.Response) {
|
||||
const user: UserModel = res.locals.oauth.token.User
|
||||
const accountToBlock: AccountModel = res.locals.account
|
||||
|
||||
await addAccountInBlocklist(user.Account.id, accountToBlock.id)
|
||||
|
||||
return res.status(204).end()
|
||||
}
|
||||
|
||||
async function unblockAccount (req: express.Request, res: express.Response) {
|
||||
const accountBlock: AccountBlocklistModel = res.locals.accountBlock
|
||||
|
||||
await removeAccountFromBlocklist(accountBlock)
|
||||
|
||||
return res.status(204).end()
|
||||
}
|
||||
|
||||
async function listBlockedServers (req: express.Request, res: express.Response) {
|
||||
const user: UserModel = res.locals.oauth.token.User
|
||||
|
||||
const resultList = await ServerBlocklistModel.listForApi(user.Account.id, req.query.start, req.query.count, req.query.sort)
|
||||
|
||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||
}
|
||||
|
||||
async function blockServer (req: express.Request, res: express.Response) {
|
||||
const user: UserModel = res.locals.oauth.token.User
|
||||
const serverToBlock: ServerModel = res.locals.server
|
||||
|
||||
await addServerInBlocklist(user.Account.id, serverToBlock.id)
|
||||
|
||||
return res.status(204).end()
|
||||
}
|
||||
|
||||
async function unblockServer (req: express.Request, res: express.Response) {
|
||||
const serverBlock: ServerBlocklistModel = res.locals.serverBlock
|
||||
|
||||
await removeServerFromBlocklist(serverBlock)
|
||||
|
||||
return res.status(204).end()
|
||||
}
|
|
@ -219,7 +219,7 @@ async function listVideoChannelVideos (req: express.Request, res: express.Respon
|
|||
nsfw: buildNSFWFilter(res, req.query.nsfw),
|
||||
withFiles: false,
|
||||
videoChannelId: videoChannelInstance.id,
|
||||
userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined
|
||||
user: res.locals.oauth ? res.locals.oauth.token.User : undefined
|
||||
})
|
||||
|
||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||
|
|
|
@ -8,7 +8,7 @@ import { buildFormattedCommentTree, createVideoComment } from '../../../lib/vide
|
|||
import {
|
||||
asyncMiddleware,
|
||||
asyncRetryTransactionMiddleware,
|
||||
authenticate,
|
||||
authenticate, optionalAuthenticate,
|
||||
paginationValidator,
|
||||
setDefaultPagination,
|
||||
setDefaultSort
|
||||
|
@ -36,10 +36,12 @@ videoCommentRouter.get('/:videoId/comment-threads',
|
|||
setDefaultSort,
|
||||
setDefaultPagination,
|
||||
asyncMiddleware(listVideoCommentThreadsValidator),
|
||||
optionalAuthenticate,
|
||||
asyncMiddleware(listVideoThreads)
|
||||
)
|
||||
videoCommentRouter.get('/:videoId/comment-threads/:threadId',
|
||||
asyncMiddleware(listVideoThreadCommentsValidator),
|
||||
optionalAuthenticate,
|
||||
asyncMiddleware(listVideoThreadComments)
|
||||
)
|
||||
|
||||
|
@ -69,10 +71,12 @@ export {
|
|||
|
||||
async function listVideoThreads (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
const video = res.locals.video as VideoModel
|
||||
const user: UserModel = res.locals.oauth ? res.locals.oauth.token.User : undefined
|
||||
|
||||
let resultList: ResultList<VideoCommentModel>
|
||||
|
||||
if (video.commentsEnabled === true) {
|
||||
resultList = await VideoCommentModel.listThreadsForApi(video.id, req.query.start, req.query.count, req.query.sort)
|
||||
resultList = await VideoCommentModel.listThreadsForApi(video.id, req.query.start, req.query.count, req.query.sort, user)
|
||||
} else {
|
||||
resultList = {
|
||||
total: 0,
|
||||
|
@ -85,10 +89,12 @@ async function listVideoThreads (req: express.Request, res: express.Response, ne
|
|||
|
||||
async function listVideoThreadComments (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
const video = res.locals.video as VideoModel
|
||||
const user: UserModel = res.locals.oauth ? res.locals.oauth.token.User : undefined
|
||||
|
||||
let resultList: ResultList<VideoCommentModel>
|
||||
|
||||
if (video.commentsEnabled === true) {
|
||||
resultList = await VideoCommentModel.listThreadCommentsForApi(video.id, res.locals.videoCommentThread.id)
|
||||
resultList = await VideoCommentModel.listThreadCommentsForApi(video.id, res.locals.videoCommentThread.id, user)
|
||||
} else {
|
||||
resultList = {
|
||||
total: 0,
|
||||
|
|
|
@ -437,7 +437,7 @@ async function listVideos (req: express.Request, res: express.Response, next: ex
|
|||
nsfw: buildNSFWFilter(res, req.query.nsfw),
|
||||
filter: req.query.filter as VideoFilter,
|
||||
withFiles: false,
|
||||
userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined
|
||||
user: res.locals.oauth ? res.locals.oauth.token.User : undefined
|
||||
})
|
||||
|
||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||
|
|
|
@ -40,7 +40,10 @@ const getServerActor = memoizee(async function () {
|
|||
const application = await ApplicationModel.load()
|
||||
if (!application) throw Error('Could not load Application from database.')
|
||||
|
||||
return application.Account.Actor
|
||||
const actor = application.Account.Actor
|
||||
actor.Account = application.Account
|
||||
|
||||
return actor
|
||||
})
|
||||
|
||||
function generateVideoTmpPath (target: string | ParseTorrent) {
|
||||
|
|
|
@ -47,7 +47,10 @@ const SORTABLE_COLUMNS = {
|
|||
VIDEOS: [ 'name', 'duration', 'createdAt', 'publishedAt', 'views', 'likes', 'trending' ],
|
||||
|
||||
VIDEOS_SEARCH: [ 'name', 'duration', 'createdAt', 'publishedAt', 'views', 'likes', 'match' ],
|
||||
VIDEO_CHANNELS_SEARCH: [ 'match', 'displayName', 'createdAt' ]
|
||||
VIDEO_CHANNELS_SEARCH: [ 'match', 'displayName', 'createdAt' ],
|
||||
|
||||
ACCOUNTS_BLOCKLIST: [ 'createdAt' ],
|
||||
SERVERS_BLOCKLIST: [ 'createdAt' ]
|
||||
}
|
||||
|
||||
const OAUTH_LIFETIME = {
|
||||
|
|
|
@ -29,6 +29,8 @@ import { VideoViewModel } from '../models/video/video-views'
|
|||
import { VideoChangeOwnershipModel } from '../models/video/video-change-ownership'
|
||||
import { VideoRedundancyModel } from '../models/redundancy/video-redundancy'
|
||||
import { UserVideoHistoryModel } from '../models/account/user-video-history'
|
||||
import { AccountBlocklistModel } from '../models/account/account-blocklist'
|
||||
import { ServerBlocklistModel } from '../models/server/server-blocklist'
|
||||
|
||||
require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string
|
||||
|
||||
|
@ -91,7 +93,9 @@ async function initDatabaseModels (silent: boolean) {
|
|||
VideoImportModel,
|
||||
VideoViewModel,
|
||||
VideoRedundancyModel,
|
||||
UserVideoHistoryModel
|
||||
UserVideoHistoryModel,
|
||||
AccountBlocklistModel,
|
||||
ServerBlocklistModel
|
||||
])
|
||||
|
||||
// Check extensions exist in the database
|
||||
|
|
40
server/lib/blocklist.ts
Normal file
40
server/lib/blocklist.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { sequelizeTypescript } from '../initializers'
|
||||
import { AccountBlocklistModel } from '../models/account/account-blocklist'
|
||||
import { ServerBlocklistModel } from '../models/server/server-blocklist'
|
||||
|
||||
function addAccountInBlocklist (byAccountId: number, targetAccountId: number) {
|
||||
return sequelizeTypescript.transaction(async t => {
|
||||
return AccountBlocklistModel.create({
|
||||
accountId: byAccountId,
|
||||
targetAccountId: targetAccountId
|
||||
}, { transaction: t })
|
||||
})
|
||||
}
|
||||
|
||||
function addServerInBlocklist (byAccountId: number, targetServerId: number) {
|
||||
return sequelizeTypescript.transaction(async t => {
|
||||
return ServerBlocklistModel.create({
|
||||
accountId: byAccountId,
|
||||
targetServerId
|
||||
}, { transaction: t })
|
||||
})
|
||||
}
|
||||
|
||||
function removeAccountFromBlocklist (accountBlock: AccountBlocklistModel) {
|
||||
return sequelizeTypescript.transaction(async t => {
|
||||
return accountBlock.destroy({ transaction: t })
|
||||
})
|
||||
}
|
||||
|
||||
function removeServerFromBlocklist (serverBlock: ServerBlocklistModel) {
|
||||
return sequelizeTypescript.transaction(async t => {
|
||||
return serverBlock.destroy({ transaction: t })
|
||||
})
|
||||
}
|
||||
|
||||
export {
|
||||
addAccountInBlocklist,
|
||||
addServerInBlocklist,
|
||||
removeAccountFromBlocklist,
|
||||
removeServerFromBlocklist
|
||||
}
|
|
@ -64,10 +64,8 @@ function buildFormattedCommentTree (resultList: ResultList<VideoCommentModel>):
|
|||
}
|
||||
|
||||
const parentCommentThread = idx[childComment.inReplyToCommentId]
|
||||
if (!parentCommentThread) {
|
||||
const msg = `Cannot format video thread tree, parent ${childComment.inReplyToCommentId} not found for child ${childComment.id}`
|
||||
throw new Error(msg)
|
||||
}
|
||||
// Maybe the parent comment was blocked by the admin/user
|
||||
if (!parentCommentThread) continue
|
||||
|
||||
parentCommentThread.children.push(childCommentThread)
|
||||
idx[childComment.id] = childCommentThread
|
||||
|
|
94
server/middlewares/validators/blocklist.ts
Normal file
94
server/middlewares/validators/blocklist.ts
Normal file
|
@ -0,0 +1,94 @@
|
|||
import { param, body } from 'express-validator/check'
|
||||
import * as express from 'express'
|
||||
import { logger } from '../../helpers/logger'
|
||||
import { areValidationErrors } from './utils'
|
||||
import { isAccountNameWithHostExist } from '../../helpers/custom-validators/accounts'
|
||||
import { UserModel } from '../../models/account/user'
|
||||
import { AccountBlocklistModel } from '../../models/account/account-blocklist'
|
||||
import { isHostValid } from '../../helpers/custom-validators/servers'
|
||||
import { ServerBlocklistModel } from '../../models/server/server-blocklist'
|
||||
|
||||
const blockAccountByAccountValidator = [
|
||||
body('accountName').exists().withMessage('Should have an account name with host'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
logger.debug('Checking blockAccountByAccountValidator parameters', { parameters: req.body })
|
||||
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (!await isAccountNameWithHostExist(req.body.accountName, res)) return
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
const unblockAccountByAccountValidator = [
|
||||
param('accountName').exists().withMessage('Should have an account name with host'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
logger.debug('Checking unblockAccountByAccountValidator parameters', { parameters: req.params })
|
||||
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (!await isAccountNameWithHostExist(req.params.accountName, res)) return
|
||||
|
||||
const user = res.locals.oauth.token.User as UserModel
|
||||
const targetAccount = res.locals.account
|
||||
if (!await isUnblockAccountExists(user.Account.id, targetAccount.id, res)) return
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
const unblockServerByAccountValidator = [
|
||||
param('host').custom(isHostValid).withMessage('Should have an account name with host'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
logger.debug('Checking unblockServerByAccountValidator parameters', { parameters: req.params })
|
||||
|
||||
if (areValidationErrors(req, res)) return
|
||||
|
||||
const user = res.locals.oauth.token.User as UserModel
|
||||
if (!await isUnblockServerExists(user.Account.id, req.params.host, res)) return
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
blockAccountByAccountValidator,
|
||||
unblockAccountByAccountValidator,
|
||||
unblockServerByAccountValidator
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function isUnblockAccountExists (accountId: number, targetAccountId: number, res: express.Response) {
|
||||
const accountBlock = await AccountBlocklistModel.loadByAccountAndTarget(accountId, targetAccountId)
|
||||
if (!accountBlock) {
|
||||
res.status(404)
|
||||
.send({ error: 'Account block entry not found.' })
|
||||
.end()
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
res.locals.accountBlock = accountBlock
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async function isUnblockServerExists (accountId: number, host: string, res: express.Response) {
|
||||
const serverBlock = await ServerBlocklistModel.loadByAccountAndHost(accountId, host)
|
||||
if (!serverBlock) {
|
||||
res.status(404)
|
||||
.send({ error: 'Server block entry not found.' })
|
||||
.end()
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
res.locals.serverBlock = serverBlock
|
||||
|
||||
return true
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
export * from './account'
|
||||
export * from './blocklist'
|
||||
export * from './oembed'
|
||||
export * from './activitypub'
|
||||
export * from './pagination'
|
||||
|
@ -10,3 +11,4 @@ export * from './user-subscriptions'
|
|||
export * from './videos'
|
||||
export * from './webfinger'
|
||||
export * from './search'
|
||||
export * from './server'
|
||||
|
|
33
server/middlewares/validators/server.ts
Normal file
33
server/middlewares/validators/server.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import * as express from 'express'
|
||||
import { logger } from '../../helpers/logger'
|
||||
import { areValidationErrors } from './utils'
|
||||
import { isHostValid } from '../../helpers/custom-validators/servers'
|
||||
import { ServerModel } from '../../models/server/server'
|
||||
import { body } from 'express-validator/check'
|
||||
|
||||
const serverGetValidator = [
|
||||
body('host').custom(isHostValid).withMessage('Should have a valid host'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
logger.debug('Checking serverGetValidator parameters', { parameters: req.body })
|
||||
|
||||
if (areValidationErrors(req, res)) return
|
||||
|
||||
const server = await ServerModel.loadByHost(req.body.host)
|
||||
if (!server) {
|
||||
return res.status(404)
|
||||
.send({ error: 'Server host not found.' })
|
||||
.end()
|
||||
}
|
||||
|
||||
res.locals.server = server
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
serverGetValidator
|
||||
}
|
|
@ -16,6 +16,8 @@ const SORTABLE_VIDEO_CHANNELS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.V
|
|||
const SORTABLE_FOLLOWERS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.FOLLOWERS)
|
||||
const SORTABLE_FOLLOWING_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.FOLLOWING)
|
||||
const SORTABLE_USER_SUBSCRIPTIONS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USER_SUBSCRIPTIONS)
|
||||
const SORTABLE_ACCOUNTS_BLOCKLIST_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.ACCOUNTS_BLOCKLIST)
|
||||
const SORTABLE_SERVERS_BLOCKLIST_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.SERVERS_BLOCKLIST)
|
||||
|
||||
const usersSortValidator = checkSort(SORTABLE_USERS_COLUMNS)
|
||||
const accountsSortValidator = checkSort(SORTABLE_ACCOUNTS_COLUMNS)
|
||||
|
@ -31,6 +33,8 @@ const videoChannelsSortValidator = checkSort(SORTABLE_VIDEO_CHANNELS_COLUMNS)
|
|||
const followersSortValidator = checkSort(SORTABLE_FOLLOWERS_COLUMNS)
|
||||
const followingSortValidator = checkSort(SORTABLE_FOLLOWING_COLUMNS)
|
||||
const userSubscriptionsSortValidator = checkSort(SORTABLE_USER_SUBSCRIPTIONS_COLUMNS)
|
||||
const accountsBlocklistSortValidator = checkSort(SORTABLE_ACCOUNTS_BLOCKLIST_COLUMNS)
|
||||
const serversBlocklistSortValidator = checkSort(SORTABLE_SERVERS_BLOCKLIST_COLUMNS)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
@ -48,5 +52,7 @@ export {
|
|||
jobsSortValidator,
|
||||
videoCommentThreadsSortValidator,
|
||||
userSubscriptionsSortValidator,
|
||||
videoChannelsSearchSortValidator
|
||||
videoChannelsSearchSortValidator,
|
||||
accountsBlocklistSortValidator,
|
||||
serversBlocklistSortValidator
|
||||
}
|
||||
|
|
111
server/models/account/account-blocklist.ts
Normal file
111
server/models/account/account-blocklist.ts
Normal file
|
@ -0,0 +1,111 @@
|
|||
import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
|
||||
import { AccountModel } from './account'
|
||||
import { getSort } from '../utils'
|
||||
import { AccountBlock } from '../../../shared/models/blocklist'
|
||||
|
||||
enum ScopeNames {
|
||||
WITH_ACCOUNTS = 'WITH_ACCOUNTS'
|
||||
}
|
||||
|
||||
@Scopes({
|
||||
[ScopeNames.WITH_ACCOUNTS]: {
|
||||
include: [
|
||||
{
|
||||
model: () => AccountModel,
|
||||
required: true,
|
||||
as: 'ByAccount'
|
||||
},
|
||||
{
|
||||
model: () => AccountModel,
|
||||
required: true,
|
||||
as: 'AccountBlocked'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
@Table({
|
||||
tableName: 'accountBlocklist',
|
||||
indexes: [
|
||||
{
|
||||
fields: [ 'accountId', 'targetAccountId' ],
|
||||
unique: true
|
||||
},
|
||||
{
|
||||
fields: [ 'targetAccountId' ]
|
||||
}
|
||||
]
|
||||
})
|
||||
export class AccountBlocklistModel extends Model<AccountBlocklistModel> {
|
||||
|
||||
@CreatedAt
|
||||
createdAt: Date
|
||||
|
||||
@UpdatedAt
|
||||
updatedAt: Date
|
||||
|
||||
@ForeignKey(() => AccountModel)
|
||||
@Column
|
||||
accountId: number
|
||||
|
||||
@BelongsTo(() => AccountModel, {
|
||||
foreignKey: {
|
||||
name: 'accountId',
|
||||
allowNull: false
|
||||
},
|
||||
as: 'ByAccount',
|
||||
onDelete: 'CASCADE'
|
||||
})
|
||||
ByAccount: AccountModel
|
||||
|
||||
@ForeignKey(() => AccountModel)
|
||||
@Column
|
||||
targetAccountId: number
|
||||
|
||||
@BelongsTo(() => AccountModel, {
|
||||
foreignKey: {
|
||||
name: 'targetAccountId',
|
||||
allowNull: false
|
||||
},
|
||||
as: 'AccountBlocked',
|
||||
onDelete: 'CASCADE'
|
||||
})
|
||||
AccountBlocked: AccountModel
|
||||
|
||||
static loadByAccountAndTarget (accountId: number, targetAccountId: number) {
|
||||
const query = {
|
||||
where: {
|
||||
accountId,
|
||||
targetAccountId
|
||||
}
|
||||
}
|
||||
|
||||
return AccountBlocklistModel.findOne(query)
|
||||
}
|
||||
|
||||
static listForApi (accountId: number, start: number, count: number, sort: string) {
|
||||
const query = {
|
||||
offset: start,
|
||||
limit: count,
|
||||
order: getSort(sort),
|
||||
where: {
|
||||
accountId
|
||||
}
|
||||
}
|
||||
|
||||
return AccountBlocklistModel
|
||||
.scope([ ScopeNames.WITH_ACCOUNTS ])
|
||||
.findAndCountAll(query)
|
||||
.then(({ rows, count }) => {
|
||||
return { total: count, data: rows }
|
||||
})
|
||||
}
|
||||
|
||||
toFormattedJSON (): AccountBlock {
|
||||
return {
|
||||
byAccount: this.ByAccount.toFormattedJSON(),
|
||||
accountBlocked: this.AccountBlocked.toFormattedJSON(),
|
||||
createdAt: this.createdAt
|
||||
}
|
||||
}
|
||||
}
|
121
server/models/server/server-blocklist.ts
Normal file
121
server/models/server/server-blocklist.ts
Normal file
|
@ -0,0 +1,121 @@
|
|||
import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
|
||||
import { AccountModel } from '../account/account'
|
||||
import { ServerModel } from './server'
|
||||
import { ServerBlock } from '../../../shared/models/blocklist'
|
||||
import { getSort } from '../utils'
|
||||
|
||||
enum ScopeNames {
|
||||
WITH_ACCOUNT = 'WITH_ACCOUNT',
|
||||
WITH_SERVER = 'WITH_SERVER'
|
||||
}
|
||||
|
||||
@Scopes({
|
||||
[ScopeNames.WITH_ACCOUNT]: {
|
||||
include: [
|
||||
{
|
||||
model: () => AccountModel,
|
||||
required: true
|
||||
}
|
||||
]
|
||||
},
|
||||
[ScopeNames.WITH_SERVER]: {
|
||||
include: [
|
||||
{
|
||||
model: () => ServerModel,
|
||||
required: true
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
@Table({
|
||||
tableName: 'serverBlocklist',
|
||||
indexes: [
|
||||
{
|
||||
fields: [ 'accountId', 'targetServerId' ],
|
||||
unique: true
|
||||
},
|
||||
{
|
||||
fields: [ 'targetServerId' ]
|
||||
}
|
||||
]
|
||||
})
|
||||
export class ServerBlocklistModel extends Model<ServerBlocklistModel> {
|
||||
|
||||
@CreatedAt
|
||||
createdAt: Date
|
||||
|
||||
@UpdatedAt
|
||||
updatedAt: Date
|
||||
|
||||
@ForeignKey(() => AccountModel)
|
||||
@Column
|
||||
accountId: number
|
||||
|
||||
@BelongsTo(() => AccountModel, {
|
||||
foreignKey: {
|
||||
name: 'accountId',
|
||||
allowNull: false
|
||||
},
|
||||
onDelete: 'CASCADE'
|
||||
})
|
||||
ByAccount: AccountModel
|
||||
|
||||
@ForeignKey(() => ServerModel)
|
||||
@Column
|
||||
targetServerId: number
|
||||
|
||||
@BelongsTo(() => ServerModel, {
|
||||
foreignKey: {
|
||||
name: 'targetServerId',
|
||||
allowNull: false
|
||||
},
|
||||
onDelete: 'CASCADE'
|
||||
})
|
||||
ServerBlocked: ServerModel
|
||||
|
||||
static loadByAccountAndHost (accountId: number, host: string) {
|
||||
const query = {
|
||||
where: {
|
||||
accountId
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: ServerModel,
|
||||
where: {
|
||||
host
|
||||
},
|
||||
required: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return ServerBlocklistModel.findOne(query)
|
||||
}
|
||||
|
||||
static listForApi (accountId: number, start: number, count: number, sort: string) {
|
||||
const query = {
|
||||
offset: start,
|
||||
limit: count,
|
||||
order: getSort(sort),
|
||||
where: {
|
||||
accountId
|
||||
}
|
||||
}
|
||||
|
||||
return ServerBlocklistModel
|
||||
.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_SERVER ])
|
||||
.findAndCountAll(query)
|
||||
.then(({ rows, count }) => {
|
||||
return { total: count, data: rows }
|
||||
})
|
||||
}
|
||||
|
||||
toFormattedJSON (): ServerBlock {
|
||||
return {
|
||||
byAccount: this.ByAccount.toFormattedJSON(),
|
||||
serverBlocked: this.ServerBlocked.toFormattedJSON(),
|
||||
createdAt: this.createdAt
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,4 +49,10 @@ export class ServerModel extends Model<ServerModel> {
|
|||
|
||||
return ServerModel.findOne(query)
|
||||
}
|
||||
|
||||
toFormattedJSON () {
|
||||
return {
|
||||
host: this.host
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,9 +64,27 @@ function createSimilarityAttribute (col: string, value: string) {
|
|||
)
|
||||
}
|
||||
|
||||
function buildBlockedAccountSQL (serverAccountId: number, userAccountId?: number) {
|
||||
const blockerIds = [ serverAccountId ]
|
||||
if (userAccountId) blockerIds.push(userAccountId)
|
||||
|
||||
const blockerIdsString = blockerIds.join(', ')
|
||||
|
||||
const query = 'SELECT "targetAccountId" AS "id" FROM "accountBlocklist" WHERE "accountId" IN (' + blockerIdsString + ')' +
|
||||
' UNION ALL ' +
|
||||
// 'SELECT "accountId" FROM "accountBlocklist" WHERE "targetAccountId" = user.account.id
|
||||
// UNION ALL
|
||||
'SELECT "account"."id" AS "id" FROM account INNER JOIN "actor" ON account."actorId" = actor.id ' +
|
||||
'INNER JOIN "serverBlocklist" ON "actor"."serverId" = "serverBlocklist"."targetServerId" ' +
|
||||
'WHERE "serverBlocklist"."accountId" IN (' + blockerIdsString + ')'
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
buildBlockedAccountSQL,
|
||||
SortType,
|
||||
getSort,
|
||||
getVideoSort,
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
import * as Sequelize from 'sequelize'
|
||||
import {
|
||||
AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table,
|
||||
AllowNull,
|
||||
BeforeDestroy,
|
||||
BelongsTo,
|
||||
Column,
|
||||
CreatedAt,
|
||||
DataType,
|
||||
ForeignKey,
|
||||
IFindOptions,
|
||||
Is,
|
||||
Model,
|
||||
Scopes,
|
||||
Table,
|
||||
UpdatedAt
|
||||
} from 'sequelize-typescript'
|
||||
import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects'
|
||||
|
@ -13,9 +24,11 @@ import { AccountModel } from '../account/account'
|
|||
import { ActorModel } from '../activitypub/actor'
|
||||
import { AvatarModel } from '../avatar/avatar'
|
||||
import { ServerModel } from '../server/server'
|
||||
import { getSort, throwIfNotValid } from '../utils'
|
||||
import { buildBlockedAccountSQL, getSort, throwIfNotValid } from '../utils'
|
||||
import { VideoModel } from './video'
|
||||
import { VideoChannelModel } from './video-channel'
|
||||
import { getServerActor } from '../../helpers/utils'
|
||||
import { UserModel } from '../account/user'
|
||||
|
||||
enum ScopeNames {
|
||||
WITH_ACCOUNT = 'WITH_ACCOUNT',
|
||||
|
@ -25,19 +38,30 @@ enum ScopeNames {
|
|||
}
|
||||
|
||||
@Scopes({
|
||||
[ScopeNames.ATTRIBUTES_FOR_API]: {
|
||||
[ScopeNames.ATTRIBUTES_FOR_API]: (serverAccountId: number, userAccountId?: number) => {
|
||||
return {
|
||||
attributes: {
|
||||
include: [
|
||||
[
|
||||
Sequelize.literal(
|
||||
'(SELECT COUNT("replies"."id") ' +
|
||||
'(' +
|
||||
'WITH "blocklist" AS (' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')' +
|
||||
'SELECT COUNT("replies"."id") - (' +
|
||||
'SELECT COUNT("replies"."id") ' +
|
||||
'FROM "videoComment" AS "replies" ' +
|
||||
'WHERE "replies"."originCommentId" = "VideoCommentModel"."id")'
|
||||
'WHERE "replies"."originCommentId" = "VideoCommentModel"."id" ' +
|
||||
'AND "accountId" IN (SELECT "id" FROM "blocklist")' +
|
||||
')' +
|
||||
'FROM "videoComment" AS "replies" ' +
|
||||
'WHERE "replies"."originCommentId" = "VideoCommentModel"."id" ' +
|
||||
'AND "accountId" NOT IN (SELECT "id" FROM "blocklist")' +
|
||||
')'
|
||||
),
|
||||
'totalReplies'
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
[ScopeNames.WITH_ACCOUNT]: {
|
||||
include: [
|
||||
|
@ -267,26 +291,47 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
|
|||
return VideoCommentModel.scope([ ScopeNames.WITH_IN_REPLY_TO, ScopeNames.WITH_VIDEO ]).findOne(query)
|
||||
}
|
||||
|
||||
static listThreadsForApi (videoId: number, start: number, count: number, sort: string) {
|
||||
static async listThreadsForApi (videoId: number, start: number, count: number, sort: string, user?: UserModel) {
|
||||
const serverActor = await getServerActor()
|
||||
const serverAccountId = serverActor.Account.id
|
||||
const userAccountId = user.Account.id
|
||||
|
||||
const query = {
|
||||
offset: start,
|
||||
limit: count,
|
||||
order: getSort(sort),
|
||||
where: {
|
||||
videoId,
|
||||
inReplyToCommentId: null
|
||||
inReplyToCommentId: null,
|
||||
accountId: {
|
||||
[Sequelize.Op.notIn]: Sequelize.literal(
|
||||
'(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')'
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: typings
|
||||
const scopes: any[] = [
|
||||
ScopeNames.WITH_ACCOUNT,
|
||||
{
|
||||
method: [ ScopeNames.ATTRIBUTES_FOR_API, serverAccountId, userAccountId ]
|
||||
}
|
||||
]
|
||||
|
||||
return VideoCommentModel
|
||||
.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.ATTRIBUTES_FOR_API ])
|
||||
.scope(scopes)
|
||||
.findAndCountAll(query)
|
||||
.then(({ rows, count }) => {
|
||||
return { total: count, data: rows }
|
||||
})
|
||||
}
|
||||
|
||||
static listThreadCommentsForApi (videoId: number, threadId: number) {
|
||||
static async listThreadCommentsForApi (videoId: number, threadId: number, user?: UserModel) {
|
||||
const serverActor = await getServerActor()
|
||||
const serverAccountId = serverActor.Account.id
|
||||
const userAccountId = user.Account.id
|
||||
|
||||
const query = {
|
||||
order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ],
|
||||
where: {
|
||||
|
@ -294,12 +339,24 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
|
|||
[ Sequelize.Op.or ]: [
|
||||
{ id: threadId },
|
||||
{ originCommentId: threadId }
|
||||
]
|
||||
],
|
||||
accountId: {
|
||||
[Sequelize.Op.notIn]: Sequelize.literal(
|
||||
'(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')'
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const scopes: any[] = [
|
||||
ScopeNames.WITH_ACCOUNT,
|
||||
{
|
||||
method: [ ScopeNames.ATTRIBUTES_FOR_API, serverAccountId, userAccountId ]
|
||||
}
|
||||
]
|
||||
|
||||
return VideoCommentModel
|
||||
.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.ATTRIBUTES_FOR_API ])
|
||||
.scope(scopes)
|
||||
.findAndCountAll(query)
|
||||
.then(({ rows, count }) => {
|
||||
return { total: count, data: rows }
|
||||
|
|
|
@ -27,7 +27,7 @@ import {
|
|||
Table,
|
||||
UpdatedAt
|
||||
} from 'sequelize-typescript'
|
||||
import { VideoPrivacy, VideoState } from '../../../shared'
|
||||
import { UserRight, VideoPrivacy, VideoState } from '../../../shared'
|
||||
import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
|
||||
import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
|
||||
import { VideoFilter } from '../../../shared/models/videos/video-query.type'
|
||||
|
@ -70,7 +70,7 @@ import { AccountVideoRateModel } from '../account/account-video-rate'
|
|||
import { ActorModel } from '../activitypub/actor'
|
||||
import { AvatarModel } from '../avatar/avatar'
|
||||
import { ServerModel } from '../server/server'
|
||||
import { buildTrigramSearchIndex, createSimilarityAttribute, getVideoSort, throwIfNotValid } from '../utils'
|
||||
import { buildBlockedAccountSQL, buildTrigramSearchIndex, createSimilarityAttribute, getVideoSort, throwIfNotValid } from '../utils'
|
||||
import { TagModel } from './tag'
|
||||
import { VideoAbuseModel } from './video-abuse'
|
||||
import { VideoChannelModel } from './video-channel'
|
||||
|
@ -93,6 +93,7 @@ import {
|
|||
} from './video-format-utils'
|
||||
import * as validator from 'validator'
|
||||
import { UserVideoHistoryModel } from '../account/user-video-history'
|
||||
import { UserModel } from '../account/user'
|
||||
|
||||
// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
|
||||
const indexes: Sequelize.DefineIndexesOptions[] = [
|
||||
|
@ -138,6 +139,7 @@ type ForAPIOptions = {
|
|||
}
|
||||
|
||||
type AvailableForListIDsOptions = {
|
||||
serverAccountId: number
|
||||
actorId: number
|
||||
includeLocalVideos: boolean
|
||||
filter?: VideoFilter
|
||||
|
@ -151,6 +153,7 @@ type AvailableForListIDsOptions = {
|
|||
accountId?: number
|
||||
videoChannelId?: number
|
||||
trendingDays?: number
|
||||
user?: UserModel
|
||||
}
|
||||
|
||||
@Scopes({
|
||||
|
@ -235,6 +238,15 @@ type AvailableForListIDsOptions = {
|
|||
)
|
||||
}
|
||||
]
|
||||
},
|
||||
channelId: {
|
||||
[ Sequelize.Op.notIn ]: Sequelize.literal(
|
||||
'(' +
|
||||
'SELECT id FROM "videoChannel" WHERE "accountId" IN (' +
|
||||
buildBlockedAccountSQL(options.serverAccountId, options.user ? options.user.Account.id : undefined) +
|
||||
')' +
|
||||
')'
|
||||
)
|
||||
}
|
||||
},
|
||||
include: []
|
||||
|
@ -975,10 +987,10 @@ export class VideoModel extends Model<VideoModel> {
|
|||
videoChannelId?: number,
|
||||
actorId?: number
|
||||
trendingDays?: number,
|
||||
userId?: number
|
||||
user?: UserModel
|
||||
}, countVideos = true) {
|
||||
if (options.filter && options.filter === 'all-local' && !options.userId) {
|
||||
throw new Error('Try to filter all-local but no userId is provided')
|
||||
if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) {
|
||||
throw new Error('Try to filter all-local but no user has not the see all videos right')
|
||||
}
|
||||
|
||||
const query: IFindOptions<VideoModel> = {
|
||||
|
@ -994,11 +1006,14 @@ export class VideoModel extends Model<VideoModel> {
|
|||
query.group = 'VideoModel.id'
|
||||
}
|
||||
|
||||
const serverActor = await getServerActor()
|
||||
|
||||
// actorId === null has a meaning, so just check undefined
|
||||
const actorId = options.actorId !== undefined ? options.actorId : (await getServerActor()).id
|
||||
const actorId = options.actorId !== undefined ? options.actorId : serverActor.id
|
||||
|
||||
const queryOptions = {
|
||||
actorId,
|
||||
serverAccountId: serverActor.Account.id,
|
||||
nsfw: options.nsfw,
|
||||
categoryOneOf: options.categoryOneOf,
|
||||
licenceOneOf: options.licenceOneOf,
|
||||
|
@ -1010,7 +1025,7 @@ export class VideoModel extends Model<VideoModel> {
|
|||
accountId: options.accountId,
|
||||
videoChannelId: options.videoChannelId,
|
||||
includeLocalVideos: options.includeLocalVideos,
|
||||
userId: options.userId,
|
||||
user: options.user,
|
||||
trendingDays
|
||||
}
|
||||
|
||||
|
@ -1033,7 +1048,7 @@ export class VideoModel extends Model<VideoModel> {
|
|||
tagsAllOf?: string[]
|
||||
durationMin?: number // seconds
|
||||
durationMax?: number // seconds
|
||||
userId?: number,
|
||||
user?: UserModel,
|
||||
filter?: VideoFilter
|
||||
}) {
|
||||
const whereAnd = []
|
||||
|
@ -1104,6 +1119,7 @@ export class VideoModel extends Model<VideoModel> {
|
|||
const serverActor = await getServerActor()
|
||||
const queryOptions = {
|
||||
actorId: serverActor.id,
|
||||
serverAccountId: serverActor.Account.id,
|
||||
includeLocalVideos: options.includeLocalVideos,
|
||||
nsfw: options.nsfw,
|
||||
categoryOneOf: options.categoryOneOf,
|
||||
|
@ -1111,7 +1127,7 @@ export class VideoModel extends Model<VideoModel> {
|
|||
languageOneOf: options.languageOneOf,
|
||||
tagsOneOf: options.tagsOneOf,
|
||||
tagsAllOf: options.tagsAllOf,
|
||||
userId: options.userId,
|
||||
user: options.user,
|
||||
filter: options.filter
|
||||
}
|
||||
|
||||
|
@ -1287,7 +1303,7 @@ export class VideoModel extends Model<VideoModel> {
|
|||
|
||||
private static async getAvailableForApi (
|
||||
query: IFindOptions<VideoModel>,
|
||||
options: AvailableForListIDsOptions & { userId?: number},
|
||||
options: AvailableForListIDsOptions,
|
||||
countVideos = true
|
||||
) {
|
||||
const idsScope = {
|
||||
|
@ -1320,8 +1336,8 @@ export class VideoModel extends Model<VideoModel> {
|
|||
}
|
||||
]
|
||||
|
||||
if (options.userId) {
|
||||
apiScope.push({ method: [ ScopeNames.WITH_USER_HISTORY, options.userId ] })
|
||||
if (options.user) {
|
||||
apiScope.push({ method: [ ScopeNames.WITH_USER_HISTORY, options.user.id ] })
|
||||
}
|
||||
|
||||
const secondQuery = {
|
||||
|
|
222
server/tests/api/check-params/blocklist.ts
Normal file
222
server/tests/api/check-params/blocklist.ts
Normal file
|
@ -0,0 +1,222 @@
|
|||
/* tslint:disable:no-unused-expression */
|
||||
|
||||
import 'mocha'
|
||||
|
||||
import {
|
||||
createUser,
|
||||
doubleFollow,
|
||||
flushAndRunMultipleServers,
|
||||
flushTests,
|
||||
killallServers,
|
||||
makeDeleteRequest,
|
||||
makeGetRequest,
|
||||
makePostBodyRequest,
|
||||
ServerInfo,
|
||||
setAccessTokensToServers
|
||||
} from '../../utils'
|
||||
import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
|
||||
|
||||
describe('Test blocklist API validators', function () {
|
||||
let servers: ServerInfo[]
|
||||
let server: ServerInfo
|
||||
|
||||
before(async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
await flushTests()
|
||||
|
||||
servers = await flushAndRunMultipleServers(2)
|
||||
await setAccessTokensToServers(servers)
|
||||
|
||||
server = servers[0]
|
||||
|
||||
const user = { username: 'user1', password: 'password' }
|
||||
await createUser(server.url, server.accessToken, user.username, user.password)
|
||||
|
||||
await doubleFollow(servers[0], servers[1])
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
describe('When managing user blocklist', function () {
|
||||
const path = '/api/v1/users/me/blocklist/accounts'
|
||||
|
||||
describe('When managing user accounts blocklist', function () {
|
||||
|
||||
describe('When listing blocked accounts', function () {
|
||||
it('Should fail with an unauthenticated user', async function () {
|
||||
await makeGetRequest({
|
||||
url: server.url,
|
||||
path,
|
||||
statusCodeExpected: 401
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail with a bad start pagination', async function () {
|
||||
await checkBadStartPagination(server.url, path, server.accessToken)
|
||||
})
|
||||
|
||||
it('Should fail with a bad count pagination', async function () {
|
||||
await checkBadCountPagination(server.url, path, server.accessToken)
|
||||
})
|
||||
|
||||
it('Should fail with an incorrect sort', async function () {
|
||||
await checkBadSortPagination(server.url, path, server.accessToken)
|
||||
})
|
||||
})
|
||||
|
||||
describe('When blocking an account', function () {
|
||||
it('Should fail with an unauthenticated user', async function () {
|
||||
await makePostBodyRequest({
|
||||
url: server.url,
|
||||
path,
|
||||
fields: { accountName: 'user1' },
|
||||
statusCodeExpected: 401
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail with an unknown account', async function () {
|
||||
await makePostBodyRequest({
|
||||
url: server.url,
|
||||
token: server.accessToken,
|
||||
path,
|
||||
fields: { accountName: 'user2' },
|
||||
statusCodeExpected: 404
|
||||
})
|
||||
})
|
||||
|
||||
it('Should succeed with the correct params', async function () {
|
||||
await makePostBodyRequest({
|
||||
url: server.url,
|
||||
token: server.accessToken,
|
||||
path,
|
||||
fields: { accountName: 'user1' },
|
||||
statusCodeExpected: 204
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('When unblocking an account', function () {
|
||||
it('Should fail with an unauthenticated user', async function () {
|
||||
await makeDeleteRequest({
|
||||
url: server.url,
|
||||
path: path + '/user1',
|
||||
statusCodeExpected: 401
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail with an unknown account block', async function () {
|
||||
await makeDeleteRequest({
|
||||
url: server.url,
|
||||
path: path + '/user2',
|
||||
token: server.accessToken,
|
||||
statusCodeExpected: 404
|
||||
})
|
||||
})
|
||||
|
||||
it('Should succeed with the correct params', async function () {
|
||||
await makeDeleteRequest({
|
||||
url: server.url,
|
||||
path: path + '/user1',
|
||||
token: server.accessToken,
|
||||
statusCodeExpected: 204
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('When managing user servers blocklist', function () {
|
||||
const path = '/api/v1/users/me/blocklist/servers'
|
||||
|
||||
describe('When listing blocked servers', function () {
|
||||
it('Should fail with an unauthenticated user', async function () {
|
||||
await makeGetRequest({
|
||||
url: server.url,
|
||||
path,
|
||||
statusCodeExpected: 401
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail with a bad start pagination', async function () {
|
||||
await checkBadStartPagination(server.url, path, server.accessToken)
|
||||
})
|
||||
|
||||
it('Should fail with a bad count pagination', async function () {
|
||||
await checkBadCountPagination(server.url, path, server.accessToken)
|
||||
})
|
||||
|
||||
it('Should fail with an incorrect sort', async function () {
|
||||
await checkBadSortPagination(server.url, path, server.accessToken)
|
||||
})
|
||||
})
|
||||
|
||||
describe('When blocking a server', function () {
|
||||
it('Should fail with an unauthenticated user', async function () {
|
||||
await makePostBodyRequest({
|
||||
url: server.url,
|
||||
path,
|
||||
fields: { host: 'localhost:9002' },
|
||||
statusCodeExpected: 401
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail with an unknown server', async function () {
|
||||
await makePostBodyRequest({
|
||||
url: server.url,
|
||||
token: server.accessToken,
|
||||
path,
|
||||
fields: { host: 'localhost:9003' },
|
||||
statusCodeExpected: 404
|
||||
})
|
||||
})
|
||||
|
||||
it('Should succeed with the correct params', async function () {
|
||||
await makePostBodyRequest({
|
||||
url: server.url,
|
||||
token: server.accessToken,
|
||||
path,
|
||||
fields: { host: 'localhost:9002' },
|
||||
statusCodeExpected: 204
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('When unblocking a server', function () {
|
||||
it('Should fail with an unauthenticated user', async function () {
|
||||
await makeDeleteRequest({
|
||||
url: server.url,
|
||||
path: path + '/localhost:9002',
|
||||
statusCodeExpected: 401
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail with an unknown server block', async function () {
|
||||
await makeDeleteRequest({
|
||||
url: server.url,
|
||||
path: path + '/localhost:9003',
|
||||
token: server.accessToken,
|
||||
statusCodeExpected: 404
|
||||
})
|
||||
})
|
||||
|
||||
it('Should succeed with the correct params', async function () {
|
||||
await makeDeleteRequest({
|
||||
url: server.url,
|
||||
path: path + '/localhost:9002',
|
||||
token: server.accessToken,
|
||||
statusCodeExpected: 204
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
killallServers(servers)
|
||||
|
||||
// Keep the logs if the test failed
|
||||
if (this['ok']) {
|
||||
await flushTests()
|
||||
}
|
||||
})
|
||||
})
|
|
@ -1,5 +1,6 @@
|
|||
// Order of the tests we want to execute
|
||||
import './accounts'
|
||||
import './blocklist'
|
||||
import './config'
|
||||
import './follows'
|
||||
import './jobs'
|
||||
|
|
294
server/tests/api/users/account-blocklist.ts
Normal file
294
server/tests/api/users/account-blocklist.ts
Normal file
|
@ -0,0 +1,294 @@
|
|||
/* tslint:disable:no-unused-expression */
|
||||
|
||||
import * as chai from 'chai'
|
||||
import 'mocha'
|
||||
import { AccountBlock, ServerBlock, Video } from '../../../../shared/index'
|
||||
import {
|
||||
createUser,
|
||||
doubleFollow,
|
||||
flushAndRunMultipleServers,
|
||||
flushTests,
|
||||
killallServers,
|
||||
ServerInfo,
|
||||
uploadVideo,
|
||||
userLogin
|
||||
} from '../../utils/index'
|
||||
import { setAccessTokensToServers } from '../../utils/users/login'
|
||||
import { getVideosListWithToken } from '../../utils/videos/videos'
|
||||
import {
|
||||
addVideoCommentReply,
|
||||
addVideoCommentThread,
|
||||
getVideoCommentThreads,
|
||||
getVideoThreadComments
|
||||
} from '../../utils/videos/video-comments'
|
||||
import { waitJobs } from '../../utils/server/jobs'
|
||||
import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
|
||||
import {
|
||||
addAccountToAccountBlocklist,
|
||||
addServerToAccountBlocklist,
|
||||
getAccountBlocklistByAccount, getServerBlocklistByAccount,
|
||||
removeAccountFromAccountBlocklist,
|
||||
removeServerFromAccountBlocklist
|
||||
} from '../../utils/users/blocklist'
|
||||
|
||||
const expect = chai.expect
|
||||
|
||||
async function checkAllVideos (url: string, token: string) {
|
||||
const res = await getVideosListWithToken(url, token)
|
||||
|
||||
expect(res.body.data).to.have.lengthOf(4)
|
||||
}
|
||||
|
||||
async function checkAllComments (url: string, token: string, videoUUID: string) {
|
||||
const resThreads = await getVideoCommentThreads(url, videoUUID, 0, 5, '-createdAt', token)
|
||||
|
||||
const threads: VideoComment[] = resThreads.body.data
|
||||
expect(threads).to.have.lengthOf(2)
|
||||
|
||||
for (const thread of threads) {
|
||||
const res = await getVideoThreadComments(url, videoUUID, thread.id, token)
|
||||
|
||||
const tree: VideoCommentThreadTree = res.body
|
||||
expect(tree.children).to.have.lengthOf(1)
|
||||
}
|
||||
}
|
||||
|
||||
describe('Test accounts blocklist', function () {
|
||||
let servers: ServerInfo[]
|
||||
let videoUUID1: string
|
||||
let videoUUID2: string
|
||||
let userToken1: string
|
||||
let userToken2: string
|
||||
|
||||
before(async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
await flushTests()
|
||||
|
||||
servers = await flushAndRunMultipleServers(2)
|
||||
await setAccessTokensToServers(servers)
|
||||
|
||||
{
|
||||
const user = { username: 'user1', password: 'password' }
|
||||
await createUser(servers[0].url, servers[0].accessToken, user.username, user.password)
|
||||
|
||||
userToken1 = await userLogin(servers[0], user)
|
||||
await uploadVideo(servers[0].url, userToken1, { name: 'video user 1' })
|
||||
}
|
||||
|
||||
{
|
||||
const user = { username: 'user2', password: 'password' }
|
||||
await createUser(servers[1].url, servers[1].accessToken, user.username, user.password)
|
||||
|
||||
userToken2 = await userLogin(servers[1], user)
|
||||
await uploadVideo(servers[1].url, userToken2, { name: 'video user 2' })
|
||||
}
|
||||
|
||||
{
|
||||
const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video server 1' })
|
||||
videoUUID1 = res.body.video.uuid
|
||||
}
|
||||
|
||||
{
|
||||
const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video server 2' })
|
||||
videoUUID2 = res.body.video.uuid
|
||||
}
|
||||
|
||||
await doubleFollow(servers[0], servers[1])
|
||||
|
||||
{
|
||||
const resComment = await addVideoCommentThread(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID1, 'comment root 1')
|
||||
const resReply = await addVideoCommentReply(servers[ 0 ].url, userToken1, videoUUID1, resComment.body.comment.id, 'comment user 1')
|
||||
await addVideoCommentReply(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID1, resReply.body.comment.id, 'comment root 1')
|
||||
}
|
||||
|
||||
{
|
||||
const resComment = await addVideoCommentThread(servers[ 0 ].url, userToken1, videoUUID1, 'comment user 1')
|
||||
await addVideoCommentReply(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID1, resComment.body.comment.id, 'comment root 1')
|
||||
}
|
||||
|
||||
await waitJobs(servers)
|
||||
})
|
||||
|
||||
describe('When managing account blocklist', function () {
|
||||
it('Should list all videos', function () {
|
||||
return checkAllVideos(servers[0].url, servers[0].accessToken)
|
||||
})
|
||||
|
||||
it('Should list the comments', function () {
|
||||
return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
|
||||
})
|
||||
|
||||
it('Should block a remote account', async function () {
|
||||
await addAccountToAccountBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:9002')
|
||||
})
|
||||
|
||||
it('Should hide its videos', async function () {
|
||||
const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
|
||||
|
||||
const videos: Video[] = res.body.data
|
||||
expect(videos).to.have.lengthOf(3)
|
||||
|
||||
const v = videos.find(v => v.name === 'video user 2')
|
||||
expect(v).to.be.undefined
|
||||
})
|
||||
|
||||
it('Should block a local account', async function () {
|
||||
await addAccountToAccountBlocklist(servers[0].url, servers[0].accessToken, 'user1')
|
||||
})
|
||||
|
||||
it('Should hide its videos', async function () {
|
||||
const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
|
||||
|
||||
const videos: Video[] = res.body.data
|
||||
expect(videos).to.have.lengthOf(2)
|
||||
|
||||
const v = videos.find(v => v.name === 'video user 1')
|
||||
expect(v).to.be.undefined
|
||||
})
|
||||
|
||||
it('Should hide its comments', async function () {
|
||||
const resThreads = await getVideoCommentThreads(servers[0].url, videoUUID1, 0, 5, '-createdAt', servers[0].accessToken)
|
||||
|
||||
const threads: VideoComment[] = resThreads.body.data
|
||||
expect(threads).to.have.lengthOf(1)
|
||||
expect(threads[0].totalReplies).to.equal(0)
|
||||
|
||||
const t = threads.find(t => t.text === 'comment user 1')
|
||||
expect(t).to.be.undefined
|
||||
|
||||
for (const thread of threads) {
|
||||
const res = await getVideoThreadComments(servers[0].url, videoUUID1, thread.id, servers[0].accessToken)
|
||||
|
||||
const tree: VideoCommentThreadTree = res.body
|
||||
expect(tree.children).to.have.lengthOf(0)
|
||||
}
|
||||
})
|
||||
|
||||
it('Should list all the videos with another user', async function () {
|
||||
return checkAllVideos(servers[0].url, userToken1)
|
||||
})
|
||||
|
||||
it('Should list all the comments with another user', async function () {
|
||||
return checkAllComments(servers[0].url, userToken1, videoUUID1)
|
||||
})
|
||||
|
||||
it('Should list blocked accounts', async function () {
|
||||
{
|
||||
const res = await getAccountBlocklistByAccount(servers[ 0 ].url, servers[ 0 ].accessToken, 0, 1, 'createdAt')
|
||||
const blocks: AccountBlock[] = res.body.data
|
||||
|
||||
expect(res.body.total).to.equal(2)
|
||||
|
||||
const block = blocks[0]
|
||||
expect(block.byAccount.displayName).to.equal('root')
|
||||
expect(block.byAccount.name).to.equal('root')
|
||||
expect(block.accountBlocked.displayName).to.equal('user2')
|
||||
expect(block.accountBlocked.name).to.equal('user2')
|
||||
expect(block.accountBlocked.host).to.equal('localhost:9002')
|
||||
}
|
||||
|
||||
{
|
||||
const res = await getAccountBlocklistByAccount(servers[ 0 ].url, servers[ 0 ].accessToken, 1, 2, 'createdAt')
|
||||
const blocks: AccountBlock[] = res.body.data
|
||||
|
||||
expect(res.body.total).to.equal(2)
|
||||
|
||||
const block = blocks[0]
|
||||
expect(block.byAccount.displayName).to.equal('root')
|
||||
expect(block.byAccount.name).to.equal('root')
|
||||
expect(block.accountBlocked.displayName).to.equal('user1')
|
||||
expect(block.accountBlocked.name).to.equal('user1')
|
||||
expect(block.accountBlocked.host).to.equal('localhost:9001')
|
||||
}
|
||||
})
|
||||
|
||||
it('Should unblock the remote account', async function () {
|
||||
await removeAccountFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:9002')
|
||||
})
|
||||
|
||||
it('Should display its videos', async function () {
|
||||
const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
|
||||
|
||||
const videos: Video[] = res.body.data
|
||||
expect(videos).to.have.lengthOf(3)
|
||||
|
||||
const v = videos.find(v => v.name === 'video user 2')
|
||||
expect(v).not.to.be.undefined
|
||||
})
|
||||
|
||||
it('Should unblock the local account', async function () {
|
||||
await removeAccountFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'user1')
|
||||
})
|
||||
|
||||
it('Should display its comments', function () {
|
||||
return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('When managing server blocklist', function () {
|
||||
it('Should list all videos', function () {
|
||||
return checkAllVideos(servers[0].url, servers[0].accessToken)
|
||||
})
|
||||
|
||||
it('Should list the comments', function () {
|
||||
return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
|
||||
})
|
||||
|
||||
it('Should block a remote server', async function () {
|
||||
await addServerToAccountBlocklist(servers[0].url, servers[0].accessToken, 'localhost:9002')
|
||||
})
|
||||
|
||||
it('Should hide its videos', async function () {
|
||||
const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
|
||||
|
||||
const videos: Video[] = res.body.data
|
||||
expect(videos).to.have.lengthOf(2)
|
||||
|
||||
const v1 = videos.find(v => v.name === 'video user 2')
|
||||
const v2 = videos.find(v => v.name === 'video server 2')
|
||||
|
||||
expect(v1).to.be.undefined
|
||||
expect(v2).to.be.undefined
|
||||
})
|
||||
|
||||
it('Should list all the videos with another user', async function () {
|
||||
return checkAllVideos(servers[0].url, userToken1)
|
||||
})
|
||||
|
||||
it('Should hide its comments')
|
||||
|
||||
it('Should list blocked servers', async function () {
|
||||
const res = await getServerBlocklistByAccount(servers[ 0 ].url, servers[ 0 ].accessToken, 0, 1, 'createdAt')
|
||||
const blocks: ServerBlock[] = res.body.data
|
||||
|
||||
expect(res.body.total).to.equal(1)
|
||||
|
||||
const block = blocks[0]
|
||||
expect(block.byAccount.displayName).to.equal('root')
|
||||
expect(block.byAccount.name).to.equal('root')
|
||||
expect(block.serverBlocked.host).to.equal('localhost:9002')
|
||||
})
|
||||
|
||||
it('Should unblock the remote server', async function () {
|
||||
await removeServerFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'localhost:9002')
|
||||
})
|
||||
|
||||
it('Should display its videos', function () {
|
||||
return checkAllVideos(servers[0].url, servers[0].accessToken)
|
||||
})
|
||||
|
||||
it('Should display its comments', function () {
|
||||
return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
|
||||
})
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
killallServers(servers)
|
||||
|
||||
// Keep the logs if the test failed
|
||||
if (this[ 'ok' ]) {
|
||||
await flushTests()
|
||||
}
|
||||
})
|
||||
})
|
|
@ -37,9 +37,7 @@ function makeDeleteRequest (options: {
|
|||
|
||||
if (options.token) req.set('Authorization', 'Bearer ' + options.token)
|
||||
|
||||
return req
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(options.statusCodeExpected)
|
||||
return req.expect(options.statusCodeExpected)
|
||||
}
|
||||
|
||||
function makeUploadRequest (options: {
|
||||
|
|
103
server/tests/utils/users/blocklist.ts
Normal file
103
server/tests/utils/users/blocklist.ts
Normal file
|
@ -0,0 +1,103 @@
|
|||
/* tslint:disable:no-unused-expression */
|
||||
|
||||
import { makeDeleteRequest, makePostBodyRequest } from '../index'
|
||||
import { makeGetRequest } from '../requests/requests'
|
||||
|
||||
function getAccountBlocklistByAccount (
|
||||
url: string,
|
||||
token: string,
|
||||
start: number,
|
||||
count: number,
|
||||
sort = '-createdAt',
|
||||
statusCodeExpected = 200
|
||||
) {
|
||||
const path = '/api/v1/users/me/blocklist/accounts'
|
||||
|
||||
return makeGetRequest({
|
||||
url,
|
||||
token,
|
||||
query: { start, count, sort },
|
||||
path,
|
||||
statusCodeExpected
|
||||
})
|
||||
}
|
||||
|
||||
function addAccountToAccountBlocklist (url: string, token: string, accountToBlock: string, statusCodeExpected = 204) {
|
||||
const path = '/api/v1/users/me/blocklist/accounts'
|
||||
|
||||
return makePostBodyRequest({
|
||||
url,
|
||||
path,
|
||||
token,
|
||||
fields: {
|
||||
accountName: accountToBlock
|
||||
},
|
||||
statusCodeExpected
|
||||
})
|
||||
}
|
||||
|
||||
function removeAccountFromAccountBlocklist (url: string, token: string, accountToUnblock: string, statusCodeExpected = 204) {
|
||||
const path = '/api/v1/users/me/blocklist/accounts/' + accountToUnblock
|
||||
|
||||
return makeDeleteRequest({
|
||||
url,
|
||||
path,
|
||||
token,
|
||||
statusCodeExpected
|
||||
})
|
||||
}
|
||||
|
||||
function getServerBlocklistByAccount (
|
||||
url: string,
|
||||
token: string,
|
||||
start: number,
|
||||
count: number,
|
||||
sort = '-createdAt',
|
||||
statusCodeExpected = 200
|
||||
) {
|
||||
const path = '/api/v1/users/me/blocklist/servers'
|
||||
|
||||
return makeGetRequest({
|
||||
url,
|
||||
token,
|
||||
query: { start, count, sort },
|
||||
path,
|
||||
statusCodeExpected
|
||||
})
|
||||
}
|
||||
|
||||
function addServerToAccountBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
|
||||
const path = '/api/v1/users/me/blocklist/servers'
|
||||
|
||||
return makePostBodyRequest({
|
||||
url,
|
||||
path,
|
||||
token,
|
||||
fields: {
|
||||
host: serverToBlock
|
||||
},
|
||||
statusCodeExpected
|
||||
})
|
||||
}
|
||||
|
||||
function removeServerFromAccountBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
|
||||
const path = '/api/v1/users/me/blocklist/servers/' + serverToBlock
|
||||
|
||||
return makeDeleteRequest({
|
||||
url,
|
||||
path,
|
||||
token,
|
||||
statusCodeExpected
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
getAccountBlocklistByAccount,
|
||||
addAccountToAccountBlocklist,
|
||||
removeAccountFromAccountBlocklist,
|
||||
getServerBlocklistByAccount,
|
||||
addServerToAccountBlocklist,
|
||||
removeServerFromAccountBlocklist
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import * as request from 'supertest'
|
||||
import { makeDeleteRequest } from '../'
|
||||
|
||||
function getVideoCommentThreads (url: string, videoId: number | string, start: number, count: number, sort?: string) {
|
||||
function getVideoCommentThreads (url: string, videoId: number | string, start: number, count: number, sort?: string, token?: string) {
|
||||
const path = '/api/v1/videos/' + videoId + '/comment-threads'
|
||||
|
||||
const req = request(url)
|
||||
|
@ -10,19 +10,23 @@ function getVideoCommentThreads (url: string, videoId: number | string, start: n
|
|||
.query({ count: count })
|
||||
|
||||
if (sort) req.query({ sort })
|
||||
if (token) req.set('Authorization', 'Bearer ' + token)
|
||||
|
||||
return req.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
.expect('Content-Type', /json/)
|
||||
}
|
||||
|
||||
function getVideoThreadComments (url: string, videoId: number | string, threadId: number) {
|
||||
function getVideoThreadComments (url: string, videoId: number | string, threadId: number, token?: string) {
|
||||
const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId
|
||||
|
||||
return request(url)
|
||||
const req = request(url)
|
||||
.get(path)
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
|
||||
if (token) req.set('Authorization', 'Bearer ' + token)
|
||||
|
||||
return req.expect(200)
|
||||
.expect('Content-Type', /json/)
|
||||
}
|
||||
|
||||
|
|
7
shared/models/blocklist/account-block.model.ts
Normal file
7
shared/models/blocklist/account-block.model.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { Account } from '../actors'
|
||||
|
||||
export interface AccountBlock {
|
||||
byAccount: Account
|
||||
accountBlocked: Account
|
||||
createdAt: Date | string
|
||||
}
|
2
shared/models/blocklist/index.ts
Normal file
2
shared/models/blocklist/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from './account-block.model'
|
||||
export * from './server-block.model'
|
9
shared/models/blocklist/server-block.model.ts
Normal file
9
shared/models/blocklist/server-block.model.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { Account } from '../actors'
|
||||
|
||||
export interface ServerBlock {
|
||||
byAccount: Account
|
||||
serverBlocked: {
|
||||
host: string
|
||||
}
|
||||
createdAt: Date | string
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
export * from './activitypub'
|
||||
export * from './actors'
|
||||
export * from './avatars'
|
||||
export * from './blocklist'
|
||||
export * from './redundancy'
|
||||
export * from './users'
|
||||
export * from './videos'
|
||||
|
|
Loading…
Reference in a new issue