follows.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import express from 'express'
  2. import { body, param, query } from 'express-validator'
  3. import { isProdInstance } from '@server/helpers/core-utils'
  4. import { isEachUniqueHandleValid, isFollowStateValid, isRemoteHandleValid } from '@server/helpers/custom-validators/follows'
  5. import { loadActorUrlOrGetFromWebfinger } from '@server/lib/activitypub/actors'
  6. import { getRemoteNameAndHost } from '@server/lib/activitypub/follow'
  7. import { getServerActor } from '@server/models/application/application'
  8. import { MActorFollowActorsDefault } from '@server/types/models'
  9. import { ServerFollowCreate } from '@shared/models'
  10. import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
  11. import { isActorTypeValid, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
  12. import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers'
  13. import { logger } from '../../helpers/logger'
  14. import { WEBSERVER } from '../../initializers/constants'
  15. import { ActorModel } from '../../models/actor/actor'
  16. import { ActorFollowModel } from '../../models/actor/actor-follow'
  17. import { areValidationErrors } from './shared'
  18. const listFollowsValidator = [
  19. query('state')
  20. .optional()
  21. .custom(isFollowStateValid),
  22. query('actorType')
  23. .optional()
  24. .custom(isActorTypeValid),
  25. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  26. if (areValidationErrors(req, res)) return
  27. return next()
  28. }
  29. ]
  30. const followValidator = [
  31. body('hosts')
  32. .toArray()
  33. .custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
  34. body('handles')
  35. .toArray()
  36. .custom(isEachUniqueHandleValid).withMessage('Should have an array of handles'),
  37. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  38. // Force https if the administrator wants to follow remote actors
  39. if (isProdInstance() && WEBSERVER.SCHEME === 'http') {
  40. return res
  41. .status(HttpStatusCode.INTERNAL_SERVER_ERROR_500)
  42. .json({
  43. error: 'Cannot follow on a non HTTPS web server.'
  44. })
  45. }
  46. if (areValidationErrors(req, res)) return
  47. const body: ServerFollowCreate = req.body
  48. if (body.hosts.length === 0 && body.handles.length === 0) {
  49. return res
  50. .status(HttpStatusCode.BAD_REQUEST_400)
  51. .json({
  52. error: 'You must provide at least one handle or one host.'
  53. })
  54. }
  55. return next()
  56. }
  57. ]
  58. const removeFollowingValidator = [
  59. param('hostOrHandle')
  60. .custom(value => isHostValid(value) || isRemoteHandleValid(value)),
  61. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  62. if (areValidationErrors(req, res)) return
  63. const serverActor = await getServerActor()
  64. const { name, host } = getRemoteNameAndHost(req.params.hostOrHandle)
  65. const follow = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI({
  66. actorId: serverActor.id,
  67. targetName: name,
  68. targetHost: host
  69. })
  70. if (!follow) {
  71. return res.fail({
  72. status: HttpStatusCode.NOT_FOUND_404,
  73. message: `Follow ${req.params.hostOrHandle} not found.`
  74. })
  75. }
  76. res.locals.follow = follow
  77. return next()
  78. }
  79. ]
  80. const getFollowerValidator = [
  81. param('nameWithHost')
  82. .custom(isValidActorHandle),
  83. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  84. if (areValidationErrors(req, res)) return
  85. let follow: MActorFollowActorsDefault
  86. try {
  87. const actorUrl = await loadActorUrlOrGetFromWebfinger(req.params.nameWithHost)
  88. const actor = await ActorModel.loadByUrl(actorUrl)
  89. const serverActor = await getServerActor()
  90. follow = await ActorFollowModel.loadByActorAndTarget(actor.id, serverActor.id)
  91. } catch (err) {
  92. logger.warn('Cannot get actor from handle.', { handle: req.params.nameWithHost, err })
  93. }
  94. if (!follow) {
  95. return res.fail({
  96. status: HttpStatusCode.NOT_FOUND_404,
  97. message: `Follower ${req.params.nameWithHost} not found.`
  98. })
  99. }
  100. res.locals.follow = follow
  101. return next()
  102. }
  103. ]
  104. const acceptFollowerValidator = [
  105. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  106. const follow = res.locals.follow
  107. if (follow.state !== 'pending' && follow.state !== 'rejected') {
  108. return res.fail({ message: 'Follow is not in pending/rejected state.' })
  109. }
  110. return next()
  111. }
  112. ]
  113. const rejectFollowerValidator = [
  114. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  115. const follow = res.locals.follow
  116. if (follow.state !== 'pending' && follow.state !== 'accepted') {
  117. return res.fail({ message: 'Follow is not in pending/accepted state.' })
  118. }
  119. return next()
  120. }
  121. ]
  122. // ---------------------------------------------------------------------------
  123. export {
  124. followValidator,
  125. removeFollowingValidator,
  126. getFollowerValidator,
  127. acceptFollowerValidator,
  128. rejectFollowerValidator,
  129. listFollowsValidator
  130. }