diff --git a/scripts/update-host.ts b/scripts/update-host.ts index d2fb19c76..b030b21c3 100755 --- a/scripts/update-host.ts +++ b/scripts/update-host.ts @@ -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) { diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index d85d0aa5f..71a5b6232 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts @@ -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 +} diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts index 23823c9fb..517d1897e 100644 --- a/server/controllers/api/server/follows.ts +++ b/server/controllers/api/server/follows.ts @@ -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() diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts index 41a0e07ff..fb08a63b2 100644 --- a/server/controllers/api/video-playlist.ts +++ b/server/controllers/api/video-playlist.ts @@ -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) diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index 5840cd063..bc5fea7aa 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts @@ -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 } diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index bfebc54ed..b5ff2e72e 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -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), diff --git a/server/controllers/api/videos/live.ts b/server/controllers/api/videos/live.ts index d438b6f3a..a6f00c1bd 100644 --- a/server/controllers/api/videos/live.ts +++ b/server/controllers/api/videos/live.ts @@ -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 diff --git a/server/controllers/api/videos/rate.ts b/server/controllers/api/videos/rate.ts index 3ee365289..df1eddb4f 100644 --- a/server/controllers/api/videos/rate.ts +++ b/server/controllers/api/videos/rate.ts @@ -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) diff --git a/server/initializers/migrations/0100-activitypub.ts b/server/initializers/migrations/0100-activitypub.ts index 05fd37406..49309286c 100644 --- a/server/initializers/migrations/0100-activitypub.ts +++ b/server/initializers/migrations/0100-activitypub.ts @@ -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() } diff --git a/server/lib/activitypub/process/process-dislike.ts b/server/lib/activitypub/process/process-dislike.ts index 0cd204501..635c8bfcc 100644 --- a/server/lib/activitypub/process/process-dislike.ts +++ b/server/lib/activitypub/process/process-dislike.ts @@ -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) { 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 }) diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts index 7eb7e828d..076ad9cf4 100644 --- a/server/lib/activitypub/process/process-follow.ts +++ b/server/lib/activitypub/process/process-follow.ts @@ -15,9 +15,11 @@ import { getServerActor } from '@server/models/application/application' async function processFollowActivity (options: APProcessorOptions) { 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 }) diff --git a/server/lib/activitypub/process/process-like.ts b/server/lib/activitypub/process/process-like.ts index b800a5618..6acc097b1 100644 --- a/server/lib/activitypub/process/process-like.ts +++ b/server/lib/activitypub/process/process-like.ts @@ -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) { 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 }) diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts index 50e192bdd..bb387e2c0 100644 --- a/server/lib/activitypub/send/send-accept.ts +++ b/server/lib/activitypub/send/send-accept.ts @@ -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) diff --git a/server/lib/activitypub/send/send-dislike.ts b/server/lib/activitypub/send/send-dislike.ts index 1bb7dc937..274230535 100644 --- a/server/lib/activitypub/send/send-dislike.ts +++ b/server/lib/activitypub/send/send-dislike.ts @@ -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) } diff --git a/server/lib/activitypub/send/send-flag.ts b/server/lib/activitypub/send/send-flag.ts index 821637ec8..b0483b5a0 100644 --- a/server/lib/activitypub/send/send-flag.ts +++ b/server/lib/activitypub/send/send-flag.ts @@ -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) diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts index 08f1d83f9..9219640dd 100644 --- a/server/lib/activitypub/send/send-follow.ts +++ b/server/lib/activitypub/send/send-follow.ts @@ -1,9 +1,8 @@ -import { ActivityFollow } from '../../../../shared/models/activitypub' -import { getActorFollowActivityPubUrl } from '../url' -import { unicastTo } from './utils' -import { logger } from '../../../helpers/logger' import { Transaction } from 'sequelize' +import { ActivityFollow } from '../../../../shared/models/activitypub' +import { logger } from '../../../helpers/logger' 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)) } diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts index 29fcfc404..ed6dfcf56 100644 --- a/server/lib/activitypub/send/send-like.ts +++ b/server/lib/activitypub/send/send-like.ts @@ -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) } diff --git a/server/lib/activitypub/send/send-reject.ts b/server/lib/activitypub/send/send-reject.ts index befbcb8da..8d74a7848 100644 --- a/server/lib/activitypub/send/send-reject.ts +++ b/server/lib/activitypub/send/send-reject.ts @@ -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) diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index 6ed343300..352c158fd 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts @@ -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 }) diff --git a/server/lib/activitypub/send/send-view.ts b/server/lib/activitypub/send/send-view.ts index 3358188a2..9254dc7c5 100644 --- a/server/lib/activitypub/send/send-view.ts +++ b/server/lib/activitypub/send/send-view.ts @@ -1,17 +1,17 @@ import { Transaction } from 'sequelize' -import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' -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 { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' +import { logger } from '../../../helpers/logger' +import { ActorModel } from '../../../models/activitypub/actor' +import { audiencify, getAudience } from '../audience' +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) } diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts index 5e426f5db..1f8a8f3c4 100644 --- a/server/lib/activitypub/share.ts +++ b/server/lib/activitypub/share.ts @@ -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, diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts index 58030be2c..ad6a1d1fd 100644 --- a/server/lib/activitypub/url.ts +++ b/server/lib/activitypub/url.ts @@ -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, + } diff --git a/server/lib/activitypub/video-rates.ts b/server/lib/activitypub/video-rates.ts index e09e5d9ec..581a2bca1 100644 --- a/server/lib/activitypub/video-rates.ts +++ b/server/lib/activitypub/video-rates.ts @@ -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 } diff --git a/server/lib/job-queue/handlers/activitypub-follow.ts b/server/lib/job-queue/handlers/activitypub-follow.ts index 7a4f85f8b..82c95be80 100644 --- a/server/lib/job-queue/handlers/activitypub-follow.ts +++ b/server/lib/job-queue/handlers/activitypub-follow.ts @@ -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 }, diff --git a/server/lib/schedulers/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts index 54d9a9894..82005a2c8 100644 --- a/server/lib/schedulers/videos-redundancy-scheduler.ts +++ b/server/lib/schedulers/videos-redundancy-scheduler.ts @@ -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, diff --git a/server/lib/user.ts b/server/lib/user.ts index 7d6497302..6b0fd9b88 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts @@ -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 }) diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts index 8928dda12..49bdf4869 100644 --- a/server/lib/video-channel.ts +++ b/server/lib/video-channel.ts @@ -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 = MChannelDefault & { Account?: T } @@ -16,7 +16,7 @@ async function createLocalVideoChannel ( t: Sequelize.Transaction ): Promise> { 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 }) diff --git a/server/lib/video-comment.ts b/server/lib/video-comment.ts index c92a7c43a..736ebb2f8 100644 --- a/server/lib/video-comment.ts +++ b/server/lib/video-comment.ts @@ -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 diff --git a/server/lib/video-playlist.ts b/server/lib/video-playlist.ts index 6eeb70c8e..a1af2e1af 100644 --- a/server/lib/video-playlist.ts +++ b/server/lib/video-playlist.ts @@ -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 }) diff --git a/server/middlewares/validators/videos/video-rates.ts b/server/middlewares/validators/videos/video-rates.ts index cbc144f69..15a8c7983 100644 --- a/server/middlewares/validators/videos/video-rates.ts +++ b/server/middlewares/validators/videos/video-rates.ts @@ -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 diff --git a/server/models/account/account-video-rate.ts b/server/models/account/account-video-rate.ts index 5064987dc..6955f45ee 100644 --- a/server/models/account/account-video-rate.ts +++ b/server/models/account/account-video-rate.ts @@ -168,7 +168,8 @@ export class AccountVideoRateModel extends Model { model: ActorModel.unscoped(), required: true, where: { - preferredUsername: accountName + preferredUsername: accountName, + serverId: null } } ] diff --git a/server/models/activitypub/actor-follow.ts b/server/models/activitypub/actor-follow.ts index 10c13304f..58bc63d34 100644 --- a/server/models/activitypub/actor-follow.ts +++ b/server/models/activitypub/actor-follow.ts @@ -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 { @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 diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts index d4b213686..b1adbcb86 100644 --- a/server/models/video/video-format-utils.ts +++ b/server/models/video/video-format-utils.ts @@ -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', diff --git a/server/tests/api/server/follows-moderation.ts b/server/tests/api/server/follows-moderation.ts index cee85cc4b..73c212a32 100644 --- a/server/tests/api/server/follows-moderation.ts +++ b/server/tests/api/server/follows-moderation.ts @@ -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)