Refractor activity pub lib/helpers
This commit is contained in:
parent
eb8b27c93e
commit
5414139835
|
@ -2,12 +2,12 @@ import * as express from 'express'
|
||||||
import { Activity, ActivityPubCollection, ActivityPubOrderedCollection, ActivityType, RootActivity } from '../../../shared'
|
import { Activity, ActivityPubCollection, ActivityPubOrderedCollection, ActivityType, RootActivity } from '../../../shared'
|
||||||
import { logger } from '../../helpers'
|
import { logger } from '../../helpers'
|
||||||
import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity'
|
import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity'
|
||||||
import { processCreateActivity, processUpdateActivity } from '../../lib'
|
import { processCreateActivity, processUpdateActivity, processUndoActivity } from '../../lib'
|
||||||
import { processAcceptActivity } from '../../lib/activitypub/process-accept'
|
import { processAcceptActivity } from '../../lib/activitypub/process/process-accept'
|
||||||
import { processAddActivity } from '../../lib/activitypub/process-add'
|
import { processAddActivity } from '../../lib/activitypub/process/process-add'
|
||||||
import { processAnnounceActivity } from '../../lib/activitypub/process-announce'
|
import { processAnnounceActivity } from '../../lib/activitypub/process/process-announce'
|
||||||
import { processDeleteActivity } from '../../lib/activitypub/process-delete'
|
import { processDeleteActivity } from '../../lib/activitypub/process/process-delete'
|
||||||
import { processFollowActivity } from '../../lib/activitypub/process-follow'
|
import { processFollowActivity } from '../../lib/activitypub/process/process-follow'
|
||||||
import { asyncMiddleware, checkSignature, localAccountValidator, signatureValidator } from '../../middlewares'
|
import { asyncMiddleware, checkSignature, localAccountValidator, signatureValidator } from '../../middlewares'
|
||||||
import { activityPubValidator } from '../../middlewares/validators/activitypub/activity'
|
import { activityPubValidator } from '../../middlewares/validators/activitypub/activity'
|
||||||
import { AccountInstance } from '../../models/account/account-interface'
|
import { AccountInstance } from '../../models/account/account-interface'
|
||||||
|
@ -19,7 +19,8 @@ const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxAccoun
|
||||||
Delete: processDeleteActivity,
|
Delete: processDeleteActivity,
|
||||||
Follow: processFollowActivity,
|
Follow: processFollowActivity,
|
||||||
Accept: processAcceptActivity,
|
Accept: processAcceptActivity,
|
||||||
Announce: processAnnounceActivity
|
Announce: processAnnounceActivity,
|
||||||
|
Undo: processUndoActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
const inboxRouter = express.Router()
|
const inboxRouter = express.Router()
|
||||||
|
|
|
@ -6,14 +6,16 @@ import { getServerAccount } from '../../../helpers/utils'
|
||||||
import { getAccountFromWebfinger } from '../../../helpers/webfinger'
|
import { getAccountFromWebfinger } from '../../../helpers/webfinger'
|
||||||
import { SERVER_ACCOUNT_NAME } from '../../../initializers/constants'
|
import { SERVER_ACCOUNT_NAME } from '../../../initializers/constants'
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
import { sendFollow } from '../../../lib/activitypub/send-request'
|
import { asyncMiddleware, paginationValidator, removeFollowingValidator, setFollowersSort, setPagination } from '../../../middlewares'
|
||||||
import { asyncMiddleware, paginationValidator, setFollowersSort, setPagination } from '../../../middlewares'
|
|
||||||
import { authenticate } from '../../../middlewares/oauth'
|
import { authenticate } from '../../../middlewares/oauth'
|
||||||
import { setBodyHostsPort } from '../../../middlewares/servers'
|
import { setBodyHostsPort } from '../../../middlewares/servers'
|
||||||
import { setFollowingSort } from '../../../middlewares/sort'
|
import { setFollowingSort } from '../../../middlewares/sort'
|
||||||
import { ensureUserHasRight } from '../../../middlewares/user-right'
|
import { ensureUserHasRight } from '../../../middlewares/user-right'
|
||||||
import { followValidator } from '../../../middlewares/validators/servers'
|
import { followValidator } from '../../../middlewares/validators/follows'
|
||||||
import { followersSortValidator, followingSortValidator } from '../../../middlewares/validators/sort'
|
import { followersSortValidator, followingSortValidator } from '../../../middlewares/validators/sort'
|
||||||
|
import { AccountFollowInstance } from '../../../models/index'
|
||||||
|
import { sendFollow } from '../../../lib/index'
|
||||||
|
import { sendUndoFollow } from '../../../lib/activitypub/send/send-undo'
|
||||||
|
|
||||||
const serverFollowsRouter = express.Router()
|
const serverFollowsRouter = express.Router()
|
||||||
|
|
||||||
|
@ -33,6 +35,13 @@ serverFollowsRouter.post('/following',
|
||||||
asyncMiddleware(follow)
|
asyncMiddleware(follow)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
serverFollowsRouter.delete('/following/:accountId',
|
||||||
|
authenticate,
|
||||||
|
ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
|
||||||
|
removeFollowingValidator,
|
||||||
|
asyncMiddleware(removeFollow)
|
||||||
|
)
|
||||||
|
|
||||||
serverFollowsRouter.get('/followers',
|
serverFollowsRouter.get('/followers',
|
||||||
paginationValidator,
|
paginationValidator,
|
||||||
followersSortValidator,
|
followersSortValidator,
|
||||||
|
@ -96,10 +105,12 @@ async function follow (req: express.Request, res: express.Response, next: expres
|
||||||
},
|
},
|
||||||
transaction: t
|
transaction: t
|
||||||
})
|
})
|
||||||
|
accountFollow.AccountFollowing = targetAccount
|
||||||
|
accountFollow.AccountFollower = fromAccount
|
||||||
|
|
||||||
// Send a notification to remote server
|
// Send a notification to remote server
|
||||||
if (accountFollow.state === 'pending') {
|
if (accountFollow.state === 'pending') {
|
||||||
await sendFollow(fromAccount, targetAccount, t)
|
await sendFollow(accountFollow, t)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -117,6 +128,17 @@ async function follow (req: express.Request, res: express.Response, next: expres
|
||||||
return res.status(204).end()
|
return res.status(204).end()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function removeFollow (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
|
const following: AccountFollowInstance = res.locals.following
|
||||||
|
|
||||||
|
await db.sequelize.transaction(async t => {
|
||||||
|
await sendUndoFollow(following, t)
|
||||||
|
await following.destroy({ transaction: t })
|
||||||
|
})
|
||||||
|
|
||||||
|
return res.status(204).end()
|
||||||
|
}
|
||||||
|
|
||||||
async function loadLocalOrGetAccountFromWebfinger (name: string, host: string) {
|
async function loadLocalOrGetAccountFromWebfinger (name: string, host: string) {
|
||||||
let loadedFromDB = true
|
let loadedFromDB = true
|
||||||
let account = await db.Account.loadByNameAndHost(name, host)
|
let account = await db.Account.loadByNameAndHost(name, host)
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
videoChannelsUpdateValidator
|
videoChannelsUpdateValidator
|
||||||
} from '../../../middlewares'
|
} from '../../../middlewares'
|
||||||
import { AccountInstance, VideoChannelInstance } from '../../../models'
|
import { AccountInstance, VideoChannelInstance } from '../../../models'
|
||||||
import { sendUpdateVideoChannel } from '../../../lib/activitypub/send-request'
|
import { sendUpdateVideoChannel } from '../../../lib/activitypub/send/send-update'
|
||||||
|
|
||||||
const videoChannelRouter = express.Router()
|
const videoChannelRouter = express.Router()
|
||||||
|
|
||||||
|
@ -128,9 +128,9 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
|
||||||
if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name)
|
if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name)
|
||||||
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
|
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
|
||||||
|
|
||||||
await videoChannelInstance.save(sequelizeOptions)
|
const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions)
|
||||||
|
|
||||||
await sendUpdateVideoChannel(videoChannelInstance, t)
|
await sendUpdateVideoChannel(videoChannelInstanceUpdated, t)
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.uuid)
|
logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.uuid)
|
||||||
|
|
|
@ -12,10 +12,11 @@ import {
|
||||||
resetSequelizeInstance,
|
resetSequelizeInstance,
|
||||||
retryTransactionWrapper
|
retryTransactionWrapper
|
||||||
} from '../../../helpers'
|
} from '../../../helpers'
|
||||||
import { getActivityPubUrl, shareVideoByServer } from '../../../helpers/activitypub'
|
import { getVideoActivityPubUrl, shareVideoByServer } from '../../../helpers/activitypub'
|
||||||
import { CONFIG, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, VIDEO_PRIVACIES } from '../../../initializers'
|
import { CONFIG, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, VIDEO_PRIVACIES } from '../../../initializers'
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
import { sendAddVideo, sendUpdateVideo } from '../../../lib/activitypub/send-request'
|
import { sendAddVideo } from '../../../lib/activitypub/send/send-add'
|
||||||
|
import { sendUpdateVideo } from '../../../lib/activitypub/send/send-update'
|
||||||
import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler/transcoding-job-scheduler'
|
import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler/transcoding-job-scheduler'
|
||||||
import {
|
import {
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
|
@ -175,7 +176,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
|
||||||
channelId: res.locals.videoChannel.id
|
channelId: res.locals.videoChannel.id
|
||||||
}
|
}
|
||||||
const video = db.Video.build(videoData)
|
const video = db.Video.build(videoData)
|
||||||
video.url = getActivityPubUrl('video', video.uuid)
|
video.url = getVideoActivityPubUrl(video)
|
||||||
|
|
||||||
const videoFilePath = join(CONFIG.STORAGE.VIDEOS_DIR, videoPhysicalFile.filename)
|
const videoFilePath = join(CONFIG.STORAGE.VIDEOS_DIR, videoPhysicalFile.filename)
|
||||||
const videoFileHeight = await getVideoFileHeight(videoFilePath)
|
const videoFileHeight = await getVideoFileHeight(videoFilePath)
|
||||||
|
@ -274,7 +275,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
|
||||||
if (videoInfoToUpdate.privacy !== undefined) videoInstance.set('privacy', videoInfoToUpdate.privacy)
|
if (videoInfoToUpdate.privacy !== undefined) videoInstance.set('privacy', videoInfoToUpdate.privacy)
|
||||||
if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description)
|
if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description)
|
||||||
|
|
||||||
await videoInstance.save(sequelizeOptions)
|
const videoInstanceUpdated = await videoInstance.save(sequelizeOptions)
|
||||||
|
|
||||||
if (videoInfoToUpdate.tags) {
|
if (videoInfoToUpdate.tags) {
|
||||||
const tagInstances = await db.Tag.findOrCreateTags(videoInfoToUpdate.tags, t)
|
const tagInstances = await db.Tag.findOrCreateTags(videoInfoToUpdate.tags, t)
|
||||||
|
@ -285,7 +286,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
|
||||||
|
|
||||||
// Now we'll update the video's meta data to our friends
|
// Now we'll update the video's meta data to our friends
|
||||||
if (wasPrivateVideo === false) {
|
if (wasPrivateVideo === false) {
|
||||||
await sendUpdateVideo(videoInstance, t)
|
await sendUpdateVideo(videoInstanceUpdated, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Video is not private anymore, send a create action to remote servers
|
// Video is not private anymore, send a create action to remote servers
|
||||||
|
|
|
@ -9,18 +9,20 @@ import { VideoChannelObject } from '../../shared/models/activitypub/objects/vide
|
||||||
import { ResultList } from '../../shared/models/result-list.model'
|
import { ResultList } from '../../shared/models/result-list.model'
|
||||||
import { database as db, REMOTE_SCHEME } from '../initializers'
|
import { database as db, REMOTE_SCHEME } from '../initializers'
|
||||||
import { ACTIVITY_PUB, CONFIG, STATIC_PATHS } from '../initializers/constants'
|
import { ACTIVITY_PUB, CONFIG, STATIC_PATHS } from '../initializers/constants'
|
||||||
import { videoChannelActivityObjectToDBAttributes } from '../lib/activitypub/misc'
|
import { videoChannelActivityObjectToDBAttributes } from '../lib/activitypub/process/misc'
|
||||||
import { sendVideoAnnounce } from '../lib/activitypub/send-request'
|
import { sendVideoAnnounce } from '../lib/activitypub/send/send-announce'
|
||||||
import { sendVideoChannelAnnounce } from '../lib/index'
|
import { sendVideoChannelAnnounce } from '../lib/index'
|
||||||
|
import { AccountFollowInstance } from '../models/account/account-follow-interface'
|
||||||
import { AccountInstance } from '../models/account/account-interface'
|
import { AccountInstance } from '../models/account/account-interface'
|
||||||
|
import { VideoAbuseInstance } from '../models/video/video-abuse-interface'
|
||||||
import { VideoChannelInstance } from '../models/video/video-channel-interface'
|
import { VideoChannelInstance } from '../models/video/video-channel-interface'
|
||||||
import { VideoInstance } from '../models/video/video-interface'
|
import { VideoInstance } from '../models/video/video-interface'
|
||||||
import { isRemoteAccountValid } from './custom-validators'
|
import { isRemoteAccountValid } from './custom-validators'
|
||||||
import { isVideoChannelObjectValid } from './custom-validators/activitypub/videos'
|
|
||||||
import { logger } from './logger'
|
import { logger } from './logger'
|
||||||
import { signObject } from './peertube-crypto'
|
import { signObject } from './peertube-crypto'
|
||||||
import { doRequest, doRequestAndSaveToFile } from './requests'
|
import { doRequest, doRequestAndSaveToFile } from './requests'
|
||||||
import { getServerAccount } from './utils'
|
import { getServerAccount } from './utils'
|
||||||
|
import { isVideoChannelObjectValid } from './custom-validators/activitypub/video-channels'
|
||||||
|
|
||||||
function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) {
|
function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) {
|
||||||
const thumbnailName = video.getThumbnailName()
|
const thumbnailName = video.getThumbnailName()
|
||||||
|
@ -55,13 +57,46 @@ async function shareVideoByServer (video: VideoInstance, t: Sequelize.Transactio
|
||||||
return sendVideoAnnounce(serverAccount, video, t)
|
return sendVideoAnnounce(serverAccount, video, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActivityPubUrl (type: 'video' | 'videoChannel' | 'account' | 'videoAbuse', id: string) {
|
function getVideoActivityPubUrl (video: VideoInstance) {
|
||||||
if (type === 'video') return CONFIG.WEBSERVER.URL + '/videos/watch/' + id
|
return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid
|
||||||
else if (type === 'videoChannel') return CONFIG.WEBSERVER.URL + '/video-channels/' + id
|
}
|
||||||
else if (type === 'account') return CONFIG.WEBSERVER.URL + '/account/' + id
|
|
||||||
else if (type === 'videoAbuse') return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + id
|
|
||||||
|
|
||||||
return ''
|
function getVideoChannelActivityPubUrl (videoChannel: VideoChannelInstance) {
|
||||||
|
return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannel.uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAccountActivityPubUrl (accountName: string) {
|
||||||
|
return CONFIG.WEBSERVER.URL + '/account/' + accountName
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseInstance) {
|
||||||
|
return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) {
|
||||||
|
const me = accountFollow.AccountFollower
|
||||||
|
const following = accountFollow.AccountFollowing
|
||||||
|
|
||||||
|
return me.url + '#follows/' + following.id
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowInstance) {
|
||||||
|
const follower = accountFollow.AccountFollower
|
||||||
|
const me = accountFollow.AccountFollowing
|
||||||
|
|
||||||
|
return follower.url + '#accepts/follows/' + me.id
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountInstance) {
|
||||||
|
return originalUrl + '#announces/' + byAccount.id
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) {
|
||||||
|
return originalUrl + '#updates/' + updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUndoActivityPubUrl (originalUrl: string) {
|
||||||
|
return originalUrl + '/undo'
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getOrCreateAccount (accountUrl: string) {
|
async function getOrCreateAccount (accountUrl: string) {
|
||||||
|
@ -257,7 +292,6 @@ export {
|
||||||
fetchRemoteAccountAndCreateServer,
|
fetchRemoteAccountAndCreateServer,
|
||||||
activityPubContextify,
|
activityPubContextify,
|
||||||
activityPubCollectionPagination,
|
activityPubCollectionPagination,
|
||||||
getActivityPubUrl,
|
|
||||||
generateThumbnailFromUrl,
|
generateThumbnailFromUrl,
|
||||||
getOrCreateAccount,
|
getOrCreateAccount,
|
||||||
fetchRemoteVideoPreview,
|
fetchRemoteVideoPreview,
|
||||||
|
@ -265,7 +299,16 @@ export {
|
||||||
shareVideoChannelByServer,
|
shareVideoChannelByServer,
|
||||||
shareVideoByServer,
|
shareVideoByServer,
|
||||||
getOrCreateVideoChannel,
|
getOrCreateVideoChannel,
|
||||||
buildSignedActivity
|
buildSignedActivity,
|
||||||
|
getVideoActivityPubUrl,
|
||||||
|
getVideoChannelActivityPubUrl,
|
||||||
|
getAccountActivityPubUrl,
|
||||||
|
getVideoAbuseActivityPubUrl,
|
||||||
|
getAccountFollowActivityPubUrl,
|
||||||
|
getAccountFollowAcceptActivityPubUrl,
|
||||||
|
getAnnounceActivityPubUrl,
|
||||||
|
getUpdateActivityPubUrl,
|
||||||
|
getUndoActivityPubUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import * as validator from 'validator'
|
import * as validator from 'validator'
|
||||||
|
import { Activity, ActivityType } from '../../../../shared/models/activitypub/activity'
|
||||||
import { isAccountAcceptActivityValid, isAccountDeleteActivityValid, isAccountFollowActivityValid } from './account'
|
import { isAccountAcceptActivityValid, isAccountDeleteActivityValid, isAccountFollowActivityValid } from './account'
|
||||||
|
import { isAnnounceValid } from './announce'
|
||||||
import { isActivityPubUrlValid } from './misc'
|
import { isActivityPubUrlValid } from './misc'
|
||||||
|
import { isUndoValid } from './undo'
|
||||||
|
import { isVideoChannelCreateActivityValid, isVideoChannelDeleteActivityValid, isVideoChannelUpdateActivityValid } from './video-channels'
|
||||||
import {
|
import {
|
||||||
isAnnounceValid,
|
|
||||||
isVideoChannelCreateActivityValid,
|
|
||||||
isVideoChannelDeleteActivityValid,
|
|
||||||
isVideoChannelUpdateActivityValid,
|
|
||||||
isVideoFlagValid,
|
isVideoFlagValid,
|
||||||
isVideoTorrentAddActivityValid,
|
isVideoTorrentAddActivityValid,
|
||||||
isVideoTorrentDeleteActivityValid,
|
isVideoTorrentDeleteActivityValid,
|
||||||
|
@ -25,18 +25,23 @@ function isRootActivityValid (activity: any) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const activityCheckers: { [ P in ActivityType ]: (activity: Activity) => boolean } = {
|
||||||
|
Create: checkCreateActivity,
|
||||||
|
Add: checkAddActivity,
|
||||||
|
Update: checkUpdateActivity,
|
||||||
|
Delete: checkDeleteActivity,
|
||||||
|
Follow: checkFollowActivity,
|
||||||
|
Accept: checkAcceptActivity,
|
||||||
|
Announce: checkAnnounceActivity,
|
||||||
|
Undo: checkUndoActivity
|
||||||
|
}
|
||||||
|
|
||||||
function isActivityValid (activity: any) {
|
function isActivityValid (activity: any) {
|
||||||
return isVideoTorrentAddActivityValid(activity) ||
|
const checker = activityCheckers[activity.type]
|
||||||
isVideoChannelCreateActivityValid(activity) ||
|
// Unknown activity type
|
||||||
isVideoTorrentUpdateActivityValid(activity) ||
|
if (!checker) return false
|
||||||
isVideoChannelUpdateActivityValid(activity) ||
|
|
||||||
isVideoTorrentDeleteActivityValid(activity) ||
|
return checker(activity)
|
||||||
isVideoChannelDeleteActivityValid(activity) ||
|
|
||||||
isAccountDeleteActivityValid(activity) ||
|
|
||||||
isAccountFollowActivityValid(activity) ||
|
|
||||||
isAccountAcceptActivityValid(activity) ||
|
|
||||||
isVideoFlagValid(activity) ||
|
|
||||||
isAnnounceValid(activity)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -45,3 +50,41 @@ export {
|
||||||
isRootActivityValid,
|
isRootActivityValid,
|
||||||
isActivityValid
|
isActivityValid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function checkCreateActivity (activity: any) {
|
||||||
|
return isVideoChannelCreateActivityValid(activity) ||
|
||||||
|
isVideoFlagValid(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAddActivity (activity: any) {
|
||||||
|
return isVideoTorrentAddActivityValid(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkUpdateActivity (activity: any) {
|
||||||
|
return isVideoTorrentUpdateActivityValid(activity) ||
|
||||||
|
isVideoChannelUpdateActivityValid(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkDeleteActivity (activity: any) {
|
||||||
|
return isVideoTorrentDeleteActivityValid(activity) ||
|
||||||
|
isVideoChannelDeleteActivityValid(activity) ||
|
||||||
|
isAccountDeleteActivityValid(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkFollowActivity (activity: any) {
|
||||||
|
return isAccountFollowActivityValid(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAcceptActivity (activity: any) {
|
||||||
|
return isAccountAcceptActivityValid(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAnnounceActivity (activity: any) {
|
||||||
|
return isAnnounceValid(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkUndoActivity (activity: any) {
|
||||||
|
return isUndoValid(activity)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { isBaseActivityValid } from './misc'
|
||||||
|
import { isVideoTorrentAddActivityValid } from './videos'
|
||||||
|
import { isVideoChannelCreateActivityValid } from './video-channels'
|
||||||
|
|
||||||
|
function isAnnounceValid (activity: any) {
|
||||||
|
return isBaseActivityValid(activity, 'Announce') &&
|
||||||
|
(
|
||||||
|
isVideoChannelCreateActivityValid(activity.object) ||
|
||||||
|
isVideoTorrentAddActivityValid(activity.object)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
isAnnounceValid
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
export * from './account'
|
export * from './account'
|
||||||
export * from './activity'
|
export * from './activity'
|
||||||
export * from './signature'
|
|
||||||
export * from './misc'
|
export * from './misc'
|
||||||
|
export * from './signature'
|
||||||
|
export * from './undo'
|
||||||
|
export * from './video-channels'
|
||||||
export * from './videos'
|
export * from './videos'
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { isAccountFollowActivityValid } from './account'
|
||||||
|
import { isBaseActivityValid } from './misc'
|
||||||
|
|
||||||
|
function isUndoValid (activity: any) {
|
||||||
|
return isBaseActivityValid(activity, 'Undo') &&
|
||||||
|
(
|
||||||
|
isAccountFollowActivityValid(activity.object)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
isUndoValid
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { isDateValid, isUUIDValid } from '../misc'
|
||||||
|
import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels'
|
||||||
|
import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
|
||||||
|
|
||||||
|
function isVideoChannelCreateActivityValid (activity: any) {
|
||||||
|
return isBaseActivityValid(activity, 'Create') &&
|
||||||
|
isVideoChannelObjectValid(activity.object)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVideoChannelUpdateActivityValid (activity: any) {
|
||||||
|
return isBaseActivityValid(activity, 'Update') &&
|
||||||
|
isVideoChannelObjectValid(activity.object)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVideoChannelDeleteActivityValid (activity: any) {
|
||||||
|
return isBaseActivityValid(activity, 'Delete')
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVideoChannelObjectValid (videoChannel: any) {
|
||||||
|
return videoChannel.type === 'VideoChannel' &&
|
||||||
|
isActivityPubUrlValid(videoChannel.id) &&
|
||||||
|
isVideoChannelNameValid(videoChannel.name) &&
|
||||||
|
isVideoChannelDescriptionValid(videoChannel.content) &&
|
||||||
|
isDateValid(videoChannel.published) &&
|
||||||
|
isDateValid(videoChannel.updated) &&
|
||||||
|
isUUIDValid(videoChannel.uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
isVideoChannelCreateActivityValid,
|
||||||
|
isVideoChannelUpdateActivityValid,
|
||||||
|
isVideoChannelDeleteActivityValid,
|
||||||
|
isVideoChannelObjectValid
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
import * as validator from 'validator'
|
import * as validator from 'validator'
|
||||||
import { ACTIVITY_PUB } from '../../../initializers'
|
import { ACTIVITY_PUB } from '../../../initializers'
|
||||||
import { exists, isDateValid, isUUIDValid } from '../misc'
|
import { exists, isDateValid, isUUIDValid } from '../misc'
|
||||||
import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels'
|
|
||||||
import {
|
import {
|
||||||
isVideoAbuseReasonValid,
|
isVideoAbuseReasonValid,
|
||||||
isVideoDurationValid,
|
isVideoDurationValid,
|
||||||
|
@ -28,6 +27,13 @@ function isVideoTorrentDeleteActivityValid (activity: any) {
|
||||||
return isBaseActivityValid(activity, 'Delete')
|
return isBaseActivityValid(activity, 'Delete')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isVideoFlagValid (activity: any) {
|
||||||
|
return isBaseActivityValid(activity, 'Create') &&
|
||||||
|
activity.object.type === 'Flag' &&
|
||||||
|
isVideoAbuseReasonValid(activity.object.content) &&
|
||||||
|
isActivityPubUrlValid(activity.object.object)
|
||||||
|
}
|
||||||
|
|
||||||
function isActivityPubVideoDurationValid (value: string) {
|
function isActivityPubVideoDurationValid (value: string) {
|
||||||
// https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
|
// https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
|
||||||
return exists(value) &&
|
return exists(value) &&
|
||||||
|
@ -57,57 +63,13 @@ function isVideoTorrentObjectValid (video: any) {
|
||||||
video.url.length !== 0
|
video.url.length !== 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function isVideoFlagValid (activity: any) {
|
|
||||||
return isBaseActivityValid(activity, 'Create') &&
|
|
||||||
activity.object.type === 'Flag' &&
|
|
||||||
isVideoAbuseReasonValid(activity.object.content) &&
|
|
||||||
isActivityPubUrlValid(activity.object.object)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAnnounceValid (activity: any) {
|
|
||||||
return isBaseActivityValid(activity, 'Announce') &&
|
|
||||||
(
|
|
||||||
isVideoChannelCreateActivityValid(activity.object) ||
|
|
||||||
isVideoTorrentAddActivityValid(activity.object)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isVideoChannelCreateActivityValid (activity: any) {
|
|
||||||
return isBaseActivityValid(activity, 'Create') &&
|
|
||||||
isVideoChannelObjectValid(activity.object)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isVideoChannelUpdateActivityValid (activity: any) {
|
|
||||||
return isBaseActivityValid(activity, 'Update') &&
|
|
||||||
isVideoChannelObjectValid(activity.object)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isVideoChannelDeleteActivityValid (activity: any) {
|
|
||||||
return isBaseActivityValid(activity, 'Delete')
|
|
||||||
}
|
|
||||||
|
|
||||||
function isVideoChannelObjectValid (videoChannel: any) {
|
|
||||||
return videoChannel.type === 'VideoChannel' &&
|
|
||||||
isActivityPubUrlValid(videoChannel.id) &&
|
|
||||||
isVideoChannelNameValid(videoChannel.name) &&
|
|
||||||
isVideoChannelDescriptionValid(videoChannel.content) &&
|
|
||||||
isDateValid(videoChannel.published) &&
|
|
||||||
isDateValid(videoChannel.updated) &&
|
|
||||||
isUUIDValid(videoChannel.uuid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
isVideoTorrentAddActivityValid,
|
isVideoTorrentAddActivityValid,
|
||||||
isVideoChannelCreateActivityValid,
|
|
||||||
isVideoTorrentUpdateActivityValid,
|
isVideoTorrentUpdateActivityValid,
|
||||||
isVideoChannelUpdateActivityValid,
|
|
||||||
isVideoChannelDeleteActivityValid,
|
|
||||||
isVideoTorrentDeleteActivityValid,
|
isVideoTorrentDeleteActivityValid,
|
||||||
isVideoFlagValid,
|
isVideoFlagValid
|
||||||
isAnnounceValid,
|
|
||||||
isVideoChannelObjectValid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,8 +1,2 @@
|
||||||
export * from './process-accept'
|
export * from './process'
|
||||||
export * from './process-add'
|
export * from './send'
|
||||||
export * from './process-announce'
|
|
||||||
export * from './process-create'
|
|
||||||
export * from './process-delete'
|
|
||||||
export * from './process-follow'
|
|
||||||
export * from './process-update'
|
|
||||||
export * from './send-request'
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
export * from './process-accept'
|
||||||
|
export * from './process-add'
|
||||||
|
export * from './process-announce'
|
||||||
|
export * from './process-create'
|
||||||
|
export * from './process-delete'
|
||||||
|
export * from './process-follow'
|
||||||
|
export * from './process-undo'
|
||||||
|
export * from './process-update'
|
|
@ -1,13 +1,13 @@
|
||||||
import * as magnetUtil from 'magnet-uri'
|
import * as magnetUtil from 'magnet-uri'
|
||||||
import { VideoTorrentObject } from '../../../shared'
|
import { VideoTorrentObject } from '../../../../shared'
|
||||||
import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object'
|
import { VideoChannelObject } from '../../../../shared/models/activitypub/objects/video-channel-object'
|
||||||
import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos'
|
import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos'
|
||||||
import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../initializers/constants'
|
import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers/constants'
|
||||||
import { AccountInstance } from '../../models/account/account-interface'
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
import { VideoChannelInstance } from '../../models/video/video-channel-interface'
|
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
||||||
import { VideoFileAttributes } from '../../models/video/video-file-interface'
|
import { VideoFileAttributes } from '../../../models/video/video-file-interface'
|
||||||
import { VideoAttributes, VideoInstance } from '../../models/video/video-interface'
|
import { VideoAttributes, VideoInstance } from '../../../models/video/video-interface'
|
||||||
import { VideoPrivacy } from '../../../shared/models/videos/video-privacy.enum'
|
import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
|
||||||
|
|
||||||
function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) {
|
function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) {
|
||||||
return {
|
return {
|
|
@ -1,6 +1,6 @@
|
||||||
import { ActivityAccept } from '../../../shared/models/activitypub/activity'
|
import { ActivityAccept } from '../../../../shared/models/activitypub/activity'
|
||||||
import { database as db } from '../../initializers'
|
import { database as db } from '../../../initializers'
|
||||||
import { AccountInstance } from '../../models/account/account-interface'
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
|
|
||||||
async function processAcceptActivity (activity: ActivityAccept, inboxAccount?: AccountInstance) {
|
async function processAcceptActivity (activity: ActivityAccept, inboxAccount?: AccountInstance) {
|
||||||
if (inboxAccount === undefined) throw new Error('Need to accept on explicit inbox.')
|
if (inboxAccount === undefined) throw new Error('Need to accept on explicit inbox.')
|
|
@ -1,11 +1,11 @@
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
import { VideoTorrentObject } from '../../../shared'
|
import { VideoTorrentObject } from '../../../../shared'
|
||||||
import { ActivityAdd } from '../../../shared/models/activitypub/activity'
|
import { ActivityAdd } from '../../../../shared/models/activitypub/activity'
|
||||||
import { generateThumbnailFromUrl, getOrCreateAccount, logger, retryTransactionWrapper } from '../../helpers'
|
import { generateThumbnailFromUrl, getOrCreateAccount, logger, retryTransactionWrapper } from '../../../helpers'
|
||||||
import { getOrCreateVideoChannel } from '../../helpers/activitypub'
|
import { getOrCreateVideoChannel } from '../../../helpers/activitypub'
|
||||||
import { database as db } from '../../initializers'
|
import { database as db } from '../../../initializers'
|
||||||
import { AccountInstance } from '../../models/account/account-interface'
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
import { VideoChannelInstance } from '../../models/video/video-channel-interface'
|
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
||||||
import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
|
import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
|
||||||
|
|
||||||
async function processAddActivity (activity: ActivityAdd) {
|
async function processAddActivity (activity: ActivityAdd) {
|
|
@ -1,9 +1,9 @@
|
||||||
import { ActivityAnnounce } from '../../../shared/models/activitypub/activity'
|
import { ActivityAnnounce } from '../../../../shared/models/activitypub/activity'
|
||||||
import { getOrCreateAccount } from '../../helpers/activitypub'
|
import { getOrCreateAccount } from '../../../helpers/activitypub'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { database as db } from '../../initializers/index'
|
import { database as db } from '../../../initializers/index'
|
||||||
import { VideoInstance } from '../../models/index'
|
import { VideoInstance } from '../../../models/index'
|
||||||
import { VideoChannelInstance } from '../../models/video/video-channel-interface'
|
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
||||||
import { processAddActivity } from './process-add'
|
import { processAddActivity } from './process-add'
|
||||||
import { processCreateActivity } from './process-create'
|
import { processCreateActivity } from './process-create'
|
||||||
|
|
||||||
|
@ -35,7 +35,8 @@ async function processAnnounceActivity (activity: ActivityAnnounce) {
|
||||||
'Unknown activity object type %s -> %s when announcing activity.', announcedActivity.type, announcedActivity.object.type,
|
'Unknown activity object type %s -> %s when announcing activity.', announcedActivity.type, announcedActivity.object.type,
|
||||||
{ activity: activity.id }
|
{ activity: activity.id }
|
||||||
)
|
)
|
||||||
return Promise.resolve(undefined)
|
|
||||||
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
|
@ -1,9 +1,9 @@
|
||||||
import { ActivityCreate, VideoChannelObject } from '../../../shared'
|
import { ActivityCreate, VideoChannelObject } from '../../../../shared'
|
||||||
import { VideoAbuseObject } from '../../../shared/models/activitypub/objects/video-abuse-object'
|
import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object'
|
||||||
import { logger, retryTransactionWrapper } from '../../helpers'
|
import { logger, retryTransactionWrapper } from '../../../helpers'
|
||||||
import { getActivityPubUrl, getOrCreateAccount } from '../../helpers/activitypub'
|
import { getOrCreateAccount, getVideoChannelActivityPubUrl } from '../../../helpers/activitypub'
|
||||||
import { database as db } from '../../initializers'
|
import { database as db } from '../../../initializers'
|
||||||
import { AccountInstance } from '../../models/account/account-interface'
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
import { videoChannelActivityObjectToDBAttributes } from './misc'
|
import { videoChannelActivityObjectToDBAttributes } from './misc'
|
||||||
|
|
||||||
async function processCreateActivity (activity: ActivityCreate) {
|
async function processCreateActivity (activity: ActivityCreate) {
|
||||||
|
@ -47,7 +47,7 @@ function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateDa
|
||||||
|
|
||||||
const videoChannelData = videoChannelActivityObjectToDBAttributes(videoChannelToCreateData, account)
|
const videoChannelData = videoChannelActivityObjectToDBAttributes(videoChannelToCreateData, account)
|
||||||
videoChannel = db.VideoChannel.build(videoChannelData)
|
videoChannel = db.VideoChannel.build(videoChannelData)
|
||||||
videoChannel.url = getActivityPubUrl('videoChannel', videoChannel.uuid)
|
videoChannel.url = getVideoChannelActivityPubUrl(videoChannel)
|
||||||
|
|
||||||
videoChannel = await videoChannel.save({ transaction: t })
|
videoChannel = await videoChannel.save({ transaction: t })
|
||||||
logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid)
|
logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid)
|
|
@ -1,11 +1,11 @@
|
||||||
import { ActivityDelete } from '../../../shared/models/activitypub/activity'
|
import { ActivityDelete } from '../../../../shared/models/activitypub/activity'
|
||||||
import { getOrCreateAccount } from '../../helpers/activitypub'
|
import { getOrCreateAccount } from '../../../helpers/activitypub'
|
||||||
import { retryTransactionWrapper } from '../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { database as db } from '../../initializers'
|
import { database as db } from '../../../initializers'
|
||||||
import { AccountInstance } from '../../models/account/account-interface'
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
import { VideoChannelInstance } from '../../models/video/video-channel-interface'
|
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
||||||
import { VideoInstance } from '../../models/video/video-interface'
|
import { VideoInstance } from '../../../models/video/video-interface'
|
||||||
|
|
||||||
async function processDeleteActivity (activity: ActivityDelete) {
|
async function processDeleteActivity (activity: ActivityDelete) {
|
||||||
const account = await getOrCreateAccount(activity.actor)
|
const account = await getOrCreateAccount(activity.actor)
|
|
@ -1,9 +1,9 @@
|
||||||
import { ActivityFollow } from '../../../shared/models/activitypub/activity'
|
import { ActivityFollow } from '../../../../shared/models/activitypub/activity'
|
||||||
import { getOrCreateAccount, retryTransactionWrapper } from '../../helpers'
|
import { getOrCreateAccount, retryTransactionWrapper } from '../../../helpers'
|
||||||
import { database as db } from '../../initializers'
|
import { database as db } from '../../../initializers'
|
||||||
import { AccountInstance } from '../../models/account/account-interface'
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
import { sendAccept } from './send-request'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { logger } from '../../helpers/logger'
|
import { sendAccept } from '../send/send-accept'
|
||||||
|
|
||||||
async function processFollowActivity (activity: ActivityFollow) {
|
async function processFollowActivity (activity: ActivityFollow) {
|
||||||
const activityObject = activity.object
|
const activityObject = activity.object
|
||||||
|
@ -33,10 +33,10 @@ async function follow (account: AccountInstance, targetAccountURL: string) {
|
||||||
await db.sequelize.transaction(async t => {
|
await db.sequelize.transaction(async t => {
|
||||||
const targetAccount = await db.Account.loadByUrl(targetAccountURL, t)
|
const targetAccount = await db.Account.loadByUrl(targetAccountURL, t)
|
||||||
|
|
||||||
if (targetAccount === undefined) throw new Error('Unknown account')
|
if (!targetAccount) throw new Error('Unknown account')
|
||||||
if (targetAccount.isOwned() === false) throw new Error('This is not a local account.')
|
if (targetAccount.isOwned() === false) throw new Error('This is not a local account.')
|
||||||
|
|
||||||
await db.AccountFollow.findOrCreate({
|
const [ accountFollow ] = await db.AccountFollow.findOrCreate({
|
||||||
where: {
|
where: {
|
||||||
accountId: account.id,
|
accountId: account.id,
|
||||||
targetAccountId: targetAccount.id
|
targetAccountId: targetAccount.id
|
||||||
|
@ -48,9 +48,11 @@ async function follow (account: AccountInstance, targetAccountURL: string) {
|
||||||
},
|
},
|
||||||
transaction: t
|
transaction: t
|
||||||
})
|
})
|
||||||
|
accountFollow.AccountFollower = account
|
||||||
|
accountFollow.AccountFollowing = targetAccount
|
||||||
|
|
||||||
// Target sends to account he accepted the follow request
|
// Target sends to account he accepted the follow request
|
||||||
return sendAccept(targetAccount, account, t)
|
return sendAccept(accountFollow, t)
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.info('Account uuid %s is followed by account %s.', account.url, targetAccountURL)
|
logger.info('Account uuid %s is followed by account %s.', account.url, targetAccountURL)
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { ActivityUndo } from '../../../../shared/models/activitypub/activity'
|
||||||
|
import { logger } from '../../../helpers/logger'
|
||||||
|
import { database as db } from '../../../initializers'
|
||||||
|
|
||||||
|
async function processUndoActivity (activity: ActivityUndo) {
|
||||||
|
const activityToUndo = activity.object
|
||||||
|
|
||||||
|
if (activityToUndo.type === 'Follow') {
|
||||||
|
const follower = await db.Account.loadByUrl(activity.actor)
|
||||||
|
const following = await db.Account.loadByUrl(activityToUndo.object)
|
||||||
|
const accountFollow = await db.AccountFollow.loadByAccountAndTarget(follower.id, following.id)
|
||||||
|
|
||||||
|
if (!accountFollow) throw new Error(`'Unknown account follow (${follower.id} -> ${following.id}.`)
|
||||||
|
|
||||||
|
await accountFollow.destroy()
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.warn('Unknown activity object type %s -> %s when undo activity.', activityToUndo.type, { activity: activity.id })
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
processUndoActivity
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
|
@ -1,12 +1,12 @@
|
||||||
import { VideoChannelObject, VideoTorrentObject } from '../../../shared'
|
import { VideoChannelObject, VideoTorrentObject } from '../../../../shared'
|
||||||
import { ActivityUpdate } from '../../../shared/models/activitypub/activity'
|
import { ActivityUpdate } from '../../../../shared/models/activitypub/activity'
|
||||||
import { getOrCreateAccount } from '../../helpers/activitypub'
|
import { getOrCreateAccount } from '../../../helpers/activitypub'
|
||||||
import { retryTransactionWrapper } from '../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { resetSequelizeInstance } from '../../helpers/utils'
|
import { resetSequelizeInstance } from '../../../helpers/utils'
|
||||||
import { database as db } from '../../initializers'
|
import { database as db } from '../../../initializers'
|
||||||
import { AccountInstance } from '../../models/account/account-interface'
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
import { VideoInstance } from '../../models/video/video-interface'
|
import { VideoInstance } from '../../../models/video/video-interface'
|
||||||
import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
|
import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
|
||||||
import Bluebird = require('bluebird')
|
import Bluebird = require('bluebird')
|
||||||
|
|
|
@ -1,275 +0,0 @@
|
||||||
import { Transaction } from 'sequelize'
|
|
||||||
import {
|
|
||||||
ActivityAccept,
|
|
||||||
ActivityAdd,
|
|
||||||
ActivityCreate,
|
|
||||||
ActivityDelete,
|
|
||||||
ActivityFollow,
|
|
||||||
ActivityUpdate
|
|
||||||
} from '../../../shared/models/activitypub/activity'
|
|
||||||
import { getActivityPubUrl } from '../../helpers/activitypub'
|
|
||||||
import { logger } from '../../helpers/logger'
|
|
||||||
import { database as db } from '../../initializers'
|
|
||||||
import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../models'
|
|
||||||
import { VideoAbuseInstance } from '../../models/video/video-abuse-interface'
|
|
||||||
import { activitypubHttpJobScheduler } from '../jobs'
|
|
||||||
import { ACTIVITY_PUB } from '../../initializers/constants'
|
|
||||||
import { VideoPrivacy } from '../../../shared/models/videos/video-privacy.enum'
|
|
||||||
|
|
||||||
async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
|
|
||||||
const byAccount = videoChannel.Account
|
|
||||||
|
|
||||||
const videoChannelObject = videoChannel.toActivityPubObject()
|
|
||||||
const data = await createActivityData(videoChannel.url, byAccount, videoChannelObject)
|
|
||||||
|
|
||||||
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
|
|
||||||
const byAccount = videoChannel.Account
|
|
||||||
|
|
||||||
const videoChannelObject = videoChannel.toActivityPubObject()
|
|
||||||
const data = await updateActivityData(videoChannel.url, byAccount, videoChannelObject)
|
|
||||||
|
|
||||||
const accountsInvolved = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id)
|
|
||||||
accountsInvolved.push(byAccount)
|
|
||||||
|
|
||||||
return broadcastToFollowers(data, byAccount, accountsInvolved, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
|
|
||||||
const byAccount = videoChannel.Account
|
|
||||||
|
|
||||||
const data = await deleteActivityData(videoChannel.url, byAccount)
|
|
||||||
|
|
||||||
const accountsInvolved = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id)
|
|
||||||
accountsInvolved.push(byAccount)
|
|
||||||
|
|
||||||
return broadcastToFollowers(data, byAccount, accountsInvolved, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendAddVideo (video: VideoInstance, t: Transaction) {
|
|
||||||
const byAccount = video.VideoChannel.Account
|
|
||||||
|
|
||||||
const videoObject = video.toActivityPubObject()
|
|
||||||
const data = await addActivityData(video.url, byAccount, video, video.VideoChannel.url, videoObject)
|
|
||||||
|
|
||||||
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendUpdateVideo (video: VideoInstance, t: Transaction) {
|
|
||||||
const byAccount = video.VideoChannel.Account
|
|
||||||
|
|
||||||
const videoObject = video.toActivityPubObject()
|
|
||||||
const data = await updateActivityData(video.url, byAccount, videoObject)
|
|
||||||
|
|
||||||
const accountsInvolved = await db.VideoShare.loadAccountsByShare(video.id)
|
|
||||||
accountsInvolved.push(byAccount)
|
|
||||||
|
|
||||||
return broadcastToFollowers(data, byAccount, accountsInvolved, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendDeleteVideo (video: VideoInstance, t: Transaction) {
|
|
||||||
const byAccount = video.VideoChannel.Account
|
|
||||||
|
|
||||||
const data = await deleteActivityData(video.url, byAccount)
|
|
||||||
|
|
||||||
const accountsInvolved = await db.VideoShare.loadAccountsByShare(video.id)
|
|
||||||
accountsInvolved.push(byAccount)
|
|
||||||
|
|
||||||
return broadcastToFollowers(data, byAccount, accountsInvolved, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendDeleteAccount (account: AccountInstance, t: Transaction) {
|
|
||||||
const data = await deleteActivityData(account.url, account)
|
|
||||||
|
|
||||||
return broadcastToFollowers(data, account, [ account ], t)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendVideoChannelAnnounce (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) {
|
|
||||||
const url = getActivityPubUrl('videoChannel', videoChannel.uuid) + '#announce'
|
|
||||||
const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject())
|
|
||||||
|
|
||||||
const data = await announceActivityData(url, byAccount, announcedActivity)
|
|
||||||
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
|
|
||||||
const url = getActivityPubUrl('video', video.uuid) + '#announce'
|
|
||||||
|
|
||||||
const videoChannel = video.VideoChannel
|
|
||||||
const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject())
|
|
||||||
|
|
||||||
const data = await announceActivityData(url, byAccount, announcedActivity)
|
|
||||||
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendVideoAbuse (byAccount: AccountInstance, videoAbuse: VideoAbuseInstance, video: VideoInstance, t: Transaction) {
|
|
||||||
const url = getActivityPubUrl('videoAbuse', videoAbuse.id.toString())
|
|
||||||
const data = await createActivityData(url, byAccount, videoAbuse.toActivityPubObject())
|
|
||||||
|
|
||||||
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendAccept (byAccount: AccountInstance, toAccount: AccountInstance, t: Transaction) {
|
|
||||||
const data = await acceptActivityData(byAccount)
|
|
||||||
|
|
||||||
return unicastTo(data, byAccount, toAccount.inboxUrl, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendFollow (byAccount: AccountInstance, toAccount: AccountInstance, t: Transaction) {
|
|
||||||
const data = await followActivityData(toAccount.url, byAccount)
|
|
||||||
|
|
||||||
return unicastTo(data, byAccount, toAccount.inboxUrl, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export {
|
|
||||||
sendCreateVideoChannel,
|
|
||||||
sendUpdateVideoChannel,
|
|
||||||
sendDeleteVideoChannel,
|
|
||||||
sendAddVideo,
|
|
||||||
sendUpdateVideo,
|
|
||||||
sendDeleteVideo,
|
|
||||||
sendDeleteAccount,
|
|
||||||
sendAccept,
|
|
||||||
sendFollow,
|
|
||||||
sendVideoAbuse,
|
|
||||||
sendVideoChannelAnnounce,
|
|
||||||
sendVideoAnnounce
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
async function broadcastToFollowers (data: any, byAccount: AccountInstance, toAccountFollowers: AccountInstance[], t: Transaction) {
|
|
||||||
const toAccountFollowerIds = toAccountFollowers.map(a => a.id)
|
|
||||||
const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds)
|
|
||||||
if (result.data.length === 0) {
|
|
||||||
logger.info('Not broadcast because of 0 followers for %s.', toAccountFollowerIds.join(', '))
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const jobPayload = {
|
|
||||||
uris: result.data,
|
|
||||||
signatureAccountId: byAccount.id,
|
|
||||||
body: data
|
|
||||||
}
|
|
||||||
|
|
||||||
return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function unicastTo (data: any, byAccount: AccountInstance, toAccountUrl: string, t: Transaction) {
|
|
||||||
const jobPayload = {
|
|
||||||
uris: [ toAccountUrl ],
|
|
||||||
signatureAccountId: byAccount.id,
|
|
||||||
body: data
|
|
||||||
}
|
|
||||||
|
|
||||||
return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getAudience (accountSender: AccountInstance, isPublic = true) {
|
|
||||||
const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls()
|
|
||||||
|
|
||||||
// Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47
|
|
||||||
let to = []
|
|
||||||
let cc = []
|
|
||||||
|
|
||||||
if (isPublic) {
|
|
||||||
to = [ ACTIVITY_PUB.PUBLIC ]
|
|
||||||
cc = followerInboxUrls
|
|
||||||
} else { // Unlisted
|
|
||||||
to = followerInboxUrls
|
|
||||||
cc = [ ACTIVITY_PUB.PUBLIC ]
|
|
||||||
}
|
|
||||||
|
|
||||||
return { to, cc }
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createActivityData (url: string, byAccount: AccountInstance, object: any) {
|
|
||||||
const { to, cc } = await getAudience(byAccount)
|
|
||||||
const activity: ActivityCreate = {
|
|
||||||
type: 'Create',
|
|
||||||
id: url,
|
|
||||||
actor: byAccount.url,
|
|
||||||
to,
|
|
||||||
cc,
|
|
||||||
object
|
|
||||||
}
|
|
||||||
|
|
||||||
return activity
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateActivityData (url: string, byAccount: AccountInstance, object: any) {
|
|
||||||
const { to, cc } = await getAudience(byAccount)
|
|
||||||
const activity: ActivityUpdate = {
|
|
||||||
type: 'Update',
|
|
||||||
id: url,
|
|
||||||
actor: byAccount.url,
|
|
||||||
to,
|
|
||||||
cc,
|
|
||||||
object
|
|
||||||
}
|
|
||||||
|
|
||||||
return activity
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteActivityData (url: string, byAccount: AccountInstance) {
|
|
||||||
const activity: ActivityDelete = {
|
|
||||||
type: 'Delete',
|
|
||||||
id: url,
|
|
||||||
actor: byAccount.url
|
|
||||||
}
|
|
||||||
|
|
||||||
return activity
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addActivityData (url: string, byAccount: AccountInstance, video: VideoInstance, target: string, object: any) {
|
|
||||||
const videoPublic = video.privacy === VideoPrivacy.PUBLIC
|
|
||||||
|
|
||||||
const { to, cc } = await getAudience(byAccount, videoPublic)
|
|
||||||
const activity: ActivityAdd = {
|
|
||||||
type: 'Add',
|
|
||||||
id: url,
|
|
||||||
actor: byAccount.url,
|
|
||||||
to,
|
|
||||||
cc,
|
|
||||||
object,
|
|
||||||
target
|
|
||||||
}
|
|
||||||
|
|
||||||
return activity
|
|
||||||
}
|
|
||||||
|
|
||||||
async function announceActivityData (url: string, byAccount: AccountInstance, object: any) {
|
|
||||||
const activity = {
|
|
||||||
type: 'Announce',
|
|
||||||
id: url,
|
|
||||||
actor: byAccount.url,
|
|
||||||
object
|
|
||||||
}
|
|
||||||
|
|
||||||
return activity
|
|
||||||
}
|
|
||||||
|
|
||||||
async function followActivityData (url: string, byAccount: AccountInstance) {
|
|
||||||
const activity: ActivityFollow = {
|
|
||||||
type: 'Follow',
|
|
||||||
id: byAccount.url,
|
|
||||||
actor: byAccount.url,
|
|
||||||
object: url
|
|
||||||
}
|
|
||||||
|
|
||||||
return activity
|
|
||||||
}
|
|
||||||
|
|
||||||
async function acceptActivityData (byAccount: AccountInstance) {
|
|
||||||
const activity: ActivityAccept = {
|
|
||||||
type: 'Accept',
|
|
||||||
id: byAccount.url,
|
|
||||||
actor: byAccount.url
|
|
||||||
}
|
|
||||||
|
|
||||||
return activity
|
|
||||||
}
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export * from './send-accept'
|
||||||
|
export * from './send-add'
|
||||||
|
export * from './send-announce'
|
||||||
|
export * from './send-create'
|
||||||
|
export * from './send-delete'
|
||||||
|
export * from './send-follow'
|
||||||
|
export * from './send-update'
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { Transaction } from 'sequelize'
|
||||||
|
import { logger } from '../../../helpers/logger'
|
||||||
|
import { ACTIVITY_PUB, database as db } from '../../../initializers'
|
||||||
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
|
import { activitypubHttpJobScheduler } from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler'
|
||||||
|
|
||||||
|
async function broadcastToFollowers (data: any, byAccount: AccountInstance, toAccountFollowers: AccountInstance[], t: Transaction) {
|
||||||
|
const toAccountFollowerIds = toAccountFollowers.map(a => a.id)
|
||||||
|
const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds)
|
||||||
|
if (result.data.length === 0) {
|
||||||
|
logger.info('Not broadcast because of 0 followers for %s.', toAccountFollowerIds.join(', '))
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobPayload = {
|
||||||
|
uris: result.data,
|
||||||
|
signatureAccountId: byAccount.id,
|
||||||
|
body: data
|
||||||
|
}
|
||||||
|
|
||||||
|
return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function unicastTo (data: any, byAccount: AccountInstance, toAccountUrl: string, t: Transaction) {
|
||||||
|
const jobPayload = {
|
||||||
|
uris: [ toAccountUrl ],
|
||||||
|
signatureAccountId: byAccount.id,
|
||||||
|
body: data
|
||||||
|
}
|
||||||
|
|
||||||
|
return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAudience (accountSender: AccountInstance, isPublic = true) {
|
||||||
|
const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls()
|
||||||
|
|
||||||
|
// Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47
|
||||||
|
let to = []
|
||||||
|
let cc = []
|
||||||
|
|
||||||
|
if (isPublic) {
|
||||||
|
to = [ ACTIVITY_PUB.PUBLIC ]
|
||||||
|
cc = followerInboxUrls
|
||||||
|
} else { // Unlisted
|
||||||
|
to = followerInboxUrls
|
||||||
|
cc = [ ACTIVITY_PUB.PUBLIC ]
|
||||||
|
}
|
||||||
|
|
||||||
|
return { to, cc }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
broadcastToFollowers,
|
||||||
|
unicastTo,
|
||||||
|
getAudience
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { Transaction } from 'sequelize'
|
||||||
|
import { ActivityAccept } from '../../../../shared/models/activitypub/activity'
|
||||||
|
import { AccountInstance } from '../../../models'
|
||||||
|
import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
|
||||||
|
import { unicastTo } from './misc'
|
||||||
|
import { getAccountFollowAcceptActivityPubUrl } from '../../../helpers/activitypub'
|
||||||
|
|
||||||
|
async function sendAccept (accountFollow: AccountFollowInstance, t: Transaction) {
|
||||||
|
const follower = accountFollow.AccountFollower
|
||||||
|
const me = accountFollow.AccountFollowing
|
||||||
|
|
||||||
|
const url = getAccountFollowAcceptActivityPubUrl(accountFollow)
|
||||||
|
const data = await acceptActivityData(url, me)
|
||||||
|
|
||||||
|
return unicastTo(data, me, follower.inboxUrl, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
sendAccept
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function acceptActivityData (url: string, byAccount: AccountInstance) {
|
||||||
|
const activity: ActivityAccept = {
|
||||||
|
type: 'Accept',
|
||||||
|
id: url,
|
||||||
|
actor: byAccount.url
|
||||||
|
}
|
||||||
|
|
||||||
|
return activity
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { Transaction } from 'sequelize'
|
||||||
|
import { ActivityAdd } from '../../../../shared/models/activitypub/activity'
|
||||||
|
import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
|
||||||
|
import { AccountInstance, VideoInstance } from '../../../models'
|
||||||
|
import { broadcastToFollowers, getAudience } from './misc'
|
||||||
|
|
||||||
|
async function sendAddVideo (video: VideoInstance, t: Transaction) {
|
||||||
|
const byAccount = video.VideoChannel.Account
|
||||||
|
|
||||||
|
const videoObject = video.toActivityPubObject()
|
||||||
|
const data = await addActivityData(video.url, byAccount, video, video.VideoChannel.url, videoObject)
|
||||||
|
|
||||||
|
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addActivityData (url: string, byAccount: AccountInstance, video: VideoInstance, target: string, object: any) {
|
||||||
|
const videoPublic = video.privacy === VideoPrivacy.PUBLIC
|
||||||
|
|
||||||
|
const { to, cc } = await getAudience(byAccount, videoPublic)
|
||||||
|
const activity: ActivityAdd = {
|
||||||
|
type: 'Add',
|
||||||
|
id: url,
|
||||||
|
actor: byAccount.url,
|
||||||
|
to,
|
||||||
|
cc,
|
||||||
|
object,
|
||||||
|
target
|
||||||
|
}
|
||||||
|
|
||||||
|
return activity
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
addActivityData,
|
||||||
|
sendAddVideo
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { Transaction } from 'sequelize'
|
||||||
|
import { AccountInstance, VideoInstance } from '../../../models'
|
||||||
|
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
||||||
|
import { broadcastToFollowers } from './misc'
|
||||||
|
import { addActivityData } from './send-add'
|
||||||
|
import { createActivityData } from './send-create'
|
||||||
|
import { getAnnounceActivityPubUrl } from '../../../helpers/activitypub'
|
||||||
|
|
||||||
|
async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
|
||||||
|
const url = getAnnounceActivityPubUrl(video.url, byAccount)
|
||||||
|
|
||||||
|
const videoChannel = video.VideoChannel
|
||||||
|
const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject())
|
||||||
|
|
||||||
|
const data = await announceActivityData(url, byAccount, announcedActivity)
|
||||||
|
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendVideoChannelAnnounce (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) {
|
||||||
|
const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount)
|
||||||
|
const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject())
|
||||||
|
|
||||||
|
const data = await announceActivityData(url, byAccount, announcedActivity)
|
||||||
|
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
sendVideoAnnounce,
|
||||||
|
sendVideoChannelAnnounce
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function announceActivityData (url: string, byAccount: AccountInstance, object: any) {
|
||||||
|
const activity = {
|
||||||
|
type: 'Announce',
|
||||||
|
id: url,
|
||||||
|
actor: byAccount.url,
|
||||||
|
object
|
||||||
|
}
|
||||||
|
|
||||||
|
return activity
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { Transaction } from 'sequelize'
|
||||||
|
import { ActivityCreate } from '../../../../shared/models/activitypub/activity'
|
||||||
|
import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
|
||||||
|
import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface'
|
||||||
|
import { broadcastToFollowers, getAudience, unicastTo } from './misc'
|
||||||
|
import { getVideoAbuseActivityPubUrl } from '../../../helpers/activitypub'
|
||||||
|
|
||||||
|
async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
|
||||||
|
const byAccount = videoChannel.Account
|
||||||
|
|
||||||
|
const videoChannelObject = videoChannel.toActivityPubObject()
|
||||||
|
const data = await createActivityData(videoChannel.url, byAccount, videoChannelObject)
|
||||||
|
|
||||||
|
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendVideoAbuse (byAccount: AccountInstance, videoAbuse: VideoAbuseInstance, video: VideoInstance, t: Transaction) {
|
||||||
|
const url = getVideoAbuseActivityPubUrl(videoAbuse)
|
||||||
|
const data = await createActivityData(url, byAccount, videoAbuse.toActivityPubObject())
|
||||||
|
|
||||||
|
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createActivityData (url: string, byAccount: AccountInstance, object: any) {
|
||||||
|
const { to, cc } = await getAudience(byAccount)
|
||||||
|
const activity: ActivityCreate = {
|
||||||
|
type: 'Create',
|
||||||
|
id: url,
|
||||||
|
actor: byAccount.url,
|
||||||
|
to,
|
||||||
|
cc,
|
||||||
|
object
|
||||||
|
}
|
||||||
|
|
||||||
|
return activity
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
sendCreateVideoChannel,
|
||||||
|
sendVideoAbuse,
|
||||||
|
createActivityData
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { Transaction } from 'sequelize'
|
||||||
|
import { ActivityDelete } from '../../../../shared/models/activitypub/activity'
|
||||||
|
import { database as db } from '../../../initializers'
|
||||||
|
import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
|
||||||
|
import { broadcastToFollowers } from './misc'
|
||||||
|
|
||||||
|
async function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
|
||||||
|
const byAccount = videoChannel.Account
|
||||||
|
|
||||||
|
const data = await deleteActivityData(videoChannel.url, byAccount)
|
||||||
|
|
||||||
|
const accountsInvolved = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id)
|
||||||
|
accountsInvolved.push(byAccount)
|
||||||
|
|
||||||
|
return broadcastToFollowers(data, byAccount, accountsInvolved, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendDeleteVideo (video: VideoInstance, t: Transaction) {
|
||||||
|
const byAccount = video.VideoChannel.Account
|
||||||
|
|
||||||
|
const data = await deleteActivityData(video.url, byAccount)
|
||||||
|
|
||||||
|
const accountsInvolved = await db.VideoShare.loadAccountsByShare(video.id)
|
||||||
|
accountsInvolved.push(byAccount)
|
||||||
|
|
||||||
|
return broadcastToFollowers(data, byAccount, accountsInvolved, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendDeleteAccount (account: AccountInstance, t: Transaction) {
|
||||||
|
const data = await deleteActivityData(account.url, account)
|
||||||
|
|
||||||
|
return broadcastToFollowers(data, account, [ account ], t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
sendDeleteVideoChannel,
|
||||||
|
sendDeleteVideo,
|
||||||
|
sendDeleteAccount
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function deleteActivityData (url: string, byAccount: AccountInstance) {
|
||||||
|
const activity: ActivityDelete = {
|
||||||
|
type: 'Delete',
|
||||||
|
id: url,
|
||||||
|
actor: byAccount.url
|
||||||
|
}
|
||||||
|
|
||||||
|
return activity
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { Transaction } from 'sequelize'
|
||||||
|
import { ActivityFollow } from '../../../../shared/models/activitypub/activity'
|
||||||
|
import { AccountInstance } from '../../../models'
|
||||||
|
import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
|
||||||
|
import { unicastTo } from './misc'
|
||||||
|
import { getAccountFollowActivityPubUrl } from '../../../helpers/activitypub'
|
||||||
|
|
||||||
|
async function sendFollow (accountFollow: AccountFollowInstance, t: Transaction) {
|
||||||
|
const me = accountFollow.AccountFollower
|
||||||
|
const following = accountFollow.AccountFollowing
|
||||||
|
|
||||||
|
const url = getAccountFollowActivityPubUrl(accountFollow)
|
||||||
|
const data = await followActivityData(url, me, following)
|
||||||
|
|
||||||
|
return unicastTo(data, me, following.inboxUrl, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function followActivityData (url: string, byAccount: AccountInstance, targetAccount: AccountInstance) {
|
||||||
|
const activity: ActivityFollow = {
|
||||||
|
type: 'Follow',
|
||||||
|
id: url,
|
||||||
|
actor: byAccount.url,
|
||||||
|
object: targetAccount.url
|
||||||
|
}
|
||||||
|
|
||||||
|
return activity
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
sendFollow,
|
||||||
|
followActivityData
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { Transaction } from 'sequelize'
|
||||||
|
import { ActivityFollow, ActivityUndo } from '../../../../shared/models/activitypub/activity'
|
||||||
|
import { AccountInstance } from '../../../models'
|
||||||
|
import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
|
||||||
|
import { unicastTo } from './misc'
|
||||||
|
import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl } from '../../../helpers/activitypub'
|
||||||
|
import { followActivityData } from './send-follow'
|
||||||
|
|
||||||
|
async function sendUndoFollow (accountFollow: AccountFollowInstance, t: Transaction) {
|
||||||
|
const me = accountFollow.AccountFollower
|
||||||
|
const following = accountFollow.AccountFollowing
|
||||||
|
|
||||||
|
const followUrl = getAccountFollowActivityPubUrl(accountFollow)
|
||||||
|
const undoUrl = getUndoActivityPubUrl(followUrl)
|
||||||
|
|
||||||
|
const object = await followActivityData(followUrl, me, following)
|
||||||
|
const data = await undoActivityData(undoUrl, me, object)
|
||||||
|
|
||||||
|
return unicastTo(data, me, following.inboxUrl, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
sendUndoFollow
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function undoActivityData (url: string, byAccount: AccountInstance, object: ActivityFollow) {
|
||||||
|
const activity: ActivityUndo = {
|
||||||
|
type: 'Undo',
|
||||||
|
id: url,
|
||||||
|
actor: byAccount.url,
|
||||||
|
object
|
||||||
|
}
|
||||||
|
|
||||||
|
return activity
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { Transaction } from 'sequelize'
|
||||||
|
import { ActivityUpdate } from '../../../../shared/models/activitypub/activity'
|
||||||
|
import { getUpdateActivityPubUrl } from '../../../helpers/activitypub'
|
||||||
|
import { database as db } from '../../../initializers'
|
||||||
|
import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
|
||||||
|
import { broadcastToFollowers, getAudience } from './misc'
|
||||||
|
|
||||||
|
async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
|
||||||
|
const byAccount = videoChannel.Account
|
||||||
|
|
||||||
|
const url = getUpdateActivityPubUrl(videoChannel.url, videoChannel.updatedAt.toISOString())
|
||||||
|
const videoChannelObject = videoChannel.toActivityPubObject()
|
||||||
|
const data = await updateActivityData(url, byAccount, videoChannelObject)
|
||||||
|
|
||||||
|
const accountsInvolved = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id)
|
||||||
|
accountsInvolved.push(byAccount)
|
||||||
|
|
||||||
|
return broadcastToFollowers(data, byAccount, accountsInvolved, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendUpdateVideo (video: VideoInstance, t: Transaction) {
|
||||||
|
const byAccount = video.VideoChannel.Account
|
||||||
|
|
||||||
|
const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString())
|
||||||
|
const videoObject = video.toActivityPubObject()
|
||||||
|
const data = await updateActivityData(url, byAccount, videoObject)
|
||||||
|
|
||||||
|
const accountsInvolved = await db.VideoShare.loadAccountsByShare(video.id)
|
||||||
|
accountsInvolved.push(byAccount)
|
||||||
|
|
||||||
|
return broadcastToFollowers(data, byAccount, accountsInvolved, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
sendUpdateVideoChannel,
|
||||||
|
sendUpdateVideo
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function updateActivityData (url: string, byAccount: AccountInstance, object: any) {
|
||||||
|
const { to, cc } = await getAudience(byAccount)
|
||||||
|
const activity: ActivityUpdate = {
|
||||||
|
type: 'Update',
|
||||||
|
id: url,
|
||||||
|
actor: byAccount.url,
|
||||||
|
to,
|
||||||
|
cc,
|
||||||
|
object
|
||||||
|
}
|
||||||
|
|
||||||
|
return activity
|
||||||
|
}
|
|
@ -3,10 +3,11 @@ import { computeResolutionsToTranscode, logger } from '../../../helpers'
|
||||||
|
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
import { VideoInstance } from '../../../models'
|
import { VideoInstance } from '../../../models'
|
||||||
import { sendAddVideo } from '../../activitypub/send-request'
|
|
||||||
import { JobScheduler } from '../job-scheduler'
|
import { JobScheduler } from '../job-scheduler'
|
||||||
import { TranscodingJobPayload } from './transcoding-job-scheduler'
|
import { TranscodingJobPayload } from './transcoding-job-scheduler'
|
||||||
import { shareVideoByServer } from '../../../helpers/activitypub'
|
import { shareVideoByServer } from '../../../helpers/activitypub'
|
||||||
|
import { sendAddVideo } from '../../activitypub/send/send-add'
|
||||||
|
|
||||||
async function process (data: TranscodingJobPayload, jobId: number) {
|
async function process (data: TranscodingJobPayload, jobId: number) {
|
||||||
const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID)
|
const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { VideoResolution } from '../../../../shared'
|
||||||
import { logger } from '../../../helpers'
|
import { logger } from '../../../helpers'
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
import { VideoInstance } from '../../../models'
|
import { VideoInstance } from '../../../models'
|
||||||
import { sendUpdateVideo } from '../../activitypub/send-request'
|
import { sendUpdateVideo } from '../../activitypub/send/send-update'
|
||||||
|
|
||||||
async function process (data: { videoUUID: string, resolution: VideoResolution }, jobId: number) {
|
async function process (data: { videoUUID: string, resolution: VideoResolution }, jobId: number) {
|
||||||
const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID)
|
const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import { getActivityPubUrl } from '../helpers/activitypub'
|
|
||||||
import { createPrivateAndPublicKeys } from '../helpers/peertube-crypto'
|
import { createPrivateAndPublicKeys } from '../helpers/peertube-crypto'
|
||||||
import { database as db } from '../initializers'
|
import { database as db } from '../initializers'
|
||||||
import { CONFIG } from '../initializers/constants'
|
import { CONFIG } from '../initializers/constants'
|
||||||
import { UserInstance } from '../models'
|
import { UserInstance } from '../models'
|
||||||
import { createVideoChannel } from './video-channel'
|
import { createVideoChannel } from './video-channel'
|
||||||
import { logger } from '../helpers/logger'
|
import { logger } from '../helpers/logger'
|
||||||
|
import { getAccountActivityPubUrl } from '../helpers/activitypub'
|
||||||
|
|
||||||
async function createUserAccountAndChannel (user: UserInstance, validateUser = true) {
|
async function createUserAccountAndChannel (user: UserInstance, validateUser = true) {
|
||||||
const { account, videoChannel } = await db.sequelize.transaction(async t => {
|
const { account, videoChannel } = await db.sequelize.transaction(async t => {
|
||||||
|
@ -36,7 +36,7 @@ async function createUserAccountAndChannel (user: UserInstance, validateUser = t
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createLocalAccountWithoutKeys (name: string, userId: number, applicationId: number, t: Sequelize.Transaction) {
|
async function createLocalAccountWithoutKeys (name: string, userId: number, applicationId: number, t: Sequelize.Transaction) {
|
||||||
const url = getActivityPubUrl('account', name)
|
const url = getAccountActivityPubUrl(name)
|
||||||
|
|
||||||
const accountInstance = db.Account.build({
|
const accountInstance = db.Account.build({
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import { VideoChannelCreate } from '../../shared/models'
|
import { VideoChannelCreate } from '../../shared/models'
|
||||||
import { logger } from '../helpers'
|
import { logger } from '../helpers'
|
||||||
import { getActivityPubUrl } from '../helpers/activitypub'
|
|
||||||
import { database as db } from '../initializers'
|
import { database as db } from '../initializers'
|
||||||
import { AccountInstance } from '../models'
|
import { AccountInstance } from '../models'
|
||||||
|
import { getVideoChannelActivityPubUrl } from '../helpers/activitypub'
|
||||||
|
|
||||||
async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) {
|
async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) {
|
||||||
const videoChannelData = {
|
const videoChannelData = {
|
||||||
|
@ -14,7 +14,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
|
||||||
}
|
}
|
||||||
|
|
||||||
const videoChannel = db.VideoChannel.build(videoChannelData)
|
const videoChannel = db.VideoChannel.build(videoChannelData)
|
||||||
videoChannel.set('url', getActivityPubUrl('videoChannel', videoChannel.uuid))
|
videoChannel.set('url', getVideoChannelActivityPubUrl(videoChannel))
|
||||||
|
|
||||||
const options = { transaction: t }
|
const options = { transaction: t }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
import * as express from 'express'
|
||||||
|
import { body } from 'express-validator/check'
|
||||||
|
import { isTestInstance } from '../../helpers/core-utils'
|
||||||
|
import { isAccountIdValid } from '../../helpers/custom-validators/activitypub/account'
|
||||||
|
import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers'
|
||||||
|
import { logger } from '../../helpers/logger'
|
||||||
|
import { CONFIG, database as db } from '../../initializers'
|
||||||
|
import { checkErrors } from './utils'
|
||||||
|
import { getServerAccount } from '../../helpers/utils'
|
||||||
|
|
||||||
|
const followValidator = [
|
||||||
|
body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
|
||||||
|
|
||||||
|
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
|
// Force https if the administrator wants to make friends
|
||||||
|
if (isTestInstance() === false && CONFIG.WEBSERVER.SCHEME === 'http') {
|
||||||
|
return res.status(400)
|
||||||
|
.json({
|
||||||
|
error: 'Cannot follow non HTTPS web server.'
|
||||||
|
})
|
||||||
|
.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug('Checking follow parameters', { parameters: req.body })
|
||||||
|
|
||||||
|
checkErrors(req, res, next)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const removeFollowingValidator = [
|
||||||
|
body('accountId').custom(isAccountIdValid).withMessage('Should have a valid account id'),
|
||||||
|
|
||||||
|
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
|
logger.debug('Checking follow parameters', { parameters: req.body })
|
||||||
|
|
||||||
|
checkErrors(req, res, async () => {
|
||||||
|
try {
|
||||||
|
const serverAccount = await getServerAccount()
|
||||||
|
const following = await db.AccountFollow.loadByAccountAndTarget(serverAccount.id, req.params.accountId)
|
||||||
|
|
||||||
|
if (!following) {
|
||||||
|
return res.status(404)
|
||||||
|
.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
res.locals.following = following
|
||||||
|
|
||||||
|
return next()
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Error in remove following validator.', err)
|
||||||
|
return res.sendStatus(500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
followValidator,
|
||||||
|
removeFollowingValidator
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ export * from './account'
|
||||||
export * from './oembed'
|
export * from './oembed'
|
||||||
export * from './activitypub'
|
export * from './activitypub'
|
||||||
export * from './pagination'
|
export * from './pagination'
|
||||||
export * from './servers'
|
export * from './follows'
|
||||||
export * from './sort'
|
export * from './sort'
|
||||||
export * from './users'
|
export * from './users'
|
||||||
export * from './videos'
|
export * from './videos'
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
import * as express from 'express'
|
|
||||||
import { body } from 'express-validator/check'
|
|
||||||
import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers'
|
|
||||||
import { isTestInstance } from '../../helpers/core-utils'
|
|
||||||
import { CONFIG } from '../../initializers/constants'
|
|
||||||
import { logger } from '../../helpers/logger'
|
|
||||||
import { checkErrors } from './utils'
|
|
||||||
|
|
||||||
const followValidator = [
|
|
||||||
body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
|
|
||||||
|
|
||||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
||||||
// Force https if the administrator wants to make friends
|
|
||||||
if (isTestInstance() === false && CONFIG.WEBSERVER.SCHEME === 'http') {
|
|
||||||
return res.status(400)
|
|
||||||
.json({
|
|
||||||
error: 'Cannot follow non HTTPS web server.'
|
|
||||||
})
|
|
||||||
.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug('Checking follow parameters', { parameters: req.body })
|
|
||||||
|
|
||||||
checkErrors(req, res, next)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export {
|
|
||||||
followValidator
|
|
||||||
}
|
|
|
@ -78,7 +78,19 @@ loadByAccountAndTarget = function (accountId: number, targetAccountId: number) {
|
||||||
where: {
|
where: {
|
||||||
accountId,
|
accountId,
|
||||||
targetAccountId
|
targetAccountId
|
||||||
}
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: AccountFollow[ 'sequelize' ].models.Account,
|
||||||
|
required: true,
|
||||||
|
as: 'AccountFollower'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: AccountFollow['sequelize'].models.Account,
|
||||||
|
required: true,
|
||||||
|
as: 'AccountFollowing'
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return AccountFollow.findOne(query)
|
return AccountFollow.findOne(query)
|
||||||
|
|
|
@ -37,7 +37,7 @@ export interface AccountClass {
|
||||||
|
|
||||||
export interface AccountAttributes {
|
export interface AccountAttributes {
|
||||||
name: string
|
name: string
|
||||||
url: string
|
url?: string
|
||||||
publicKey: string
|
publicKey: string
|
||||||
privateKey: string
|
privateKey: string
|
||||||
followersCount: number
|
followersCount: number
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
activityPubContextify,
|
activityPubContextify,
|
||||||
isAccountFollowersCountValid,
|
isAccountFollowersCountValid,
|
||||||
|
@ -15,7 +14,7 @@ import {
|
||||||
isUserUsernameValid
|
isUserUsernameValid
|
||||||
} from '../../helpers'
|
} from '../../helpers'
|
||||||
import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants'
|
import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants'
|
||||||
import { sendDeleteAccount } from '../../lib/activitypub/send-request'
|
import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete'
|
||||||
|
|
||||||
import { addMethodsToModel } from '../utils'
|
import { addMethodsToModel } from '../utils'
|
||||||
import { AccountAttributes, AccountInstance, AccountMethods } from './account-interface'
|
import { AccountAttributes, AccountInstance, AccountMethods } from './account-interface'
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers'
|
||||||
import { isVideoChannelNameValid, isVideoChannelDescriptionValid } from '../../helpers'
|
|
||||||
|
|
||||||
import { addMethodsToModel, getSort } from '../utils'
|
|
||||||
import {
|
|
||||||
VideoChannelInstance,
|
|
||||||
VideoChannelAttributes,
|
|
||||||
|
|
||||||
VideoChannelMethods
|
|
||||||
} from './video-channel-interface'
|
|
||||||
import { sendDeleteVideoChannel } from '../../lib/activitypub/send-request'
|
|
||||||
import { isVideoChannelUrlValid } from '../../helpers/custom-validators/video-channels'
|
import { isVideoChannelUrlValid } from '../../helpers/custom-validators/video-channels'
|
||||||
import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
|
import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
|
||||||
|
import { sendDeleteVideoChannel } from '../../lib/activitypub/send/send-delete'
|
||||||
|
|
||||||
|
import { addMethodsToModel, getSort } from '../utils'
|
||||||
|
import { VideoChannelAttributes, VideoChannelInstance, VideoChannelMethods } from './video-channel-interface'
|
||||||
|
|
||||||
let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes>
|
let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes>
|
||||||
let toFormattedJSON: VideoChannelMethods.ToFormattedJSON
|
let toFormattedJSON: VideoChannelMethods.ToFormattedJSON
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/v
|
||||||
import {
|
import {
|
||||||
createTorrentPromise,
|
createTorrentPromise,
|
||||||
generateImageFromVideoFile,
|
generateImageFromVideoFile,
|
||||||
getActivityPubUrl,
|
|
||||||
getVideoFileHeight,
|
getVideoFileHeight,
|
||||||
isVideoCategoryValid,
|
isVideoCategoryValid,
|
||||||
isVideoDescriptionValid,
|
isVideoDescriptionValid,
|
||||||
|
@ -40,13 +39,13 @@ import {
|
||||||
VIDEO_LICENCES,
|
VIDEO_LICENCES,
|
||||||
VIDEO_PRIVACIES
|
VIDEO_PRIVACIES
|
||||||
} from '../../initializers'
|
} from '../../initializers'
|
||||||
import { sendDeleteVideo } from '../../lib/activitypub/send-request'
|
|
||||||
|
|
||||||
import { addMethodsToModel, getSort } from '../utils'
|
import { addMethodsToModel, getSort } from '../utils'
|
||||||
|
|
||||||
import { TagInstance } from './tag-interface'
|
import { TagInstance } from './tag-interface'
|
||||||
import { VideoFileInstance, VideoFileModel } from './video-file-interface'
|
import { VideoFileInstance, VideoFileModel } from './video-file-interface'
|
||||||
import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
|
import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
|
||||||
|
import { sendDeleteVideo } from '../../lib/index'
|
||||||
|
|
||||||
const Buffer = safeBuffer.Buffer
|
const Buffer = safeBuffer.Buffer
|
||||||
|
|
||||||
|
@ -584,7 +583,7 @@ toActivityPubObject = function (this: VideoInstance) {
|
||||||
|
|
||||||
const videoObject: VideoTorrentObject = {
|
const videoObject: VideoTorrentObject = {
|
||||||
type: 'Video' as 'Video',
|
type: 'Video' as 'Video',
|
||||||
id: getActivityPubUrl('video', this.uuid),
|
id: this.url,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
// https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
|
// https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
|
||||||
duration: 'PT' + this.duration + 'S',
|
duration: 'PT' + this.duration + 'S',
|
||||||
|
@ -615,7 +614,7 @@ toActivityPubObject = function (this: VideoInstance) {
|
||||||
width: THUMBNAILS_SIZE.width,
|
width: THUMBNAILS_SIZE.width,
|
||||||
height: THUMBNAILS_SIZE.height
|
height: THUMBNAILS_SIZE.height
|
||||||
},
|
},
|
||||||
url
|
url // FIXME: needed?
|
||||||
}
|
}
|
||||||
|
|
||||||
return videoObject
|
return videoObject
|
||||||
|
|
|
@ -316,7 +316,7 @@ describe('Test multiple servers', function () {
|
||||||
expect(video1.serverHost).to.equal('localhost:9003')
|
expect(video1.serverHost).to.equal('localhost:9003')
|
||||||
expect(video1.duration).to.equal(5)
|
expect(video1.duration).to.equal(5)
|
||||||
expect(video1.tags).to.deep.equal([ 'tag1p3' ])
|
expect(video1.tags).to.deep.equal([ 'tag1p3' ])
|
||||||
expect(video1.author).to.equal('root')
|
expect(video1.account).to.equal('root')
|
||||||
expect(dateIsValid(video1.createdAt)).to.be.true
|
expect(dateIsValid(video1.createdAt)).to.be.true
|
||||||
expect(dateIsValid(video1.updatedAt)).to.be.true
|
expect(dateIsValid(video1.updatedAt)).to.be.true
|
||||||
|
|
||||||
|
@ -342,7 +342,7 @@ describe('Test multiple servers', function () {
|
||||||
expect(video2.serverHost).to.equal('localhost:9003')
|
expect(video2.serverHost).to.equal('localhost:9003')
|
||||||
expect(video2.duration).to.equal(5)
|
expect(video2.duration).to.equal(5)
|
||||||
expect(video2.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ])
|
expect(video2.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ])
|
||||||
expect(video2.author).to.equal('root')
|
expect(video2.account).to.equal('root')
|
||||||
expect(dateIsValid(video2.createdAt)).to.be.true
|
expect(dateIsValid(video2.createdAt)).to.be.true
|
||||||
expect(dateIsValid(video2.updatedAt)).to.be.true
|
expect(dateIsValid(video2.updatedAt)).to.be.true
|
||||||
|
|
||||||
|
@ -690,7 +690,7 @@ describe('Test multiple servers', function () {
|
||||||
expect(baseVideo.licence).to.equal(video.licence)
|
expect(baseVideo.licence).to.equal(video.licence)
|
||||||
expect(baseVideo.category).to.equal(video.category)
|
expect(baseVideo.category).to.equal(video.category)
|
||||||
expect(baseVideo.nsfw).to.equal(video.nsfw)
|
expect(baseVideo.nsfw).to.equal(video.nsfw)
|
||||||
expect(baseVideo.author).to.equal(video.account)
|
expect(baseVideo.account).to.equal(video.account)
|
||||||
expect(baseVideo.tags).to.deep.equal(video.tags)
|
expect(baseVideo.tags).to.deep.equal(video.tags)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,10 +3,10 @@ import { ActivityPubSignature } from './activitypub-signature'
|
||||||
import { VideoAbuseObject } from './objects/video-abuse-object'
|
import { VideoAbuseObject } from './objects/video-abuse-object'
|
||||||
|
|
||||||
export type Activity = ActivityCreate | ActivityAdd | ActivityUpdate |
|
export type Activity = ActivityCreate | ActivityAdd | ActivityUpdate |
|
||||||
ActivityDelete | ActivityFollow | ActivityAccept | ActivityAnnounce
|
ActivityDelete | ActivityFollow | ActivityAccept | ActivityAnnounce |
|
||||||
|
ActivityUndo
|
||||||
|
|
||||||
// Flag -> report abuse
|
export type ActivityType = 'Create' | 'Add' | 'Update' | 'Delete' | 'Follow' | 'Accept' | 'Announce' | 'Undo'
|
||||||
export type ActivityType = 'Create' | 'Add' | 'Update' | 'Delete' | 'Follow' | 'Accept' | 'Announce'
|
|
||||||
|
|
||||||
export interface BaseActivity {
|
export interface BaseActivity {
|
||||||
'@context'?: any[]
|
'@context'?: any[]
|
||||||
|
@ -51,3 +51,8 @@ export interface ActivityAnnounce extends BaseActivity {
|
||||||
type: 'Announce'
|
type: 'Announce'
|
||||||
object: ActivityCreate | ActivityAdd
|
object: ActivityCreate | ActivityAdd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ActivityUndo extends BaseActivity {
|
||||||
|
type: 'Undo',
|
||||||
|
object: ActivityFollow
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue