mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-03 09:49:20 +02:00
Implement auto tag on comments and videos
* Comments and videos can be automatically tagged using core rules or watched word lists * These tags can be used to automatically filter videos and comments * Introduce a new video comment policy where comments must be approved first * Comments may have to be approved if the user auto block them using core rules or watched word lists * Implement FEP-5624 to federate reply control policies
This commit is contained in:
parent
b3e39df59e
commit
29329d6c45
241 changed files with 8090 additions and 1399 deletions
82
server/core/controllers/api/automatic-tags.ts
Normal file
82
server/core/controllers/api/automatic-tags.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
import { AutomaticTagPolicy, CommentAutomaticTagPoliciesUpdate, HttpStatusCode, UserRight } from '@peertube/peertube-models'
|
||||
import { AutomaticTagger } from '@server/lib/automatic-tags/automatic-tagger.js'
|
||||
import { setAccountAutomaticTagsPolicy } from '@server/lib/automatic-tags/automatic-tags.js'
|
||||
import {
|
||||
manageAccountAutomaticTagsValidator,
|
||||
updateAutomaticTagPoliciesValidator
|
||||
} from '@server/middlewares/validators/automatic-tags.js'
|
||||
import { getServerActor } from '@server/models/application/application.js'
|
||||
import express from 'express'
|
||||
import {
|
||||
apiRateLimiter,
|
||||
asyncMiddleware,
|
||||
authenticate,
|
||||
ensureUserHasRight
|
||||
} from '../../middlewares/index.js'
|
||||
|
||||
const automaticTagRouter = express.Router()
|
||||
|
||||
automaticTagRouter.use(apiRateLimiter)
|
||||
|
||||
automaticTagRouter.get('/policies/accounts/:accountName/comments',
|
||||
authenticate,
|
||||
asyncMiddleware(manageAccountAutomaticTagsValidator),
|
||||
asyncMiddleware(getAutomaticTagPolicies)
|
||||
)
|
||||
|
||||
automaticTagRouter.put('/policies/accounts/:accountName/comments',
|
||||
authenticate,
|
||||
asyncMiddleware(manageAccountAutomaticTagsValidator),
|
||||
asyncMiddleware(updateAutomaticTagPoliciesValidator),
|
||||
asyncMiddleware(updateAutomaticTagPolicies)
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
automaticTagRouter.get('/accounts/:accountName/available',
|
||||
authenticate,
|
||||
asyncMiddleware(manageAccountAutomaticTagsValidator),
|
||||
asyncMiddleware(getAccountAutomaticTagAvailable)
|
||||
)
|
||||
|
||||
automaticTagRouter.get('/server/available',
|
||||
authenticate,
|
||||
ensureUserHasRight(UserRight.MANAGE_INSTANCE_AUTO_TAGS),
|
||||
asyncMiddleware(getServerAutomaticTagAvailable)
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
automaticTagRouter
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function getAutomaticTagPolicies (req: express.Request, res: express.Response) {
|
||||
const result = await AutomaticTagger.getAutomaticTagPolicies(res.locals.account)
|
||||
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
async function updateAutomaticTagPolicies (req: express.Request, res: express.Response) {
|
||||
await setAccountAutomaticTagsPolicy({
|
||||
account: res.locals.account,
|
||||
policy: AutomaticTagPolicy.REVIEW_COMMENT,
|
||||
tags: (req.body as CommentAutomaticTagPoliciesUpdate).review
|
||||
})
|
||||
|
||||
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
||||
}
|
||||
|
||||
async function getAccountAutomaticTagAvailable (req: express.Request, res: express.Response) {
|
||||
const result = await AutomaticTagger.getAutomaticTagAvailable(res.locals.account)
|
||||
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
async function getServerAutomaticTagAvailable (req: express.Request, res: express.Response) {
|
||||
const result = await AutomaticTagger.getAutomaticTagAvailable((await getServerActor()).Account)
|
||||
|
||||
return res.json(result)
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
import { HttpStatusCode } from '@peertube/peertube-models'
|
||||
import { logger } from '@server/helpers/logger.js'
|
||||
import cors from 'cors'
|
||||
import express from 'express'
|
||||
import { logger } from '@server/helpers/logger.js'
|
||||
import { HttpStatusCode } from '@peertube/peertube-models'
|
||||
import { abuseRouter } from './abuse.js'
|
||||
import { accountsRouter } from './accounts.js'
|
||||
import { automaticTagRouter } from './automatic-tags.js'
|
||||
import { blocklistRouter } from './blocklist.js'
|
||||
import { bulkRouter } from './bulk.js'
|
||||
import { configRouter } from './config.js'
|
||||
|
@ -21,6 +22,7 @@ import { videoChannelSyncRouter } from './video-channel-sync.js'
|
|||
import { videoChannelRouter } from './video-channel.js'
|
||||
import { videoPlaylistRouter } from './video-playlist.js'
|
||||
import { videosRouter } from './videos/index.js'
|
||||
import { watchedWordsRouter } from './watched-words.js'
|
||||
|
||||
const apiRouter = express.Router()
|
||||
|
||||
|
@ -49,6 +51,8 @@ apiRouter.use('/plugins', pluginRouter)
|
|||
apiRouter.use('/custom-pages', customPageRouter)
|
||||
apiRouter.use('/blocklist', blocklistRouter)
|
||||
apiRouter.use('/runners', runnersRouter)
|
||||
apiRouter.use('/watched-words', watchedWordsRouter)
|
||||
apiRouter.use('/automatic-tags', automaticTagRouter)
|
||||
|
||||
apiRouter.use('/ping', pong)
|
||||
apiRouter.use('/*', badRequest)
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
import 'multer'
|
||||
import express from 'express'
|
||||
import { pick } from '@peertube/peertube-core-utils'
|
||||
import {
|
||||
ActorImageType,
|
||||
UserVideoRate as FormattedUserVideoRate,
|
||||
HttpStatusCode,
|
||||
UserUpdateMe,
|
||||
UserVideoQuota,
|
||||
UserVideoRate as FormattedUserVideoRate
|
||||
UserVideoQuota
|
||||
} from '@peertube/peertube-models'
|
||||
import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '@server/helpers/audit-logger.js'
|
||||
import { Hooks } from '@server/lib/plugins/hooks.js'
|
||||
import { AttributesOnly } from '@peertube/peertube-typescript-utils'
|
||||
import { UserAuditView, auditLoggerFactory, getAuditIdFromRes } from '@server/helpers/audit-logger.js'
|
||||
import { Hooks } from '@server/lib/plugins/hooks.js'
|
||||
import { VideoCommentModel } from '@server/models/video/video-comment.js'
|
||||
import express from 'express'
|
||||
import 'multer'
|
||||
import { createReqFiles } from '../../../helpers/express-utils.js'
|
||||
import { getFormattedObjects } from '../../../helpers/utils.js'
|
||||
import { CONFIG } from '../../../initializers/config.js'
|
||||
|
@ -34,6 +35,7 @@ import { updateAvatarValidator } from '../../../middlewares/validators/actor-ima
|
|||
import {
|
||||
deleteMeValidator,
|
||||
getMyVideoImportsValidator,
|
||||
listCommentsOnUserVideosValidator,
|
||||
usersVideosValidator,
|
||||
videoImportsSortValidator,
|
||||
videosSortValidator
|
||||
|
@ -75,6 +77,16 @@ meRouter.get('/me/videos/imports',
|
|||
asyncMiddleware(getUserVideoImports)
|
||||
)
|
||||
|
||||
meRouter.get('/me/videos/comments',
|
||||
authenticate,
|
||||
paginationValidator,
|
||||
videosSortValidator,
|
||||
setDefaultVideosSort,
|
||||
setDefaultPagination,
|
||||
asyncMiddleware(listCommentsOnUserVideosValidator),
|
||||
asyncMiddleware(listCommentsOnUserVideos)
|
||||
)
|
||||
|
||||
meRouter.get('/me/videos',
|
||||
authenticate,
|
||||
paginationValidator,
|
||||
|
@ -82,7 +94,7 @@ meRouter.get('/me/videos',
|
|||
setDefaultVideosSort,
|
||||
setDefaultPagination,
|
||||
asyncMiddleware(usersVideosValidator),
|
||||
asyncMiddleware(getUserVideos)
|
||||
asyncMiddleware(listUserVideos)
|
||||
)
|
||||
|
||||
meRouter.get('/me/videos/:videoId/rating',
|
||||
|
@ -117,7 +129,7 @@ export {
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function getUserVideos (req: express.Request, res: express.Response) {
|
||||
async function listUserVideos (req: express.Request, res: express.Response) {
|
||||
const user = res.locals.oauth.token.User
|
||||
|
||||
const apiOptions = await Hooks.wrapObject({
|
||||
|
@ -145,6 +157,36 @@ async function getUserVideos (req: express.Request, res: express.Response) {
|
|||
return res.json(getFormattedObjects(resultList.data, resultList.total, { additionalAttributes }))
|
||||
}
|
||||
|
||||
async function listCommentsOnUserVideos (req: express.Request, res: express.Response) {
|
||||
const userAccount = res.locals.oauth.token.User.Account
|
||||
|
||||
const options = {
|
||||
...pick(req.query, [
|
||||
'start',
|
||||
'count',
|
||||
'sort',
|
||||
'search',
|
||||
'searchAccount',
|
||||
'searchVideo',
|
||||
'autoTagOneOf'
|
||||
]),
|
||||
|
||||
autoTagOfAccountId: userAccount.id,
|
||||
videoAccountOwnerId: userAccount.id,
|
||||
heldForReview: req.query.isHeldForReview,
|
||||
|
||||
videoChannelOwnerId: res.locals.videoChannel?.id,
|
||||
videoId: res.locals.videoAll?.id
|
||||
}
|
||||
|
||||
const resultList = await VideoCommentModel.listCommentsForApi(options)
|
||||
|
||||
return res.json({
|
||||
total: resultList.total,
|
||||
data: resultList.data.map(c => c.toFormattedForAdminOrUserJSON())
|
||||
})
|
||||
}
|
||||
|
||||
async function getUserVideoImports (req: express.Request, res: express.Response) {
|
||||
const user = res.locals.oauth.token.User
|
||||
const resultList = await VideoImportModel.listUserVideoImportsForApi({
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
import express from 'express'
|
||||
import { pick } from '@peertube/peertube-core-utils'
|
||||
import {
|
||||
HttpStatusCode,
|
||||
ResultList,
|
||||
ThreadsResultList,
|
||||
UserRight,
|
||||
VideoCommentCreate,
|
||||
VideoCommentPolicy,
|
||||
VideoCommentThreads
|
||||
} from '@peertube/peertube-models'
|
||||
import { getServerActor } from '@server/models/application/application.js'
|
||||
import { MCommentFormattable } from '@server/types/models/index.js'
|
||||
import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger.js'
|
||||
import express from 'express'
|
||||
import { CommentAuditView, auditLoggerFactory, getAuditIdFromRes } from '../../../helpers/audit-logger.js'
|
||||
import { getFormattedObjects } from '../../../helpers/utils.js'
|
||||
import { sequelizeTypescript } from '../../../initializers/database.js'
|
||||
import { Notifier } from '../../../lib/notifier/index.js'
|
||||
import { Hooks } from '../../../lib/plugins/hooks.js'
|
||||
import { buildFormattedCommentTree, createVideoComment, removeComment } from '../../../lib/video-comment.js'
|
||||
import { approveComment, buildFormattedCommentTree, createLocalVideoComment, removeComment } from '../../../lib/video-comment.js'
|
||||
import {
|
||||
asyncMiddleware,
|
||||
asyncRetryTransactionMiddleware,
|
||||
|
@ -27,14 +29,14 @@ import {
|
|||
import {
|
||||
addVideoCommentReplyValidator,
|
||||
addVideoCommentThreadValidator,
|
||||
listVideoCommentsValidator,
|
||||
approveVideoCommentValidator,
|
||||
listAllVideoCommentsForAdminValidator,
|
||||
listVideoCommentThreadsValidator,
|
||||
listVideoThreadCommentsValidator,
|
||||
removeVideoCommentValidator,
|
||||
videoCommentsValidator,
|
||||
videoCommentThreadsSortValidator
|
||||
videoCommentThreadsSortValidator,
|
||||
videoCommentsValidator
|
||||
} from '../../../middlewares/validators/index.js'
|
||||
import { AccountModel } from '../../../models/account/account.js'
|
||||
import { VideoCommentModel } from '../../../models/video/video-comment.js'
|
||||
|
||||
const auditLogger = auditLoggerFactory('comments')
|
||||
|
@ -71,6 +73,12 @@ videoCommentRouter.delete('/:videoId/comments/:commentId',
|
|||
asyncRetryTransactionMiddleware(removeVideoComment)
|
||||
)
|
||||
|
||||
videoCommentRouter.post('/:videoId/comments/:commentId/approve',
|
||||
authenticate,
|
||||
asyncMiddleware(approveVideoCommentValidator),
|
||||
asyncMiddleware(approveVideoComment)
|
||||
)
|
||||
|
||||
videoCommentRouter.get('/comments',
|
||||
authenticate,
|
||||
ensureUserHasRight(UserRight.SEE_ALL_COMMENTS),
|
||||
|
@ -78,7 +86,7 @@ videoCommentRouter.get('/comments',
|
|||
videoCommentsValidator,
|
||||
setDefaultSort,
|
||||
setDefaultPagination,
|
||||
listVideoCommentsValidator,
|
||||
asyncMiddleware(listAllVideoCommentsForAdminValidator),
|
||||
asyncMiddleware(listComments)
|
||||
)
|
||||
|
||||
|
@ -92,22 +100,29 @@ export {
|
|||
|
||||
async function listComments (req: express.Request, res: express.Response) {
|
||||
const options = {
|
||||
start: req.query.start,
|
||||
count: req.query.count,
|
||||
sort: req.query.sort,
|
||||
...pick(req.query, [
|
||||
'start',
|
||||
'count',
|
||||
'sort',
|
||||
'isLocal',
|
||||
'onLocalVideo',
|
||||
'search',
|
||||
'searchAccount',
|
||||
'searchVideo',
|
||||
'autoTagOneOf'
|
||||
]),
|
||||
|
||||
isLocal: req.query.isLocal,
|
||||
onLocalVideo: req.query.onLocalVideo,
|
||||
search: req.query.search,
|
||||
searchAccount: req.query.searchAccount,
|
||||
searchVideo: req.query.searchVideo
|
||||
videoId: res.locals.onlyImmutableVideo?.id,
|
||||
videoChannelOwnerId: res.locals.videoChannel?.id,
|
||||
autoTagOfAccountId: (await getServerActor()).Account.id,
|
||||
heldForReview: undefined
|
||||
}
|
||||
|
||||
const resultList = await VideoCommentModel.listCommentsForApi(options)
|
||||
|
||||
return res.json({
|
||||
total: resultList.total,
|
||||
data: resultList.data.map(c => c.toFormattedAdminJSON())
|
||||
data: resultList.data.map(c => c.toFormattedForAdminOrUserJSON())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -117,10 +132,9 @@ async function listVideoThreads (req: express.Request, res: express.Response) {
|
|||
|
||||
let resultList: ThreadsResultList<MCommentFormattable>
|
||||
|
||||
if (video.commentsEnabled === true) {
|
||||
if (video.commentsPolicy !== VideoCommentPolicy.DISABLED) {
|
||||
const apiOptions = await Hooks.wrapObject({
|
||||
videoId: video.id,
|
||||
isVideoOwned: video.isOwned(),
|
||||
video,
|
||||
start: req.query.start,
|
||||
count: req.query.count,
|
||||
sort: req.query.sort,
|
||||
|
@ -152,9 +166,9 @@ async function listVideoThreadComments (req: express.Request, res: express.Respo
|
|||
|
||||
let resultList: ResultList<MCommentFormattable>
|
||||
|
||||
if (video.commentsEnabled === true) {
|
||||
if (video.commentsPolicy !== VideoCommentPolicy.DISABLED) {
|
||||
const apiOptions = await Hooks.wrapObject({
|
||||
videoId: video.id,
|
||||
video,
|
||||
threadId: res.locals.videoCommentThread.id,
|
||||
user
|
||||
}, 'filter:api.video-thread-comments.list.params')
|
||||
|
@ -184,15 +198,11 @@ async function listVideoThreadComments (req: express.Request, res: express.Respo
|
|||
async function addVideoCommentThread (req: express.Request, res: express.Response) {
|
||||
const videoCommentInfo: VideoCommentCreate = req.body
|
||||
|
||||
const comment = await sequelizeTypescript.transaction(async t => {
|
||||
const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
|
||||
|
||||
return createVideoComment({
|
||||
text: videoCommentInfo.text,
|
||||
inReplyToComment: null,
|
||||
video: res.locals.videoAll,
|
||||
account
|
||||
}, t)
|
||||
const comment = await createLocalVideoComment({
|
||||
text: videoCommentInfo.text,
|
||||
inReplyToComment: null,
|
||||
video: res.locals.videoAll,
|
||||
user: res.locals.oauth.token.User
|
||||
})
|
||||
|
||||
Notifier.Instance.notifyOnNewComment(comment)
|
||||
|
@ -206,15 +216,11 @@ async function addVideoCommentThread (req: express.Request, res: express.Respons
|
|||
async function addVideoCommentReply (req: express.Request, res: express.Response) {
|
||||
const videoCommentInfo: VideoCommentCreate = req.body
|
||||
|
||||
const comment = await sequelizeTypescript.transaction(async t => {
|
||||
const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
|
||||
|
||||
return createVideoComment({
|
||||
text: videoCommentInfo.text,
|
||||
inReplyToComment: res.locals.videoCommentFull,
|
||||
video: res.locals.videoAll,
|
||||
account
|
||||
}, t)
|
||||
const comment = await createLocalVideoComment({
|
||||
text: videoCommentInfo.text,
|
||||
inReplyToComment: res.locals.videoCommentFull,
|
||||
video: res.locals.videoAll,
|
||||
user: res.locals.oauth.token.User
|
||||
})
|
||||
|
||||
Notifier.Instance.notifyOnNewComment(comment)
|
||||
|
@ -226,13 +232,17 @@ async function addVideoCommentReply (req: express.Request, res: express.Response
|
|||
}
|
||||
|
||||
async function removeVideoComment (req: express.Request, res: express.Response) {
|
||||
const videoCommentInstance = res.locals.videoCommentFull
|
||||
const comment = res.locals.videoCommentFull
|
||||
|
||||
await removeComment(videoCommentInstance, req, res)
|
||||
await removeComment(comment, req, res)
|
||||
|
||||
auditLogger.delete(getAuditIdFromRes(res), new CommentAuditView(videoCommentInstance.toFormattedJSON()))
|
||||
auditLogger.delete(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON()))
|
||||
|
||||
return res.type('json')
|
||||
.status(HttpStatusCode.NO_CONTENT_204)
|
||||
.end()
|
||||
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
||||
}
|
||||
|
||||
async function approveVideoComment (req: express.Request, res: express.Response) {
|
||||
await approveComment(res.locals.videoCommentFull)
|
||||
|
||||
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { forceNumber } from '@peertube/peertube-core-utils'
|
||||
import { HttpStatusCode, ThumbnailType, VideoPrivacy, VideoPrivacyType, VideoUpdate } from '@peertube/peertube-models'
|
||||
import { HttpStatusCode, ThumbnailType, VideoCommentPolicy, VideoPrivacy, VideoPrivacyType, VideoUpdate } from '@peertube/peertube-models'
|
||||
import { exists } from '@server/helpers/custom-validators/misc.js'
|
||||
import { changeVideoChannelShare } from '@server/lib/activitypub/share.js'
|
||||
import { isNewVideoPrivacyForFederation, isPrivacyForFederation } from '@server/lib/activitypub/videos/federate.js'
|
||||
import { AutomaticTagger } from '@server/lib/automatic-tags/automatic-tagger.js'
|
||||
import { setAndSaveVideoAutomaticTags } from '@server/lib/automatic-tags/automatic-tags.js'
|
||||
import { updateLocalVideoMiniatureFromExisting } from '@server/lib/thumbnail.js'
|
||||
import { replaceChaptersFromDescriptionIfNeeded } from '@server/lib/video-chapters.js'
|
||||
import { addVideoJobsAfterUpdate } from '@server/lib/video-jobs.js'
|
||||
|
@ -65,6 +67,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
|
|||
// Refresh video since thumbnails to prevent concurrent updates
|
||||
const video = await VideoModel.loadFull(videoFromReq.id, t)
|
||||
|
||||
const oldName = video.name
|
||||
const oldDescription = video.description
|
||||
const oldVideoChannel = video.VideoChannel
|
||||
|
||||
|
@ -77,7 +80,6 @@ async function updateVideo (req: express.Request, res: express.Response) {
|
|||
'waitTranscoding',
|
||||
'support',
|
||||
'description',
|
||||
'commentsEnabled',
|
||||
'downloadEnabled'
|
||||
]
|
||||
|
||||
|
@ -85,6 +87,15 @@ async function updateVideo (req: express.Request, res: express.Response) {
|
|||
if (videoInfoToUpdate[key] !== undefined) video.set(key, videoInfoToUpdate[key])
|
||||
}
|
||||
|
||||
// Special treatment for comments policy to support deprecated commentsEnabled attribute
|
||||
if (videoInfoToUpdate.commentsPolicy !== undefined) {
|
||||
video.commentsPolicy = videoInfoToUpdate.commentsPolicy
|
||||
} else if (videoInfoToUpdate.commentsEnabled === true) {
|
||||
video.commentsPolicy = VideoCommentPolicy.ENABLED
|
||||
} else if (videoInfoToUpdate.commentsEnabled === false) {
|
||||
video.commentsPolicy = VideoCommentPolicy.DISABLED
|
||||
}
|
||||
|
||||
if (videoInfoToUpdate.originallyPublishedAt !== undefined) {
|
||||
video.originallyPublishedAt = videoInfoToUpdate.originallyPublishedAt
|
||||
? new Date(videoInfoToUpdate.originallyPublishedAt)
|
||||
|
@ -142,6 +153,11 @@ async function updateVideo (req: express.Request, res: express.Response) {
|
|||
})
|
||||
}
|
||||
|
||||
if (oldName !== video.name || oldDescription !== video.description) {
|
||||
const automaticTags = await new AutomaticTagger().buildVideoAutomaticTags({ video, transaction: t })
|
||||
await setAndSaveVideoAutomaticTags({ video, automaticTags, transaction: t })
|
||||
}
|
||||
|
||||
await autoBlacklistVideoIfNeeded({
|
||||
video: videoInstanceUpdated,
|
||||
user: res.locals.oauth.token.User,
|
||||
|
|
162
server/core/controllers/api/watched-words.ts
Normal file
162
server/core/controllers/api/watched-words.ts
Normal file
|
@ -0,0 +1,162 @@
|
|||
import { HttpStatusCode, UserRight } from '@peertube/peertube-models'
|
||||
import { Awaitable } from '@peertube/peertube-typescript-utils'
|
||||
import {
|
||||
addWatchedWordsListValidatorFactory,
|
||||
getWatchedWordsListValidatorFactory,
|
||||
manageAccountWatchedWordsListValidator,
|
||||
updateWatchedWordsListValidatorFactory
|
||||
} from '@server/middlewares/validators/watched-words.js'
|
||||
import { getServerActor } from '@server/models/application/application.js'
|
||||
import { WatchedWordsListModel } from '@server/models/watched-words/watched-words-list.js'
|
||||
import { MAccountId } from '@server/types/models/index.js'
|
||||
import express from 'express'
|
||||
import { getFormattedObjects } from '../../helpers/utils.js'
|
||||
import {
|
||||
apiRateLimiter,
|
||||
asyncMiddleware,
|
||||
authenticate, ensureUserHasRight, paginationValidator,
|
||||
setDefaultPagination,
|
||||
setDefaultSort,
|
||||
watchedWordsListsSortValidator
|
||||
} from '../../middlewares/index.js'
|
||||
|
||||
const watchedWordsRouter = express.Router()
|
||||
|
||||
watchedWordsRouter.use(apiRateLimiter)
|
||||
|
||||
{
|
||||
const common = [
|
||||
authenticate,
|
||||
paginationValidator,
|
||||
watchedWordsListsSortValidator,
|
||||
setDefaultSort,
|
||||
setDefaultPagination
|
||||
]
|
||||
|
||||
watchedWordsRouter.get('/accounts/:accountName/lists',
|
||||
...common,
|
||||
|
||||
asyncMiddleware(manageAccountWatchedWordsListValidator),
|
||||
asyncMiddleware(listWatchedWordsListsFactory(res => res.locals.account))
|
||||
)
|
||||
|
||||
watchedWordsRouter.get('/server/lists',
|
||||
...common,
|
||||
|
||||
ensureUserHasRight(UserRight.MANAGE_INSTANCE_WATCHED_WORDS),
|
||||
asyncMiddleware(listWatchedWordsListsFactory(() => getServerActor().then(a => a.Account)))
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
{
|
||||
watchedWordsRouter.post('/accounts/:accountName/lists',
|
||||
authenticate,
|
||||
asyncMiddleware(manageAccountWatchedWordsListValidator),
|
||||
asyncMiddleware(addWatchedWordsListValidatorFactory(res => res.locals.account)),
|
||||
asyncMiddleware(addWatchedWordsListFactory(res => res.locals.account))
|
||||
)
|
||||
|
||||
watchedWordsRouter.post('/server/lists',
|
||||
authenticate,
|
||||
ensureUserHasRight(UserRight.MANAGE_INSTANCE_WATCHED_WORDS),
|
||||
asyncMiddleware(addWatchedWordsListValidatorFactory(() => getServerActor().then(a => a.Account))),
|
||||
asyncMiddleware(addWatchedWordsListFactory(() => getServerActor().then(a => a.Account)))
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
{
|
||||
watchedWordsRouter.put('/accounts/:accountName/lists/:listId',
|
||||
authenticate,
|
||||
asyncMiddleware(manageAccountWatchedWordsListValidator),
|
||||
asyncMiddleware(getWatchedWordsListValidatorFactory(res => res.locals.account)),
|
||||
asyncMiddleware(updateWatchedWordsListValidatorFactory(res => res.locals.account)),
|
||||
asyncMiddleware(updateWatchedWordsList)
|
||||
)
|
||||
|
||||
watchedWordsRouter.put('/server/lists/:listId',
|
||||
authenticate,
|
||||
ensureUserHasRight(UserRight.MANAGE_INSTANCE_WATCHED_WORDS),
|
||||
asyncMiddleware(getWatchedWordsListValidatorFactory(() => getServerActor().then(a => a.Account))),
|
||||
asyncMiddleware(updateWatchedWordsListValidatorFactory(() => getServerActor().then(a => a.Account))),
|
||||
asyncMiddleware(updateWatchedWordsList)
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
{
|
||||
watchedWordsRouter.delete('/accounts/:accountName/lists/:listId',
|
||||
authenticate,
|
||||
asyncMiddleware(manageAccountWatchedWordsListValidator),
|
||||
asyncMiddleware(getWatchedWordsListValidatorFactory(res => res.locals.account)),
|
||||
asyncMiddleware(deleteWatchedWordsList)
|
||||
)
|
||||
|
||||
watchedWordsRouter.delete('/server/lists/:listId',
|
||||
authenticate,
|
||||
ensureUserHasRight(UserRight.MANAGE_INSTANCE_WATCHED_WORDS),
|
||||
asyncMiddleware(getWatchedWordsListValidatorFactory(() => getServerActor().then(a => a.Account))),
|
||||
asyncMiddleware(deleteWatchedWordsList)
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
watchedWordsRouter
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function listWatchedWordsListsFactory (accountGetter: (res: express.Response) => Awaitable<MAccountId>) {
|
||||
return async (req: express.Request, res: express.Response) => {
|
||||
const resultList = await WatchedWordsListModel.listForAPI({
|
||||
accountId: (await accountGetter(res)).id,
|
||||
start: req.query.start,
|
||||
count: req.query.count,
|
||||
sort: req.query.sort
|
||||
})
|
||||
|
||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||
}
|
||||
}
|
||||
|
||||
function addWatchedWordsListFactory (accountGetter: (res: express.Response) => Awaitable<MAccountId>) {
|
||||
return async (req: express.Request, res: express.Response) => {
|
||||
const list = await WatchedWordsListModel.createList({
|
||||
accountId: (await accountGetter(res)).id,
|
||||
|
||||
listName: req.body.listName,
|
||||
words: req.body.words
|
||||
})
|
||||
|
||||
return res.json({
|
||||
watchedWordsList: {
|
||||
id: list.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function updateWatchedWordsList (req: express.Request, res: express.Response) {
|
||||
const list = res.locals.watchedWordsList
|
||||
|
||||
await list.updateList({
|
||||
listName: req.body.listName,
|
||||
words: req.body.words
|
||||
})
|
||||
|
||||
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
||||
}
|
||||
|
||||
async function deleteWatchedWordsList (req: express.Request, res: express.Response) {
|
||||
const list = res.locals.watchedWordsList
|
||||
|
||||
await list.destroy()
|
||||
|
||||
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue