Browse Source

Fix incorrect IDs in AP federation

pull/3334/head
Chocobozzz 1 week ago
parent
commit
de94ac86a2
No known key found for this signature in database GPG Key ID: 583A612D890159BE
34 changed files with 278 additions and 233 deletions
  1. +10
    -10
      scripts/update-host.ts
  2. +39
    -17
      server/controllers/activitypub/client.ts
  3. +1
    -1
      server/controllers/api/server/follows.ts
  4. +23
    -23
      server/controllers/api/video-playlist.ts
  5. +2
    -2
      server/controllers/api/videos/import.ts
  6. +2
    -2
      server/controllers/api/videos/index.ts
  7. +2
    -2
      server/controllers/api/videos/live.ts
  8. +3
    -3
      server/controllers/api/videos/rate.ts
  9. +3
    -3
      server/initializers/migrations/0100-activitypub.ts
  10. +8
    -8
      server/lib/activitypub/process/process-dislike.ts
  11. +11
    -5
      server/lib/activitypub/process/process-follow.ts
  12. +5
    -8
      server/lib/activitypub/process/process-like.ts
  13. +5
    -6
      server/lib/activitypub/send/send-accept.ts
  14. +2
    -2
      server/lib/activitypub/send/send-dislike.ts
  15. +2
    -2
      server/lib/activitypub/send/send-flag.ts
  16. +3
    -5
      server/lib/activitypub/send/send-follow.ts
  17. +2
    -2
      server/lib/activitypub/send/send-like.ts
  18. +5
    -6
      server/lib/activitypub/send/send-reject.ts
  19. +15
    -15
      server/lib/activitypub/send/send-undo.ts
  20. +5
    -5
      server/lib/activitypub/send/send-view.ts
  21. +3
    -3
      server/lib/activitypub/share.ts
  22. +47
    -46
      server/lib/activitypub/url.ts
  23. +5
    -5
      server/lib/activitypub/video-rates.ts
  24. +2
    -0
      server/lib/job-queue/handlers/activitypub-follow.ts
  25. +19
    -18
      server/lib/schedulers/videos-redundancy-scheduler.ts
  26. +2
    -2
      server/lib/user.ts
  27. +4
    -4
      server/lib/video-channel.ts
  28. +3
    -3
      server/lib/video-comment.ts
  29. +3
    -3
      server/lib/video-playlist.ts
  30. +0
    -1
      server/middlewares/validators/videos/video-rates.ts
  31. +2
    -1
      server/models/account/account-video-rate.ts
  32. +24
    -12
      server/models/activitypub/actor-follow.ts
  33. +8
    -8
      server/models/video/video-format-utils.ts
  34. +8
    -0
      server/tests/api/server/follows-moderation.ts

+ 10
- 10
scripts/update-host.ts View File

@@ -6,11 +6,11 @@ import { ActorFollowModel } from '../server/models/activitypub/actor-follow'
import { VideoModel } from '../server/models/video/video'
import { ActorModel } from '../server/models/activitypub/actor'
import {
getAccountActivityPubUrl,
getVideoActivityPubUrl,
getVideoAnnounceActivityPubUrl,
getVideoChannelActivityPubUrl,
getVideoCommentActivityPubUrl
getLocalAccountActivityPubUrl,
getLocalVideoActivityPubUrl,
getLocalVideoAnnounceActivityPubUrl,
getLocalVideoChannelActivityPubUrl,
getLocalVideoCommentActivityPubUrl
} from '../server/lib/activitypub/url'
import { VideoShareModel } from '../server/models/video/video-share'
import { VideoCommentModel } from '../server/models/video/video-comment'
@@ -62,8 +62,8 @@ async function run () {
console.log('Updating actor ' + actor.url)

const newUrl = actor.Account
? getAccountActivityPubUrl(actor.preferredUsername)
: getVideoChannelActivityPubUrl(actor.preferredUsername)
? getLocalAccountActivityPubUrl(actor.preferredUsername)
: getLocalVideoChannelActivityPubUrl(actor.preferredUsername)

actor.url = newUrl
actor.inboxUrl = newUrl + '/inbox'
@@ -85,7 +85,7 @@ async function run () {

console.log('Updating video share ' + videoShare.url)

videoShare.url = getVideoAnnounceActivityPubUrl(videoShare.Actor, videoShare.Video)
videoShare.url = getLocalVideoAnnounceActivityPubUrl(videoShare.Actor, videoShare.Video)
await videoShare.save()
}

@@ -110,7 +110,7 @@ async function run () {

console.log('Updating comment ' + comment.url)

comment.url = getVideoCommentActivityPubUrl(comment.Video, comment)
comment.url = getLocalVideoCommentActivityPubUrl(comment.Video, comment)
await comment.save()
}

@@ -120,7 +120,7 @@ async function run () {
for (const video of videos) {
console.log('Updating video ' + video.uuid)

video.url = getVideoActivityPubUrl(video)
video.url = getLocalVideoActivityPubUrl(video)
await video.save()

for (const file of video.VideoFiles) {


+ 39
- 17
server/controllers/activitypub/client.ts View File

@@ -1,8 +1,7 @@
import * as cors from 'cors'
import * as express from 'express'
import { getRateUrl } from '@server/lib/activitypub/video-rates'
import { getServerActor } from '@server/models/application/application'
import { MAccountId, MActorId, MChannelId, MVideoId } from '@server/types/models'
import { MAccountId, MActorId, MChannelId, MVideoId, MVideoUrl } from '@server/types/models'
import { VideoPrivacy, VideoRateType } from '../../../shared/models/videos'
import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
import { activityPubCollectionPagination, activityPubContextify } from '../../helpers/activitypub'
@@ -12,10 +11,10 @@ import { buildAnnounceWithVideoAudience, buildLikeActivity } from '../../lib/act
import { buildCreateActivity } from '../../lib/activitypub/send/send-create'
import { buildDislikeActivity } from '../../lib/activitypub/send/send-dislike'
import {
getVideoCommentsActivityPubUrl,
getVideoDislikesActivityPubUrl,
getVideoLikesActivityPubUrl,
getVideoSharesActivityPubUrl
getLocalVideoCommentsActivityPubUrl,
getLocalVideoDislikesActivityPubUrl,
getLocalVideoLikesActivityPubUrl,
getLocalVideoSharesActivityPubUrl
} from '../../lib/activitypub/url'
import {
asyncMiddleware,
@@ -212,10 +211,9 @@ function getAccountVideoRateFactory (rateType: VideoRateType) {
const accountVideoRate = res.locals.accountVideoRate

const byActor = accountVideoRate.Account.Actor
const url = getRateUrl(rateType, byActor, accountVideoRate.Video)
const APObject = rateType === 'like'
? buildLikeActivity(url, byActor, accountVideoRate.Video)
: buildDislikeActivity(url, byActor, accountVideoRate.Video)
? buildLikeActivity(accountVideoRate.url, byActor, accountVideoRate.Video)
: buildDislikeActivity(accountVideoRate.url, byActor, accountVideoRate.Video)

return activityPubResponse(activityPubContextify(APObject), res)
}
@@ -225,7 +223,7 @@ async function videoController (req: express.Request, res: express.Response) {
// We need more attributes
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(res.locals.onlyVideoWithRights.id)

if (video.url.startsWith(WEBSERVER.URL) === false) return res.redirect(video.url)
if (redirectIfNotOwned(video.url, res)) return

// We need captions to render AP object
const captions = await VideoCaptionModel.listVideoCaptions(video.id)
@@ -245,7 +243,7 @@ async function videoController (req: express.Request, res: express.Response) {
async function videoAnnounceController (req: express.Request, res: express.Response) {
const share = res.locals.videoShare

if (share.url.startsWith(WEBSERVER.URL) === false) return res.redirect(share.url)
if (redirectIfNotOwned(share.url, res)) return

const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.videoAll, undefined)

@@ -255,6 +253,8 @@ async function videoAnnounceController (req: express.Request, res: express.Respo
async function videoAnnouncesController (req: express.Request, res: express.Response) {
const video = res.locals.onlyImmutableVideo

if (redirectIfNotOwned(video.url, res)) return

const handler = async (start: number, count: number) => {
const result = await VideoShareModel.listAndCountByVideoId(video.id, start, count)
return {
@@ -262,21 +262,27 @@ async function videoAnnouncesController (req: express.Request, res: express.Resp
data: result.rows.map(r => r.url)
}
}
const json = await activityPubCollectionPagination(getVideoSharesActivityPubUrl(video), handler, req.query.page)
const json = await activityPubCollectionPagination(getLocalVideoSharesActivityPubUrl(video), handler, req.query.page)

return activityPubResponse(activityPubContextify(json), res)
}

async function videoLikesController (req: express.Request, res: express.Response) {
const video = res.locals.onlyImmutableVideo
const json = await videoRates(req, 'like', video, getVideoLikesActivityPubUrl(video))

if (redirectIfNotOwned(video.url, res)) return

const json = await videoRates(req, 'like', video, getLocalVideoLikesActivityPubUrl(video))

return activityPubResponse(activityPubContextify(json), res)
}

async function videoDislikesController (req: express.Request, res: express.Response) {
const video = res.locals.onlyImmutableVideo
const json = await videoRates(req, 'dislike', video, getVideoDislikesActivityPubUrl(video))

if (redirectIfNotOwned(video.url, res)) return

const json = await videoRates(req, 'dislike', video, getLocalVideoDislikesActivityPubUrl(video))

return activityPubResponse(activityPubContextify(json), res)
}
@@ -284,6 +290,8 @@ async function videoDislikesController (req: express.Request, res: express.Respo
async function videoCommentsController (req: express.Request, res: express.Response) {
const video = res.locals.onlyImmutableVideo

if (redirectIfNotOwned(video.url, res)) return

const handler = async (start: number, count: number) => {
const result = await VideoCommentModel.listAndCountByVideoForAP(video, start, count)
return {
@@ -291,7 +299,7 @@ async function videoCommentsController (req: express.Request, res: express.Respo
data: result.rows.map(r => r.url)
}
}
const json = await activityPubCollectionPagination(getVideoCommentsActivityPubUrl(video), handler, req.query.page)
const json = await activityPubCollectionPagination(getLocalVideoCommentsActivityPubUrl(video), handler, req.query.page)

return activityPubResponse(activityPubContextify(json), res)
}
@@ -319,7 +327,7 @@ async function videoChannelFollowingController (req: express.Request, res: expre
async function videoCommentController (req: express.Request, res: express.Response) {
const videoComment = res.locals.videoCommentFull

if (videoComment.url.startsWith(WEBSERVER.URL) === false) return res.redirect(videoComment.url)
if (redirectIfNotOwned(videoComment.url, res)) return

const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, undefined)
const isPublic = true // Comments are always public
@@ -340,7 +348,8 @@ async function videoCommentController (req: express.Request, res: express.Respon

async function videoRedundancyController (req: express.Request, res: express.Response) {
const videoRedundancy = res.locals.videoRedundancy
if (videoRedundancy.url.startsWith(WEBSERVER.URL) === false) return res.redirect(videoRedundancy.url)

if (redirectIfNotOwned(videoRedundancy.url, res)) return

const serverActor = await getServerActor()

@@ -358,6 +367,8 @@ async function videoRedundancyController (req: express.Request, res: express.Res
async function videoPlaylistController (req: express.Request, res: express.Response) {
const playlist = res.locals.videoPlaylistFull

if (redirectIfNotOwned(playlist.url, res)) return

// We need more attributes
playlist.OwnerAccount = await AccountModel.load(playlist.ownerAccountId)

@@ -371,6 +382,8 @@ async function videoPlaylistController (req: express.Request, res: express.Respo
function videoPlaylistElementController (req: express.Request, res: express.Response) {
const videoPlaylistElement = res.locals.videoPlaylistElementAP

if (redirectIfNotOwned(videoPlaylistElement.url, res)) return

const json = videoPlaylistElement.toActivityPubObject()
return activityPubResponse(activityPubContextify(json), res)
}
@@ -411,3 +424,12 @@ function videoRates (req: express.Request, rateType: VideoRateType, video: MVide
}
return activityPubCollectionPagination(url, handler, req.query.page)
}

function redirectIfNotOwned (url: string, res: express.Response) {
if (url.startsWith(WEBSERVER.URL) === false) {
res.redirect(url)
return true
}

return false
}

+ 1
- 1
server/controllers/api/server/follows.ts View File

@@ -165,7 +165,7 @@ async function removeFollowing (req: express.Request, res: express.Response) {
async function removeOrRejectFollower (req: express.Request, res: express.Response) {
const follow = res.locals.follow

await sendReject(follow.ActorFollower, follow.ActorFollowing)
await sendReject(follow.url, follow.ActorFollower, follow.ActorFollowing)

await follow.destroy()



+ 23
- 23
server/controllers/api/video-playlist.ts View File

@@ -1,5 +1,24 @@
import * as express from 'express'
import { join } from 'path'
import { getServerActor } from '@server/models/application/application'
import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models'
import { VideoPlaylistCreate } from '../../../shared/models/videos/playlist/video-playlist-create.model'
import { VideoPlaylistElementCreate } from '../../../shared/models/videos/playlist/video-playlist-element-create.model'
import { VideoPlaylistElementUpdate } from '../../../shared/models/videos/playlist/video-playlist-element-update.model'
import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
import { VideoPlaylistReorder } from '../../../shared/models/videos/playlist/video-playlist-reorder.model'
import { VideoPlaylistUpdate } from '../../../shared/models/videos/playlist/video-playlist-update.model'
import { resetSequelizeInstance } from '../../helpers/database-utils'
import { buildNSFWFilter, createReqFiles } from '../../helpers/express-utils'
import { logger } from '../../helpers/logger'
import { getFormattedObjects } from '../../helpers/utils'
import { CONFIG } from '../../initializers/config'
import { MIMETYPES, VIDEO_PLAYLIST_PRIVACIES } from '../../initializers/constants'
import { sequelizeTypescript } from '../../initializers/database'
import { sendCreateVideoPlaylist, sendDeleteVideoPlaylist, sendUpdateVideoPlaylist } from '../../lib/activitypub/send'
import { getLocalVideoPlaylistActivityPubUrl, getLocalVideoPlaylistElementActivityPubUrl } from '../../lib/activitypub/url'
import { JobQueue } from '../../lib/job-queue'
import { createPlaylistMiniatureFromExisting } from '../../lib/thumbnail'
import {
asyncMiddleware,
asyncRetryTransactionMiddleware,
@@ -10,11 +29,6 @@ import {
setDefaultSort
} from '../../middlewares'
import { videoPlaylistsSortValidator } from '../../middlewares/validators'
import { buildNSFWFilter, createReqFiles } from '../../helpers/express-utils'
import { MIMETYPES, VIDEO_PLAYLIST_PRIVACIES } from '../../initializers/constants'
import { logger } from '../../helpers/logger'
import { resetSequelizeInstance } from '../../helpers/database-utils'
import { VideoPlaylistModel } from '../../models/video/video-playlist'
import {
commonVideoPlaylistFiltersValidator,
videoPlaylistsAddValidator,
@@ -25,23 +39,9 @@ import {
videoPlaylistsUpdateOrRemoveVideoValidator,
videoPlaylistsUpdateValidator
} from '../../middlewares/validators/videos/video-playlists'
import { VideoPlaylistCreate } from '../../../shared/models/videos/playlist/video-playlist-create.model'
import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
import { join } from 'path'
import { sendCreateVideoPlaylist, sendDeleteVideoPlaylist, sendUpdateVideoPlaylist } from '../../lib/activitypub/send'
import { getVideoPlaylistActivityPubUrl, getVideoPlaylistElementActivityPubUrl } from '../../lib/activitypub/url'
import { VideoPlaylistUpdate } from '../../../shared/models/videos/playlist/video-playlist-update.model'
import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element'
import { VideoPlaylistElementCreate } from '../../../shared/models/videos/playlist/video-playlist-element-create.model'
import { VideoPlaylistElementUpdate } from '../../../shared/models/videos/playlist/video-playlist-element-update.model'
import { AccountModel } from '../../models/account/account'
import { VideoPlaylistReorder } from '../../../shared/models/videos/playlist/video-playlist-reorder.model'
import { JobQueue } from '../../lib/job-queue'
import { CONFIG } from '../../initializers/config'
import { sequelizeTypescript } from '../../initializers/database'
import { createPlaylistMiniatureFromExisting } from '../../lib/thumbnail'
import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models'
import { getServerActor } from '@server/models/application/application'
import { VideoPlaylistModel } from '../../models/video/video-playlist'
import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element'

const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR })

@@ -161,7 +161,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) {
ownerAccountId: user.Account.id
}) as MVideoPlaylistFull

videoPlaylist.url = getVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object
videoPlaylist.url = getLocalVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object

if (videoPlaylistInfo.videoChannelId) {
const videoChannel = res.locals.videoChannel
@@ -304,7 +304,7 @@ async function addVideoInPlaylist (req: express.Request, res: express.Response)
videoId: video.id
}, { transaction: t })

playlistElement.url = getVideoPlaylistElementActivityPubUrl(videoPlaylist, playlistElement)
playlistElement.url = getLocalVideoPlaylistElementActivityPubUrl(videoPlaylist, playlistElement)
await playlistElement.save({ transaction: t })

videoPlaylist.changed('updatedAt', true)


+ 2
- 2
server/controllers/api/videos/import.ts View File

@@ -28,7 +28,7 @@ import { getYoutubeDLInfo, getYoutubeDLSubs, YoutubeDLInfo } from '../../../help
import { CONFIG } from '../../../initializers/config'
import { MIMETYPES } from '../../../initializers/constants'
import { sequelizeTypescript } from '../../../initializers/database'
import { getVideoActivityPubUrl } from '../../../lib/activitypub/url'
import { getLocalVideoActivityPubUrl } from '../../../lib/activitypub/url'
import { JobQueue } from '../../../lib/job-queue/job-queue'
import { createVideoMiniatureFromExisting, createVideoMiniatureFromUrl } from '../../../lib/thumbnail'
import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist'
@@ -250,7 +250,7 @@ function buildVideo (channelId: number, body: VideoImportCreate, importData: You
originallyPublishedAt: body.originallyPublishedAt || importData.originallyPublishedAt
}
const video = new VideoModel(videoData)
video.url = getVideoActivityPubUrl(video)
video.url = getLocalVideoActivityPubUrl(video)

return video
}


+ 2
- 2
server/controllers/api/videos/index.ts View File

@@ -5,7 +5,7 @@ import toInt from 'validator/lib/toInt'
import { addOptimizeOrMergeAudioJob } from '@server/helpers/video'
import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
import { changeVideoChannelShare } from '@server/lib/activitypub/share'
import { getVideoActivityPubUrl } from '@server/lib/activitypub/url'
import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url'
import { LiveManager } from '@server/lib/live-manager'
import { buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video'
import { getVideoFilePath } from '@server/lib/video-paths'
@@ -189,7 +189,7 @@ async function addVideo (req: express.Request, res: express.Response) {
videoData.duration = videoPhysicalFile['duration'] // duration was added by a previous middleware

const video = new VideoModel(videoData) as MVideoFullLight
video.url = getVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object
video.url = getLocalVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object

const videoFile = new VideoFileModel({
extname: extname(videoPhysicalFile.filename),


+ 2
- 2
server/controllers/api/videos/live.ts View File

@@ -3,7 +3,7 @@ import { v4 as uuidv4 } from 'uuid'
import { createReqFiles } from '@server/helpers/express-utils'
import { CONFIG } from '@server/initializers/config'
import { ASSETS_PATH, MIMETYPES } from '@server/initializers/constants'
import { getVideoActivityPubUrl } from '@server/lib/activitypub/url'
import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url'
import { federateVideoIfNeeded } from '@server/lib/activitypub/videos'
import { Hooks } from '@server/lib/plugins/hooks'
import { buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video'
@@ -86,7 +86,7 @@ async function addLiveVideo (req: express.Request, res: express.Response) {
videoData.duration = 0

const video = new VideoModel(videoData) as MVideoDetails
video.url = getVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object
video.url = getLocalVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object

const videoLive = new VideoLiveModel()
videoLive.saveReplay = videoInfo.saveReplay || false


+ 3
- 3
server/controllers/api/videos/rate.ts View File

@@ -2,7 +2,7 @@ import * as express from 'express'
import { UserVideoRateUpdate } from '../../../../shared'
import { logger } from '../../../helpers/logger'
import { VIDEO_RATE_TYPES } from '../../../initializers/constants'
import { getRateUrl, sendVideoRateChange } from '../../../lib/activitypub/video-rates'
import { getLocalRateUrl, sendVideoRateChange } from '../../../lib/activitypub/video-rates'
import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoUpdateRateValidator } from '../../../middlewares'
import { AccountModel } from '../../../models/account/account'
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
@@ -52,7 +52,7 @@ async function rateVideo (req: express.Request, res: express.Response) {
await previousRate.destroy(sequelizeOptions)
} else { // Update previous rate
previousRate.type = rateType
previousRate.url = getRateUrl(rateType, userAccount.Actor, videoInstance)
previousRate.url = getLocalRateUrl(rateType, userAccount.Actor, videoInstance)
await previousRate.save(sequelizeOptions)
}
} else if (rateType !== 'none') { // There was not a previous rate, insert a new one if there is a rate
@@ -60,7 +60,7 @@ async function rateVideo (req: express.Request, res: express.Response) {
accountId: accountInstance.id,
videoId: videoInstance.id,
type: rateType,
url: getRateUrl(rateType, userAccount.Actor, videoInstance)
url: getLocalRateUrl(rateType, userAccount.Actor, videoInstance)
}

await AccountVideoRateModel.create(query, sequelizeOptions)


+ 3
- 3
server/initializers/migrations/0100-activitypub.ts View File

@@ -1,7 +1,7 @@
import * as Sequelize from 'sequelize'
import { createPrivateAndPublicKeys } from '../../helpers/peertube-crypto'
import { shareVideoByServerAndChannel } from '../../lib/activitypub/share'
import { getVideoActivityPubUrl, getVideoChannelActivityPubUrl } from '../../lib/activitypub/url'
import { getLocalVideoActivityPubUrl, getLocalVideoChannelActivityPubUrl } from '../../lib/activitypub/url'
import { createLocalAccountWithoutKeys } from '../../lib/user'
import { ApplicationModel } from '../../models/application/application'
import { SERVER_ACTOR_NAME } from '../constants'
@@ -132,7 +132,7 @@ async function up (utils: {

const videos = await db.Video.findAll()
for (const video of videos) {
video.url = getVideoActivityPubUrl(video)
video.url = getLocalVideoActivityPubUrl(video)
await video.save()
}

@@ -151,7 +151,7 @@ async function up (utils: {

const videoChannels = await db.VideoChannel.findAll()
for (const videoChannel of videoChannels) {
videoChannel.url = getVideoChannelActivityPubUrl(videoChannel)
videoChannel.url = getLocalVideoChannelActivityPubUrl(videoChannel)
await videoChannel.save()
}



+ 8
- 8
server/lib/activitypub/process/process-dislike.ts View File

@@ -3,11 +3,10 @@ import { DislikeObject } from '../../../../shared/models/activitypub/objects'
import { retryTransactionWrapper } from '../../../helpers/database-utils'
import { sequelizeTypescript } from '../../../initializers/database'
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
import { forwardVideoRelatedActivity } from '../send/utils'
import { getVideoDislikeActivityPubUrl } from '../url'
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
import { MActorSignature } from '../../../types/models'
import { forwardVideoRelatedActivity } from '../send/utils'
import { getOrCreateVideoAndAccountAndChannel } from '../videos'

async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) {
const { activity, byActor } = options
@@ -23,7 +22,10 @@ export {
// ---------------------------------------------------------------------------

async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: MActorSignature) {
const dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object
const dislikeObject = activity.type === 'Dislike'
? activity.object
: (activity.object as DislikeObject).object

const byAccount = byActor.Account

if (!byAccount) throw new Error('Cannot create dislike with the non account actor ' + byActor.url)
@@ -31,9 +33,7 @@ async function processDislike (activity: ActivityCreate | ActivityDislike, byAct
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: dislikeObject })

return sequelizeTypescript.transaction(async t => {
const url = getVideoDislikeActivityPubUrl(byActor, video)

const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, url)
const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, activity.id)
if (existingRate && existingRate.type === 'dislike') return

await video.increment('dislikes', { transaction: t })
@@ -46,7 +46,7 @@ async function processDislike (activity: ActivityCreate | ActivityDislike, byAct
rate.type = 'dislike'
rate.videoId = video.id
rate.accountId = byAccount.id
rate.url = url
rate.url = activity.id

await rate.save({ transaction: t })



+ 11
- 5
server/lib/activitypub/process/process-follow.ts View File

@@ -15,9 +15,11 @@ import { getServerActor } from '@server/models/application/application'

async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) {
const { activity, byActor } = options
const activityObject = getAPId(activity.object)

return retryTransactionWrapper(processFollow, byActor, activityObject)
const activityId = activity.id
const objectId = getAPId(activity.object)

return retryTransactionWrapper(processFollow, byActor, activityId, objectId)
}

// ---------------------------------------------------------------------------
@@ -28,7 +30,7 @@ export {

// ---------------------------------------------------------------------------

async function processFollow (byActor: MActorSignature, targetActorURL: string) {
async function processFollow (byActor: MActorSignature, activityId: string, targetActorURL: string) {
const { actorFollow, created, isFollowingInstance, targetActor } = await sequelizeTypescript.transaction(async t => {
const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t)

@@ -41,7 +43,7 @@ async function processFollow (byActor: MActorSignature, targetActorURL: string)
if (isFollowingInstance && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
logger.info('Rejecting %s because instance followers are disabled.', targetActor.url)

await sendReject(byActor, targetActor)
await sendReject(activityId, byActor, targetActor)

return { actorFollow: undefined as MActorFollowActors }
}
@@ -54,7 +56,11 @@ async function processFollow (byActor: MActorSignature, targetActorURL: string)
defaults: {
actorId: byActor.id,
targetActorId: targetActor.id,
state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted'
url: activityId,

state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL
? 'pending'
: 'accepted'
},
transaction: t
})


+ 5
- 8
server/lib/activitypub/process/process-like.ts View File

@@ -1,13 +1,12 @@
import { ActivityLike } from '../../../../shared/models/activitypub'
import { getAPId } from '../../../helpers/activitypub'
import { retryTransactionWrapper } from '../../../helpers/database-utils'
import { sequelizeTypescript } from '../../../initializers/database'
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
import { forwardVideoRelatedActivity } from '../send/utils'
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
import { getVideoLikeActivityPubUrl } from '../url'
import { getAPId } from '../../../helpers/activitypub'
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
import { MActorSignature } from '../../../types/models'
import { forwardVideoRelatedActivity } from '../send/utils'
import { getOrCreateVideoAndAccountAndChannel } from '../videos'

async function processLikeActivity (options: APProcessorOptions<ActivityLike>) {
const { activity, byActor } = options
@@ -31,9 +30,7 @@ async function processLikeVideo (byActor: MActorSignature, activity: ActivityLik
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoUrl })

return sequelizeTypescript.transaction(async t => {
const url = getVideoLikeActivityPubUrl(byActor, video)

const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, url)
const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, activity.id)
if (existingRate && existingRate.type === 'like') return

if (existingRate && existingRate.type === 'dislike') {
@@ -46,7 +43,7 @@ async function processLikeVideo (byActor: MActorSignature, activity: ActivityLik
rate.type = 'like'
rate.videoId = video.id
rate.accountId = byAccount.id
rate.url = url
rate.url = activity.id

await rate.save({ transaction: t })



+ 5
- 6
server/lib/activitypub/send/send-accept.ts View File

@@ -1,9 +1,9 @@
import { ActivityAccept, ActivityFollow } from '../../../../shared/models/activitypub'
import { getActorFollowAcceptActivityPubUrl, getActorFollowActivityPubUrl } from '../url'
import { unicastTo } from './utils'
import { buildFollowActivity } from './send-follow'
import { logger } from '../../../helpers/logger'
import { MActor, MActorFollowActors } from '../../../types/models'
import { getLocalActorFollowAcceptActivityPubUrl } from '../url'
import { buildFollowActivity } from './send-follow'
import { unicastTo } from './utils'

function sendAccept (actorFollow: MActorFollowActors) {
const follower = actorFollow.ActorFollower
@@ -16,10 +16,9 @@ function sendAccept (actorFollow: MActorFollowActors) {

logger.info('Creating job to accept follower %s.', follower.url)

const followUrl = getActorFollowActivityPubUrl(follower, me)
const followData = buildFollowActivity(followUrl, follower, me)
const followData = buildFollowActivity(actorFollow.url, follower, me)

const url = getActorFollowAcceptActivityPubUrl(actorFollow)
const url = getLocalActorFollowAcceptActivityPubUrl(actorFollow)
const data = buildAcceptActivity(url, me, followData)

return unicastTo(data, me, follower.inboxUrl)


+ 2
- 2
server/lib/activitypub/send/send-dislike.ts View File

@@ -1,5 +1,5 @@
import { Transaction } from 'sequelize'
import { getVideoDislikeActivityPubUrl } from '../url'
import { getVideoDislikeActivityPubUrlByLocalActor } from '../url'
import { logger } from '../../../helpers/logger'
import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub'
import { sendVideoRelatedActivity } from './utils'
@@ -10,7 +10,7 @@ function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction
logger.info('Creating job to dislike %s.', video.url)

const activityBuilder = (audience: ActivityAudience) => {
const url = getVideoDislikeActivityPubUrl(byActor, video)
const url = getVideoDislikeActivityPubUrlByLocalActor(byActor, video)

return buildDislikeActivity(url, byActor, video, audience)
}


+ 2
- 2
server/lib/activitypub/send/send-flag.ts View File

@@ -3,13 +3,13 @@ import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activi
import { logger } from '../../../helpers/logger'
import { MAbuseAP, MAccountLight, MActor } from '../../../types/models'
import { audiencify, getAudience } from '../audience'
import { getAbuseActivityPubUrl } from '../url'
import { getLocalAbuseActivityPubUrl } from '../url'
import { unicastTo } from './utils'

function sendAbuse (byActor: MActor, abuse: MAbuseAP, flaggedAccount: MAccountLight, t: Transaction) {
if (!flaggedAccount.Actor.serverId) return // Local user

const url = getAbuseActivityPubUrl(abuse)
const url = getLocalAbuseActivityPubUrl(abuse)

logger.info('Creating job to send abuse %s.', url)



+ 3
- 5
server/lib/activitypub/send/send-follow.ts View File

@@ -1,9 +1,8 @@
import { Transaction } from 'sequelize'
import { ActivityFollow } from '../../../../shared/models/activitypub'
import { getActorFollowActivityPubUrl } from '../url'
import { unicastTo } from './utils'
import { logger } from '../../../helpers/logger'
import { Transaction } from 'sequelize'
import { MActor, MActorFollowActors } from '../../../types/models'
import { unicastTo } from './utils'

function sendFollow (actorFollow: MActorFollowActors, t: Transaction) {
const me = actorFollow.ActorFollower
@@ -14,8 +13,7 @@ function sendFollow (actorFollow: MActorFollowActors, t: Transaction) {

logger.info('Creating job to send follow request to %s.', following.url)

const url = getActorFollowActivityPubUrl(me, following)
const data = buildFollowActivity(url, me, following)
const data = buildFollowActivity(actorFollow.url, me, following)

t.afterCommit(() => unicastTo(data, me, following.inboxUrl))
}


+ 2
- 2
server/lib/activitypub/send/send-like.ts View File

@@ -1,6 +1,6 @@
import { Transaction } from 'sequelize'
import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub'
import { getVideoLikeActivityPubUrl } from '../url'
import { getVideoLikeActivityPubUrlByLocalActor } from '../url'
import { sendVideoRelatedActivity } from './utils'
import { audiencify, getAudience } from '../audience'
import { logger } from '../../../helpers/logger'
@@ -10,7 +10,7 @@ function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
logger.info('Creating job to like %s.', video.url)

const activityBuilder = (audience: ActivityAudience) => {
const url = getVideoLikeActivityPubUrl(byActor, video)
const url = getVideoLikeActivityPubUrlByLocalActor(byActor, video)

return buildLikeActivity(url, byActor, video, audience)
}


+ 5
- 6
server/lib/activitypub/send/send-reject.ts View File

@@ -1,11 +1,11 @@
import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub'
import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url'
import { unicastTo } from './utils'
import { buildFollowActivity } from './send-follow'
import { logger } from '../../../helpers/logger'
import { MActor } from '../../../types/models'
import { getLocalActorFollowRejectActivityPubUrl } from '../url'
import { buildFollowActivity } from './send-follow'
import { unicastTo } from './utils'

function sendReject (follower: MActor, following: MActor) {
function sendReject (followUrl: string, follower: MActor, following: MActor) {
if (!follower.serverId) { // This should never happen
logger.warn('Do not sending reject to local follower.')
return
@@ -13,10 +13,9 @@ function sendReject (follower: MActor, following: MActor) {

logger.info('Creating job to reject follower %s.', follower.url)

const followUrl = getActorFollowActivityPubUrl(follower, following)
const followData = buildFollowActivity(followUrl, follower, following)

const url = getActorFollowRejectActivityPubUrl(follower, following)
const url = getLocalActorFollowRejectActivityPubUrl(follower, following)
const data = buildRejectActivity(url, following, followData)

return unicastTo(data, following, follower.inboxUrl)


+ 15
- 15
server/lib/activitypub/send/send-undo.ts View File

@@ -8,18 +8,11 @@ import {
ActivityLike,
ActivityUndo
} from '../../../../shared/models/activitypub'
import { VideoModel } from '../../../models/video/video'
import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
import { audiencify, getAudience } from '../audience'
import { buildCreateActivity } from './send-create'
import { buildFollowActivity } from './send-follow'
import { buildLikeActivity } from './send-like'
import { buildAnnounceWithVideoAudience } from './send-announce'
import { logger } from '../../../helpers/logger'
import { buildDislikeActivity } from './send-dislike'
import { VideoModel } from '../../../models/video/video'
import {
MActor, MActorAudience,
MActor,
MActorAudience,
MActorFollowActors,
MActorLight,
MVideo,
@@ -27,6 +20,14 @@ import {
MVideoRedundancyVideo,
MVideoShare
} from '../../../types/models'
import { audiencify, getAudience } from '../audience'
import { getUndoActivityPubUrl, getVideoDislikeActivityPubUrlByLocalActor, getVideoLikeActivityPubUrlByLocalActor } from '../url'
import { buildAnnounceWithVideoAudience } from './send-announce'
import { buildCreateActivity } from './send-create'
import { buildDislikeActivity } from './send-dislike'
import { buildFollowActivity } from './send-follow'
import { buildLikeActivity } from './send-like'
import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'

function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) {
const me = actorFollow.ActorFollower
@@ -37,10 +38,9 @@ function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) {

logger.info('Creating job to send an unfollow request to %s.', following.url)

const followUrl = getActorFollowActivityPubUrl(me, following)
const undoUrl = getUndoActivityPubUrl(followUrl)
const undoUrl = getUndoActivityPubUrl(actorFollow.url)

const followActivity = buildFollowActivity(followUrl, me, following)
const followActivity = buildFollowActivity(actorFollow.url, me, following)
const undoActivity = undoActivityData(undoUrl, me, followActivity)

t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl))
@@ -61,7 +61,7 @@ async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare,
async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
logger.info('Creating job to undo a like of video %s.', video.url)

const likeUrl = getVideoLikeActivityPubUrl(byActor, video)
const likeUrl = getVideoLikeActivityPubUrlByLocalActor(byActor, video)
const likeActivity = buildLikeActivity(likeUrl, byActor, video)

return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t })
@@ -70,7 +70,7 @@ async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Tran
async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
logger.info('Creating job to undo a dislike of video %s.', video.url)

const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video)
const dislikeUrl = getVideoDislikeActivityPubUrlByLocalActor(byActor, video)
const dislikeActivity = buildDislikeActivity(dislikeUrl, byActor, video)

return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t })


+ 5
- 5
server/lib/activitypub/send/send-view.ts View File

@@ -1,17 +1,17 @@
import { Transaction } from 'sequelize'
import { MActorAudience, MVideoImmutable, MVideoUrl } from '@server/types/models'
import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub'
import { logger } from '../../../helpers/logger'
import { ActorModel } from '../../../models/activitypub/actor'
import { getVideoLikeActivityPubUrl } from '../url'
import { sendVideoRelatedActivity } from './utils'
import { audiencify, getAudience } from '../audience'
import { logger } from '../../../helpers/logger'
import { MActorAudience, MVideoImmutable, MVideoUrl } from '@server/types/models'
import { getLocalVideoViewActivityPubUrl } from '../url'
import { sendVideoRelatedActivity } from './utils'

async function sendView (byActor: ActorModel, video: MVideoImmutable, t: Transaction) {
logger.info('Creating job to send view of %s.', video.url)

const activityBuilder = (audience: ActivityAudience) => {
const url = getVideoLikeActivityPubUrl(byActor, video)
const url = getLocalVideoViewActivityPubUrl(byActor, video)

return buildViewActivity(url, byActor, video, audience)
}


+ 3
- 3
server/lib/activitypub/share.ts View File

@@ -1,7 +1,7 @@
import { Transaction } from 'sequelize'
import { VideoShareModel } from '../../models/video/video-share'
import { sendUndoAnnounce, sendVideoAnnounce } from './send'
import { getVideoAnnounceActivityPubUrl } from './url'
import { getLocalVideoAnnounceActivityPubUrl } from './url'
import * as Bluebird from 'bluebird'
import { doRequest } from '../../helpers/requests'
import { getOrCreateActorAndServerAndModel } from './actor'
@@ -74,7 +74,7 @@ export {
async function shareByServer (video: MVideo, t: Transaction) {
const serverActor = await getServerActor()

const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video)
const serverShareUrl = getLocalVideoAnnounceActivityPubUrl(serverActor, video)
const [ serverShare ] = await VideoShareModel.findOrCreate({
defaults: {
actorId: serverActor.id,
@@ -91,7 +91,7 @@ async function shareByServer (video: MVideo, t: Transaction) {
}

async function shareByVideoChannel (video: MVideoAccountLight, t: Transaction) {
const videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video)
const videoChannelShareUrl = getLocalVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video)
const [ videoChannelShare ] = await VideoShareModel.findOrCreate({
defaults: {
actorId: video.VideoChannel.actorId,


+ 47
- 46
server/lib/activitypub/url.ts View File

@@ -1,102 +1,102 @@
import { WEBSERVER } from '../../initializers/constants'
import {
MAbuseId,
MActor,
MActorFollowActors,
MActorId,
MActorUrl,
MCommentId,
MVideoId,
MVideoPlaylistElement,
MVideoUrl,
MVideoUUID,
MAbuseId,
MVideoPlaylistElement
MVideoUUID
} from '../../types/models'
import { MVideoPlaylist, MVideoPlaylistUUID } from '../../types/models/video/video-playlist'
import { MVideoFileVideoUUID } from '../../types/models/video/video-file'
import { MVideoPlaylist, MVideoPlaylistUUID } from '../../types/models/video/video-playlist'
import { MStreamingPlaylist } from '../../types/models/video/video-streaming-playlist'

function getVideoActivityPubUrl (video: MVideoUUID) {
function getLocalVideoActivityPubUrl (video: MVideoUUID) {
return WEBSERVER.URL + '/videos/watch/' + video.uuid
}

function getVideoPlaylistActivityPubUrl (videoPlaylist: MVideoPlaylist) {
function getLocalVideoPlaylistActivityPubUrl (videoPlaylist: MVideoPlaylist) {
return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid
}

function getVideoPlaylistElementActivityPubUrl (videoPlaylist: MVideoPlaylistUUID, videoPlaylistElement: MVideoPlaylistElement) {
function getLocalVideoPlaylistElementActivityPubUrl (videoPlaylist: MVideoPlaylistUUID, videoPlaylistElement: MVideoPlaylistElement) {
return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid + '/videos/' + videoPlaylistElement.id
}

function getVideoCacheFileActivityPubUrl (videoFile: MVideoFileVideoUUID) {
function getLocalVideoCacheFileActivityPubUrl (videoFile: MVideoFileVideoUUID) {
const suffixFPS = videoFile.fps && videoFile.fps !== -1 ? '-' + videoFile.fps : ''

return `${WEBSERVER.URL}/redundancy/videos/${videoFile.Video.uuid}/${videoFile.resolution}${suffixFPS}`
}

function getVideoCacheStreamingPlaylistActivityPubUrl (video: MVideoUUID, playlist: MStreamingPlaylist) {
function getLocalVideoCacheStreamingPlaylistActivityPubUrl (video: MVideoUUID, playlist: MStreamingPlaylist) {
return `${WEBSERVER.URL}/redundancy/streaming-playlists/${playlist.getStringType()}/${video.uuid}`
}

function getVideoCommentActivityPubUrl (video: MVideoUUID, videoComment: MCommentId) {
function getLocalVideoCommentActivityPubUrl (video: MVideoUUID, videoComment: MCommentId) {
return WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id
}

function getVideoChannelActivityPubUrl (videoChannelName: string) {
function getLocalVideoChannelActivityPubUrl (videoChannelName: string) {
return WEBSERVER.URL + '/video-channels/' + videoChannelName
}

function getAccountActivityPubUrl (accountName: string) {
function getLocalAccountActivityPubUrl (accountName: string) {
return WEBSERVER.URL + '/accounts/' + accountName
}

function getAbuseActivityPubUrl (abuse: MAbuseId) {
function getLocalAbuseActivityPubUrl (abuse: MAbuseId) {
return WEBSERVER.URL + '/admin/abuses/' + abuse.id
}

function getVideoViewActivityPubUrl (byActor: MActorUrl, video: MVideoId) {
function getLocalVideoViewActivityPubUrl (byActor: MActorUrl, video: MVideoId) {
return byActor.url + '/views/videos/' + video.id + '/' + new Date().toISOString()
}

function getVideoLikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) {
function getVideoLikeActivityPubUrlByLocalActor (byActor: MActorUrl, video: MVideoId) {
return byActor.url + '/likes/' + video.id
}

function getVideoDislikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) {
function getVideoDislikeActivityPubUrlByLocalActor (byActor: MActorUrl, video: MVideoId) {
return byActor.url + '/dislikes/' + video.id
}

function getVideoSharesActivityPubUrl (video: MVideoUrl) {
function getLocalVideoSharesActivityPubUrl (video: MVideoUrl) {
return video.url + '/announces'
}

function getVideoCommentsActivityPubUrl (video: MVideoUrl) {
function getLocalVideoCommentsActivityPubUrl (video: MVideoUrl) {
return video.url + '/comments'
}

function getVideoLikesActivityPubUrl (video: MVideoUrl) {
function getLocalVideoLikesActivityPubUrl (video: MVideoUrl) {
return video.url + '/likes'
}

function getVideoDislikesActivityPubUrl (video: MVideoUrl) {
function getLocalVideoDislikesActivityPubUrl (video: MVideoUrl) {
return video.url + '/dislikes'
}

function getActorFollowActivityPubUrl (follower: MActor, following: MActorId) {
function getLocalActorFollowActivityPubUrl (follower: MActor, following: MActorId) {
return follower.url + '/follows/' + following.id
}

function getActorFollowAcceptActivityPubUrl (actorFollow: MActorFollowActors) {
function getLocalActorFollowAcceptActivityPubUrl (actorFollow: MActorFollowActors) {
const follower = actorFollow.ActorFollower
const me = actorFollow.ActorFollowing

return follower.url + '/accepts/follows/' + me.id
return WEBSERVER.URL + '/accepts/follows/' + follower.id + '/' + me.id
}

function getActorFollowRejectActivityPubUrl (follower: MActorUrl, following: MActorId) {
return follower.url + '/rejects/follows/' + following.id
function getLocalActorFollowRejectActivityPubUrl (follower: MActorId, following: MActorId) {
return WEBSERVER.URL + '/rejects/follows/' + follower.id + '/' + following.id
}

function getVideoAnnounceActivityPubUrl (byActor: MActorId, video: MVideoUrl) {
function getLocalVideoAnnounceActivityPubUrl (byActor: MActorId, video: MVideoUrl) {
return video.url + '/announces/' + byActor.id
}

@@ -113,27 +113,28 @@ function getUndoActivityPubUrl (originalUrl: string) {
}

export {
getVideoActivityPubUrl,
getVideoPlaylistElementActivityPubUrl,
getVideoPlaylistActivityPubUrl,
getVideoCacheStreamingPlaylistActivityPubUrl,
getVideoChannelActivityPubUrl,
getAccountActivityPubUrl,
getAbuseActivityPubUrl,
getActorFollowActivityPubUrl,
getActorFollowAcceptActivityPubUrl,
getVideoAnnounceActivityPubUrl,
getLocalVideoActivityPubUrl,
getLocalVideoPlaylistActivityPubUrl,
getLocalVideoPlaylistElementActivityPubUrl,
getLocalVideoCacheFileActivityPubUrl,
getLocalVideoCacheStreamingPlaylistActivityPubUrl,
getLocalVideoCommentActivityPubUrl,
getLocalVideoChannelActivityPubUrl,
getLocalAccountActivityPubUrl,
getLocalAbuseActivityPubUrl,
getLocalActorFollowActivityPubUrl,
getLocalActorFollowAcceptActivityPubUrl,
getLocalVideoAnnounceActivityPubUrl,
getUpdateActivityPubUrl,
getUndoActivityPubUrl,
getVideoViewActivityPubUrl,
getVideoLikeActivityPubUrl,
getVideoDislikeActivityPubUrl,
getActorFollowRejectActivityPubUrl,
getVideoCommentActivityPubUrl,
getVideoLikeActivityPubUrlByLocalActor,
getLocalVideoViewActivityPubUrl,
getVideoDislikeActivityPubUrlByLocalActor,
getLocalActorFollowRejectActivityPubUrl,
getDeleteActivityPubUrl,
getVideoSharesActivityPubUrl,
getVideoCommentsActivityPubUrl,
getVideoLikesActivityPubUrl,
getVideoDislikesActivityPubUrl,
getVideoCacheFileActivityPubUrl
getLocalVideoSharesActivityPubUrl,
getLocalVideoCommentsActivityPubUrl,
getLocalVideoLikesActivityPubUrl,
getLocalVideoDislikesActivityPubUrl,
}

+ 5
- 5
server/lib/activitypub/video-rates.ts View File

@@ -8,7 +8,7 @@ import { logger } from '../../helpers/logger'
import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
import { doRequest } from '../../helpers/requests'
import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
import { getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from './url'
import { getVideoDislikeActivityPubUrlByLocalActor, getVideoLikeActivityPubUrlByLocalActor } from './url'
import { sendDislike } from './send/send-dislike'
import { MAccountActor, MActorUrl, MVideo, MVideoAccountLight, MVideoId } from '../../types/models'

@@ -82,14 +82,14 @@ async function sendVideoRateChange (
if (dislikes > 0) await sendDislike(actor, video, t)
}

function getRateUrl (rateType: VideoRateType, actor: MActorUrl, video: MVideoId) {
function getLocalRateUrl (rateType: VideoRateType, actor: MActorUrl, video: MVideoId) {
return rateType === 'like'
? getVideoLikeActivityPubUrl(actor, video)
: getVideoDislikeActivityPubUrl(actor, video)
? getVideoLikeActivityPubUrlByLocalActor(actor, video)
: getVideoDislikeActivityPubUrlByLocalActor(actor, video)
}

export {
getRateUrl,
getLocalRateUrl,
createRates,
sendVideoRateChange
}

+ 2
- 0
server/lib/job-queue/handlers/activitypub-follow.ts View File

@@ -12,6 +12,7 @@ import { Notifier } from '../../notifier'
import { sequelizeTypescript } from '../../../initializers/database'
import { MActor, MActorFollowActors, MActorFull } from '../../../types/models'
import { ActivitypubFollowPayload } from '@shared/models'
import { getLocalActorFollowActivityPubUrl } from '@server/lib/activitypub/url'

async function processActivityPubFollow (job: Bull.Job) {
const payload = job.data as ActivitypubFollowPayload
@@ -61,6 +62,7 @@ async function follow (fromActor: MActor, targetActor: MActorFull, isAutoFollow
},
defaults: {
state,
url: getLocalActorFollowActivityPubUrl(fromActor, targetActor),
actorId: fromActor.id,
targetActorId: targetActor.id
},


+ 19
- 18
server/lib/schedulers/videos-redundancy-scheduler.ts View File

@@ -1,19 +1,10 @@
import { AbstractScheduler } from './abstract-scheduler'
import { HLS_REDUNDANCY_DIRECTORY, REDUNDANCY, VIDEO_IMPORT_TIMEOUT, WEBSERVER } from '../../initializers/constants'
import { logger } from '../../helpers/logger'
import { VideosRedundancyStrategy } from '../../../shared/models/redundancy'
import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
import { downloadWebTorrentVideo, generateMagnetUri } from '../../helpers/webtorrent'
import { join } from 'path'
import { move } from 'fs-extra'
import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send'
import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url'
import { removeVideoRedundancy } from '../redundancy'
import { getOrCreateVideoAndAccountAndChannel } from '../activitypub/videos'
import { downloadPlaylistSegments } from '../hls'
import { CONFIG } from '../../initializers/config'
import { join } from 'path'
import { getServerActor } from '@server/models/application/application'
import { VideoModel } from '@server/models/video/video'
import {
MStreamingPlaylist, MStreamingPlaylistFiles,
MStreamingPlaylist,
MStreamingPlaylistFiles,
MStreamingPlaylistVideo,
MVideoAccountLight,
MVideoFile,
@@ -23,9 +14,19 @@ import {
MVideoRedundancyVideo,
MVideoWithAllFiles
} from '@server/types/models'
import { VideosRedundancyStrategy } from '../../../shared/models/redundancy'
import { logger } from '../../helpers/logger'
import { downloadWebTorrentVideo, generateMagnetUri } from '../../helpers/webtorrent'
import { CONFIG } from '../../initializers/config'
import { HLS_REDUNDANCY_DIRECTORY, REDUNDANCY, VIDEO_IMPORT_TIMEOUT, WEBSERVER } from '../../initializers/constants'
import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send'
import { getLocalVideoCacheFileActivityPubUrl, getLocalVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url'
import { getOrCreateVideoAndAccountAndChannel } from '../activitypub/videos'
import { downloadPlaylistSegments } from '../hls'
import { removeVideoRedundancy } from '../redundancy'
import { getVideoFilename } from '../video-paths'
import { VideoModel } from '@server/models/video/video'
import { getServerActor } from '@server/models/application/application'
import { AbstractScheduler } from './abstract-scheduler'

type CandidateToDuplicate = {
redundancy: VideosRedundancyStrategy
@@ -230,7 +231,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {

const createdModel: MVideoRedundancyFileVideo = await VideoRedundancyModel.create({
expiresOn,
url: getVideoCacheFileActivityPubUrl(file),
url: getLocalVideoCacheFileActivityPubUrl(file),
fileUrl: video.getVideoRedundancyUrl(file, WEBSERVER.URL),
strategy,
videoFileId: file.id,
@@ -269,7 +270,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {

const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({
expiresOn,
url: getVideoCacheStreamingPlaylistActivityPubUrl(video, playlist),
url: getLocalVideoCacheStreamingPlaylistActivityPubUrl(video, playlist),
fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL),
strategy,
videoStreamingPlaylistId: playlist.id,


+ 2
- 2
server/lib/user.ts View File

@@ -11,7 +11,7 @@ import { ActorModel } from '../models/activitypub/actor'
import { MAccountDefault, MActorDefault, MChannelActor } from '../types/models'
import { MUser, MUserDefault, MUserId } from '../types/models/user'
import { buildActorInstance, setAsyncActorKeys } from './activitypub/actor'
import { getAccountActivityPubUrl } from './activitypub/url'
import { getLocalAccountActivityPubUrl } from './activitypub/url'
import { Emailer } from './emailer'
import { LiveManager } from './live-manager'
import { Redis } from './redis'
@@ -74,7 +74,7 @@ async function createLocalAccountWithoutKeys (parameters: {
type?: ActivityPubActorType
}) {
const { name, displayName, userId, applicationId, t, type = 'Person' } = parameters
const url = getAccountActivityPubUrl(name)
const url = getLocalAccountActivityPubUrl(name)

const actorInstance = buildActorInstance(type, url, name)
const actorInstanceCreated: MActorDefault = await actorInstance.save({ transaction: t })


+ 4
- 4
server/lib/video-channel.ts View File

@@ -1,11 +1,11 @@
import * as Sequelize from 'sequelize'
import { v4 as uuidv4 } from 'uuid'
import { VideoChannelCreate } from '../../shared/models'
import { VideoChannelModel } from '../models/video/video-channel'
import { buildActorInstance } from './activitypub/actor'
import { VideoModel } from '../models/video/video'
import { VideoChannelModel } from '../models/video/video-channel'
import { MAccountId, MChannelDefault, MChannelId } from '../types/models'
import { getVideoChannelActivityPubUrl } from './activitypub/url'
import { buildActorInstance } from './activitypub/actor'
import { getLocalVideoChannelActivityPubUrl } from './activitypub/url'
import { federateVideoIfNeeded } from './activitypub/videos'

type CustomVideoChannelModelAccount <T extends MAccountId> = MChannelDefault & { Account?: T }
@@ -16,7 +16,7 @@ async function createLocalVideoChannel <T extends MAccountId> (
t: Sequelize.Transaction
): Promise<CustomVideoChannelModelAccount<T>> {
const uuid = uuidv4()
const url = getVideoChannelActivityPubUrl(videoChannelInfo.name)
const url = getLocalVideoChannelActivityPubUrl(videoChannelInfo.name)
const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid)

const actorInstanceCreated = await actorInstance.save({ transaction: t })


+ 3
- 3
server/lib/video-comment.ts View File

@@ -5,9 +5,9 @@ import { sequelizeTypescript } from '@server/initializers/database'
import { ResultList } from '../../shared/models'
import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model'
import { VideoCommentModel } from '../models/video/video-comment'
import { MAccountDefault, MComment, MCommentOwnerVideoReply, MVideoFullLight, MCommentOwnerVideo } from '../types/models'
import { MAccountDefault, MComment, MCommentOwnerVideo, MCommentOwnerVideoReply, MVideoFullLight } from '../types/models'
import { sendCreateVideoComment, sendDeleteVideoComment } from './activitypub/send'
import { getVideoCommentActivityPubUrl } from './activitypub/url'
import { getLocalVideoCommentActivityPubUrl } from './activitypub/url'
import { Hooks } from './plugins/hooks'

async function removeComment (videoCommentInstance: MCommentOwnerVideo) {
@@ -51,7 +51,7 @@ async function createVideoComment (obj: {
url: new Date().toISOString()
}, { transaction: t, validate: false })

comment.url = getVideoCommentActivityPubUrl(obj.video, comment)
comment.url = getLocalVideoCommentActivityPubUrl(obj.video, comment)

const savedComment: MCommentOwnerVideoReply = await comment.save({ transaction: t })
savedComment.InReplyToVideoComment = obj.inReplyToComment


+ 3
- 3
server/lib/video-playlist.ts View File

@@ -1,10 +1,10 @@
import * as Sequelize from 'sequelize'
import { VideoPlaylistModel } from '../models/video/video-playlist'
import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model'
import { getVideoPlaylistActivityPubUrl } from './activitypub/url'
import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model'
import { VideoPlaylistModel } from '../models/video/video-playlist'
import { MAccount } from '../types/models'
import { MVideoPlaylistOwner } from '../types/models/video/video-playlist'
import { getLocalVideoPlaylistActivityPubUrl } from './activitypub/url'

async function createWatchLaterPlaylist (account: MAccount, t: Sequelize.Transaction) {
const videoPlaylist: MVideoPlaylistOwner = new VideoPlaylistModel({
@@ -14,7 +14,7 @@ async function createWatchLaterPlaylist (account: MAccount, t: Sequelize.Transac
ownerAccountId: account.id
})

videoPlaylist.url = getVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object
videoPlaylist.url = getLocalVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object

await videoPlaylist.save({ transaction: t })



+ 0
- 1
server/middlewares/validators/videos/video-rates.ts View File

@@ -38,7 +38,6 @@ const getAccountVideoRateValidatorFactory = function (rateType: VideoRateType) {
if (!rate) {
return res.status(404)
.json({ error: 'Video rate not found' })
.end()
}

res.locals.accountVideoRate = rate


+ 2
- 1
server/models/account/account-video-rate.ts View File

@@ -168,7 +168,8 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
model: ActorModel.unscoped(),
required: true,
where: {
preferredUsername: accountName
preferredUsername: accountName,
serverId: null
}
}
]


+ 24
- 12
server/models/activitypub/actor-follow.ts View File

@@ -1,5 +1,6 @@
import * as Bluebird from 'bluebird'
import { difference, values } from 'lodash'
import { IncludeOptions, Op, QueryTypes, Transaction, WhereOptions } from 'sequelize'
import {
AfterCreate,
AfterDestroy,
@@ -11,22 +12,16 @@ import {
DataType,
Default,
ForeignKey,
Is,
IsInt,
Max,
Model,
Table,
UpdatedAt
} from 'sequelize-typescript'
import { FollowState } from '../../../shared/models/actors'
import { ActorFollow } from '../../../shared/models/actors/follow.model'
import { logger } from '../../helpers/logger'
import { ACTOR_FOLLOW_SCORE, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants'
import { ServerModel } from '../server/server'
import { createSafeIn, getFollowsSort, getSort, searchAttribute } from '../utils'
import { ActorModel, unusedActorAttributesForAPI } from './actor'
import { VideoChannelModel } from '../video/video-channel'
import { AccountModel } from '../account/account'
import { IncludeOptions, Op, QueryTypes, Transaction, WhereOptions } from 'sequelize'
import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc'
import { getServerActor } from '@server/models/application/application'
import { VideoModel } from '@server/models/video/video'
import {
MActorFollowActorsDefault,
MActorFollowActorsDefaultSubscription,
@@ -35,8 +30,15 @@ import {
MActorFollowSubscriptions
} from '@server/types/models'
import { ActivityPubActorType } from '@shared/models'
import { VideoModel } from '@server/models/video/video'
import { getServerActor } from '@server/models/application/application'
import { FollowState } from '../../../shared/models/actors'
import { ActorFollow } from '../../../shared/models/actors/follow.model'
import { logger } from '../../helpers/logger'
import { ACTOR_FOLLOW_SCORE, CONSTRAINTS_FIELDS, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants'
import { AccountModel } from '../account/account'
import { ServerModel } from '../server/server'
import { createSafeIn, getFollowsSort, getSort, searchAttribute, throwIfNotValid } from '../utils'
import { VideoChannelModel } from '../video/video-channel'
import { ActorModel, unusedActorAttributesForAPI } from './actor'

@Table({
tableName: 'actorFollow',
@@ -53,6 +55,10 @@ import { getServerActor } from '@server/models/application/application'
},
{
fields: [ 'score' ]
},
{
fields: [ 'url' ],
unique: true
}
]
})
@@ -69,6 +75,12 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
@Column
score: number

// Allow null because we added this column in PeerTube v3, and don't want to generate fake URLs of remote follows
@AllowNull(true)
@Is('ActorFollowUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
@Column(DataType.STRING(CONSTRAINTS_FIELDS.COMMONS.URL.max))
url: string

@CreatedAt
createdAt: Date



+ 8
- 8
server/models/video/video-format-utils.ts View File

@@ -4,10 +4,10 @@ import { ActivityTagObject, ActivityUrlObject, VideoObject } from '../../../shar
import { MIMETYPES, WEBSERVER } from '../../initializers/constants'
import { VideoCaptionModel } from './video-caption'
import {
getVideoCommentsActivityPubUrl,
getVideoDislikesActivityPubUrl,
getVideoLikesActivityPubUrl,
getVideoSharesActivityPubUrl
getLocalVideoCommentsActivityPubUrl,
getLocalVideoDislikesActivityPubUrl,
getLocalVideoLikesActivityPubUrl,
getLocalVideoSharesActivityPubUrl
} from '../../lib/activitypub/url'
import { isArray } from '../../helpers/custom-validators/misc'
import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model'
@@ -382,10 +382,10 @@ function videoModelToActivityPubObject (video: MVideoAP): VideoObject {
height: i.height
})),
url,
likes: getVideoLikesActivityPubUrl(video),
dislikes: getVideoDislikesActivityPubUrl(video),
shares: getVideoSharesActivityPubUrl(video),
comments: getVideoCommentsActivityPubUrl(video),
likes: getLocalVideoLikesActivityPubUrl(video),
dislikes: getLocalVideoDislikesActivityPubUrl(video),
shares: getLocalVideoSharesActivityPubUrl(video),
comments: getLocalVideoCommentsActivityPubUrl(video),
attributedTo: [
{
type: 'Person',


+ 8
- 0
server/tests/api/server/follows-moderation.ts View File

@@ -81,6 +81,8 @@ describe('Test follows moderation', function () {
})

it('Should remove follower on server 2', async function () {
this.timeout(10000)

await removeFollower(servers[1].url, servers[1].accessToken, servers[0])

await waitJobs(servers)
@@ -91,6 +93,8 @@ describe('Test follows moderation', function () {
})

it('Should disable followers on server 2', async function () {
this.timeout(10000)

const subConfig = {
followers: {
instance: {
@@ -109,6 +113,8 @@ describe('Test follows moderation', function () {
})

it('Should re enable followers on server 2', async function () {
this.timeout(10000)

const subConfig = {
followers: {
instance: {
@@ -151,6 +157,8 @@ describe('Test follows moderation', function () {
})

it('Should accept a follower', async function () {
this.timeout(10000)

await acceptFollower(servers[1].url, servers[1].accessToken, 'peertube@localhost:' + servers[0].port)
await waitJobs(servers)



Loading…
Cancel
Save