Stronger actor association typing in AP functions
This commit is contained in:
parent
511765c9f8
commit
5224c394b3
|
@ -7,6 +7,7 @@ import { asyncMiddleware, checkSignature, localAccountValidator, localVideoChann
|
||||||
import { activityPubValidator } from '../../middlewares/validators/activitypub/activity'
|
import { activityPubValidator } from '../../middlewares/validators/activitypub/activity'
|
||||||
import { queue } from 'async'
|
import { queue } from 'async'
|
||||||
import { ActorModel } from '../../models/activitypub/actor'
|
import { ActorModel } from '../../models/activitypub/actor'
|
||||||
|
import { SignatureActorModel } from '../../typings/models'
|
||||||
|
|
||||||
const inboxRouter = express.Router()
|
const inboxRouter = express.Router()
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const inboxQueue = queue<{ activities: Activity[], signatureActor?: ActorModel, inboxActor?: ActorModel }, Error>((task, cb) => {
|
const inboxQueue = queue<{ activities: Activity[], signatureActor?: SignatureActorModel, inboxActor?: ActorModel }, Error>((task, cb) => {
|
||||||
const options = { signatureActor: task.signatureActor, inboxActor: task.inboxActor }
|
const options = { signatureActor: task.signatureActor, inboxActor: task.inboxActor }
|
||||||
|
|
||||||
processActivities(task.activities, options)
|
processActivities(task.activities, options)
|
||||||
|
|
|
@ -195,7 +195,7 @@ async function fetchAvatarIfExists (actorJSON: ActivityPubActor) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addFetchOutboxJob (actor: ActorModel) {
|
async function addFetchOutboxJob (actor: Pick<ActorModel, 'id' | 'outboxUrl'>) {
|
||||||
// Don't fetch ourselves
|
// Don't fetch ourselves
|
||||||
const serverActor = await getServerActor()
|
const serverActor = await getServerActor()
|
||||||
if (serverActor.id === actor.id) {
|
if (serverActor.id === actor.id) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { ActorModel } from '../../models/activitypub/actor'
|
||||||
import { VideoModel } from '../../models/video/video'
|
import { VideoModel } from '../../models/video/video'
|
||||||
import { VideoCommentModel } from '../../models/video/video-comment'
|
import { VideoCommentModel } from '../../models/video/video-comment'
|
||||||
import { VideoShareModel } from '../../models/video/video-share'
|
import { VideoShareModel } from '../../models/video/video-share'
|
||||||
|
import { ActorModelOnly } from '../../typings/models'
|
||||||
|
|
||||||
function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]): ActivityAudience {
|
function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]): ActivityAudience {
|
||||||
return {
|
return {
|
||||||
|
@ -60,7 +61,7 @@ async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) {
|
||||||
return actors
|
return actors
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAudience (actorSender: ActorModel, isPublic = true) {
|
function getAudience (actorSender: ActorModelOnly, isPublic = true) {
|
||||||
return buildAudience([ actorSender.followersUrl ], isPublic)
|
return buildAudience([ actorSender.followersUrl ], isPublic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,9 @@ import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/vid
|
||||||
import { sequelizeTypescript } from '../../initializers/database'
|
import { sequelizeTypescript } from '../../initializers/database'
|
||||||
import { createPlaylistMiniatureFromUrl } from '../thumbnail'
|
import { createPlaylistMiniatureFromUrl } from '../thumbnail'
|
||||||
import { FilteredModelAttributes } from '../../typings/sequelize'
|
import { FilteredModelAttributes } from '../../typings/sequelize'
|
||||||
|
import { AccountModelId } from '../../typings/models'
|
||||||
|
|
||||||
function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: AccountModel, to: string[]) {
|
function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) {
|
||||||
const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPlaylistPrivacy.PUBLIC : VideoPlaylistPrivacy.UNLISTED
|
const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPlaylistPrivacy.PUBLIC : VideoPlaylistPrivacy.UNLISTED
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -74,7 +75,7 @@ async function createAccountPlaylists (playlistUrls: string[], account: AccountM
|
||||||
}, { concurrency: CRAWL_REQUEST_CONCURRENCY })
|
}, { concurrency: CRAWL_REQUEST_CONCURRENCY })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: AccountModel, to: string[]) {
|
async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) {
|
||||||
const playlistAttributes = playlistObjectToDBAttributes(playlistObject, byAccount, to)
|
const playlistAttributes = playlistObjectToDBAttributes(playlistObject, byAccount, to)
|
||||||
|
|
||||||
if (isArray(playlistObject.attributedTo) && playlistObject.attributedTo.length === 1) {
|
if (isArray(playlistObject.attributedTo) && playlistObject.attributedTo.length === 1) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { ActorModel } from '../../../models/activitypub/actor'
|
||||||
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
||||||
import { addFetchOutboxJob } from '../actor'
|
import { addFetchOutboxJob } from '../actor'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
|
import { SignatureActorModel } from '../../../typings/models'
|
||||||
|
|
||||||
async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) {
|
async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) {
|
||||||
const { byActor: targetActor, inboxActor } = options
|
const { byActor: targetActor, inboxActor } = options
|
||||||
|
@ -19,7 +20,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processAccept (actor: ActorModel, targetActor: ActorModel) {
|
async function processAccept (actor: ActorModel, targetActor: SignatureActorModel) {
|
||||||
const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id)
|
const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id)
|
||||||
if (!follow) throw new Error('Cannot find associated follow.')
|
if (!follow) throw new Error('Cannot find associated follow.')
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { ActivityAnnounce } from '../../../../shared/models/activitypub'
|
import { ActivityAnnounce } from '../../../../shared/models/activitypub'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { sequelizeTypescript } from '../../../initializers'
|
import { sequelizeTypescript } from '../../../initializers'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
import { VideoShareModel } from '../../../models/video/video-share'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/utils'
|
||||||
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
||||||
|
@ -9,6 +8,7 @@ import { Notifier } from '../../notifier'
|
||||||
import { VideoModel } from '../../../models/video/video'
|
import { VideoModel } from '../../../models/video/video'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
|
import { SignatureActorModel } from '../../../typings/models'
|
||||||
|
|
||||||
async function processAnnounceActivity (options: APProcessorOptions<ActivityAnnounce>) {
|
async function processAnnounceActivity (options: APProcessorOptions<ActivityAnnounce>) {
|
||||||
const { activity, byActor: actorAnnouncer } = options
|
const { activity, byActor: actorAnnouncer } = options
|
||||||
|
@ -26,7 +26,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processVideoShare (actorAnnouncer: ActorModel, activity: ActivityAnnounce, notify: boolean) {
|
async function processVideoShare (actorAnnouncer: SignatureActorModel, activity: ActivityAnnounce, notify: boolean) {
|
||||||
const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id
|
const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id
|
||||||
|
|
||||||
let video: VideoModel
|
let video: VideoModel
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { VideoCommentObject } from '../../../../shared/models/activitypub/object
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { sequelizeTypescript } from '../../../initializers'
|
import { sequelizeTypescript } from '../../../initializers'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { resolveThread } from '../video-comments'
|
import { resolveThread } from '../video-comments'
|
||||||
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/utils'
|
||||||
|
@ -14,6 +13,7 @@ import { createOrUpdateVideoPlaylist } from '../playlist'
|
||||||
import { VideoModel } from '../../../models/video/video'
|
import { VideoModel } from '../../../models/video/video'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
import { VideoCommentModel } from '../../../models/video/video-comment'
|
import { VideoCommentModel } from '../../../models/video/video-comment'
|
||||||
|
import { SignatureActorModel } from '../../../typings/models'
|
||||||
|
|
||||||
async function processCreateActivity (options: APProcessorOptions<ActivityCreate>) {
|
async function processCreateActivity (options: APProcessorOptions<ActivityCreate>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
@ -61,7 +61,7 @@ async function processCreateVideo (activity: ActivityCreate, notify: boolean) {
|
||||||
return video
|
return video
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processCreateCacheFile (activity: ActivityCreate, byActor: ActorModel) {
|
async function processCreateCacheFile (activity: ActivityCreate, byActor: SignatureActorModel) {
|
||||||
const cacheFile = activity.object as CacheFileObject
|
const cacheFile = activity.object as CacheFileObject
|
||||||
|
|
||||||
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFile.object })
|
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFile.object })
|
||||||
|
@ -77,7 +77,7 @@ async function processCreateCacheFile (activity: ActivityCreate, byActor: ActorM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processCreateVideoComment (activity: ActivityCreate, byActor: ActorModel, notify: boolean) {
|
async function processCreateVideoComment (activity: ActivityCreate, byActor: SignatureActorModel, notify: boolean) {
|
||||||
const commentObject = activity.object as VideoCommentObject
|
const commentObject = activity.object as VideoCommentObject
|
||||||
const byAccount = byActor.Account
|
const byAccount = byActor.Account
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ async function processCreateVideoComment (activity: ActivityCreate, byActor: Act
|
||||||
if (created && notify) Notifier.Instance.notifyOnNewComment(comment)
|
if (created && notify) Notifier.Instance.notifyOnNewComment(comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processCreatePlaylist (activity: ActivityCreate, byActor: ActorModel) {
|
async function processCreatePlaylist (activity: ActivityCreate, byActor: SignatureActorModel) {
|
||||||
const playlistObject = activity.object as PlaylistObject
|
const playlistObject = activity.object as PlaylistObject
|
||||||
const byAccount = byActor.Account
|
const byAccount = byActor.Account
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { VideoCommentModel } from '../../../models/video/video-comment'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/utils'
|
||||||
import { VideoPlaylistModel } from '../../../models/video/video-playlist'
|
import { VideoPlaylistModel } from '../../../models/video/video-playlist'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
|
import { SignatureActorModel } from '../../../typings/models'
|
||||||
|
|
||||||
async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) {
|
async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
@ -117,7 +118,7 @@ async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelMode
|
||||||
logger.info('Remote video channel %s removed.', videoChannelToRemove.Actor.url)
|
logger.info('Remote video channel %s removed.', videoChannelToRemove.Actor.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
function processDeleteVideoComment (byActor: ActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) {
|
function processDeleteVideoComment (byActor: SignatureActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) {
|
||||||
logger.debug('Removing remote video comment "%s".', videoComment.url)
|
logger.debug('Removing remote video comment "%s".', videoComment.url)
|
||||||
|
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
|
|
|
@ -3,11 +3,11 @@ import { DislikeObject } from '../../../../shared/models/activitypub/objects'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { sequelizeTypescript } from '../../../initializers'
|
import { sequelizeTypescript } from '../../../initializers'
|
||||||
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
|
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/utils'
|
||||||
import { getVideoDislikeActivityPubUrl } from '../url'
|
import { getVideoDislikeActivityPubUrl } from '../url'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
|
import { SignatureActorModel } from '../../../typings/models'
|
||||||
|
|
||||||
async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) {
|
async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
@ -22,7 +22,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: ActorModel) {
|
async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: SignatureActorModel) {
|
||||||
const dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object
|
const dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object
|
||||||
const byAccount = byActor.Account
|
const byAccount = byActor.Account
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,12 @@ import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { sequelizeTypescript } from '../../../initializers'
|
import { sequelizeTypescript } from '../../../initializers'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { VideoAbuseModel } from '../../../models/video/video-abuse'
|
import { VideoAbuseModel } from '../../../models/video/video-abuse'
|
||||||
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
||||||
import { Notifier } from '../../notifier'
|
import { Notifier } from '../../notifier'
|
||||||
import { getAPId } from '../../../helpers/activitypub'
|
import { getAPId } from '../../../helpers/activitypub'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
|
import { SignatureActorModel } from '../../../typings/models'
|
||||||
|
|
||||||
async function processFlagActivity (options: APProcessorOptions<ActivityCreate | ActivityFlag>) {
|
async function processFlagActivity (options: APProcessorOptions<ActivityCreate | ActivityFlag>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
@ -23,7 +23,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: ActorModel) {
|
async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: SignatureActorModel) {
|
||||||
const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject)
|
const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject)
|
||||||
|
|
||||||
logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object))
|
logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object))
|
||||||
|
|
|
@ -10,6 +10,8 @@ import { getAPId } from '../../../helpers/activitypub'
|
||||||
import { getServerActor } from '../../../helpers/utils'
|
import { getServerActor } from '../../../helpers/utils'
|
||||||
import { CONFIG } from '../../../initializers/config'
|
import { CONFIG } from '../../../initializers/config'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
|
import { SignatureActorModel } from '../../../typings/models'
|
||||||
|
import { ActorFollowModelLight } from '../../../typings/models/actor-follow'
|
||||||
|
|
||||||
async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) {
|
async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
@ -26,7 +28,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processFollow (actor: ActorModel, targetActorURL: string) {
|
async function processFollow (byActor: SignatureActorModel, targetActorURL: string) {
|
||||||
const { actorFollow, created, isFollowingInstance } = await sequelizeTypescript.transaction(async t => {
|
const { actorFollow, created, isFollowingInstance } = await sequelizeTypescript.transaction(async t => {
|
||||||
const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t)
|
const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t)
|
||||||
|
|
||||||
|
@ -39,30 +41,30 @@ async function processFollow (actor: ActorModel, targetActorURL: string) {
|
||||||
if (isFollowingInstance && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
|
if (isFollowingInstance && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
|
||||||
logger.info('Rejecting %s because instance followers are disabled.', targetActor.url)
|
logger.info('Rejecting %s because instance followers are disabled.', targetActor.url)
|
||||||
|
|
||||||
await sendReject(actor, targetActor)
|
await sendReject(byActor, targetActor)
|
||||||
|
|
||||||
return { actorFollow: undefined }
|
return { actorFollow: undefined }
|
||||||
}
|
}
|
||||||
|
|
||||||
const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({
|
const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({
|
||||||
where: {
|
where: {
|
||||||
actorId: actor.id,
|
actorId: byActor.id,
|
||||||
targetActorId: targetActor.id
|
targetActorId: targetActor.id
|
||||||
},
|
},
|
||||||
defaults: {
|
defaults: {
|
||||||
actorId: actor.id,
|
actorId: byActor.id,
|
||||||
targetActorId: targetActor.id,
|
targetActorId: targetActor.id,
|
||||||
state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted'
|
state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted'
|
||||||
},
|
},
|
||||||
transaction: t
|
transaction: t
|
||||||
})
|
}) as [ ActorFollowModelLight, boolean ]
|
||||||
|
|
||||||
if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) {
|
if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) {
|
||||||
actorFollow.state = 'accepted'
|
actorFollow.state = 'accepted'
|
||||||
await actorFollow.save({ transaction: t })
|
await actorFollow.save({ transaction: t })
|
||||||
}
|
}
|
||||||
|
|
||||||
actorFollow.ActorFollower = actor
|
actorFollow.ActorFollower = byActor
|
||||||
actorFollow.ActorFollowing = targetActor
|
actorFollow.ActorFollowing = targetActor
|
||||||
|
|
||||||
// Target sends to actor he accepted the follow request
|
// Target sends to actor he accepted the follow request
|
||||||
|
@ -79,5 +81,5 @@ async function processFollow (actor: ActorModel, targetActorURL: string) {
|
||||||
else Notifier.Instance.notifyOfNewUserFollow(actorFollow)
|
else Notifier.Instance.notifyOfNewUserFollow(actorFollow)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Actor %s is followed by actor %s.', targetActorURL, actor.url)
|
logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,12 @@ import { ActivityLike } from '../../../../shared/models/activitypub'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { sequelizeTypescript } from '../../../initializers'
|
import { sequelizeTypescript } from '../../../initializers'
|
||||||
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
|
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/utils'
|
||||||
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
||||||
import { getVideoLikeActivityPubUrl } from '../url'
|
import { getVideoLikeActivityPubUrl } from '../url'
|
||||||
import { getAPId } from '../../../helpers/activitypub'
|
import { getAPId } from '../../../helpers/activitypub'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
|
import { SignatureActorModel } from '../../../typings/models'
|
||||||
|
|
||||||
async function processLikeActivity (options: APProcessorOptions<ActivityLike>) {
|
async function processLikeActivity (options: APProcessorOptions<ActivityLike>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
@ -22,7 +22,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processLikeVideo (byActor: ActorModel, activity: ActivityLike) {
|
async function processLikeVideo (byActor: SignatureActorModel, activity: ActivityLike) {
|
||||||
const videoUrl = getAPId(activity.object)
|
const videoUrl = getAPId(activity.object)
|
||||||
|
|
||||||
const byAccount = byActor.Account
|
const byAccount = byActor.Account
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { ActivityReject } from '../../../../shared/models/activitypub/activity'
|
import { ActivityReject } from '../../../../shared/models/activitypub/activity'
|
||||||
import { sequelizeTypescript } from '../../../initializers'
|
import { sequelizeTypescript } from '../../../initializers'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
|
import { ActorModelOnly } from '../../../typings/models'
|
||||||
|
|
||||||
async function processRejectActivity (options: APProcessorOptions<ActivityReject>) {
|
async function processRejectActivity (options: APProcessorOptions<ActivityReject>) {
|
||||||
const { byActor: targetActor, inboxActor } = options
|
const { byActor: targetActor, inboxActor } = options
|
||||||
|
@ -19,7 +19,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processReject (follower: ActorModel, targetActor: ActorModel) {
|
async function processReject (follower: ActorModelOnly, targetActor: ActorModelOnly) {
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, targetActor.id, t)
|
const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, targetActor.id, t)
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
import { VideoShareModel } from '../../../models/video/video-share'
|
||||||
import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
|
import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
|
import { SignatureActorModel } from '../../../typings/models'
|
||||||
|
|
||||||
async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) {
|
async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
@ -53,7 +54,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processUndoLike (byActor: ActorModel, activity: ActivityUndo) {
|
async function processUndoLike (byActor: SignatureActorModel, activity: ActivityUndo) {
|
||||||
const likeActivity = activity.object as ActivityLike
|
const likeActivity = activity.object as ActivityLike
|
||||||
|
|
||||||
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: likeActivity.object })
|
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: likeActivity.object })
|
||||||
|
@ -76,7 +77,7 @@ async function processUndoLike (byActor: ActorModel, activity: ActivityUndo) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processUndoDislike (byActor: ActorModel, activity: ActivityUndo) {
|
async function processUndoDislike (byActor: SignatureActorModel, activity: ActivityUndo) {
|
||||||
const dislike = activity.object.type === 'Dislike'
|
const dislike = activity.object.type === 'Dislike'
|
||||||
? activity.object
|
? activity.object
|
||||||
: activity.object.object as DislikeObject
|
: activity.object.object as DislikeObject
|
||||||
|
@ -101,7 +102,7 @@ async function processUndoDislike (byActor: ActorModel, activity: ActivityUndo)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processUndoCacheFile (byActor: ActorModel, activity: ActivityUndo) {
|
async function processUndoCacheFile (byActor: SignatureActorModel, activity: ActivityUndo) {
|
||||||
const cacheFileObject = activity.object.object as CacheFileObject
|
const cacheFileObject = activity.object.object as CacheFileObject
|
||||||
|
|
||||||
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object })
|
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object })
|
||||||
|
@ -126,7 +127,7 @@ async function processUndoCacheFile (byActor: ActorModel, activity: ActivityUndo
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function processUndoFollow (follower: ActorModel, followActivity: ActivityFollow) {
|
function processUndoFollow (follower: SignatureActorModel, followActivity: ActivityFollow) {
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t)
|
const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t)
|
||||||
const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t)
|
const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t)
|
||||||
|
@ -139,7 +140,7 @@ function processUndoFollow (follower: ActorModel, followActivity: ActivityFollow
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function processUndoAnnounce (byActor: ActorModel, announceActivity: ActivityAnnounce) {
|
function processUndoAnnounce (byActor: SignatureActorModel, announceActivity: ActivityAnnounce) {
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
const share = await VideoShareModel.loadByUrl(announceActivity.id, t)
|
const share = await VideoShareModel.loadByUrl(announceActivity.id, t)
|
||||||
if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`)
|
if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`)
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { forwardVideoRelatedActivity } from '../send/utils'
|
||||||
import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
|
import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
|
||||||
import { createOrUpdateVideoPlaylist } from '../playlist'
|
import { createOrUpdateVideoPlaylist } from '../playlist'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
|
import { SignatureActorModel } from '../../../typings/models'
|
||||||
|
|
||||||
async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) {
|
async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
@ -52,7 +53,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate) {
|
async function processUpdateVideo (actor: SignatureActorModel, activity: ActivityUpdate) {
|
||||||
const videoObject = activity.object as VideoTorrentObject
|
const videoObject = activity.object as VideoTorrentObject
|
||||||
|
|
||||||
if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
|
if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
|
||||||
|
@ -73,7 +74,7 @@ async function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate)
|
||||||
return updateVideoFromAP(updateOptions)
|
return updateVideoFromAP(updateOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processUpdateCacheFile (byActor: ActorModel, activity: ActivityUpdate) {
|
async function processUpdateCacheFile (byActor: SignatureActorModel, activity: ActivityUpdate) {
|
||||||
const cacheFileObject = activity.object as CacheFileObject
|
const cacheFileObject = activity.object as CacheFileObject
|
||||||
|
|
||||||
if (!isCacheFileObjectValid(cacheFileObject)) {
|
if (!isCacheFileObjectValid(cacheFileObject)) {
|
||||||
|
@ -147,7 +148,7 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processUpdatePlaylist (byActor: ActorModel, activity: ActivityUpdate) {
|
async function processUpdatePlaylist (byActor: SignatureActorModel, activity: ActivityUpdate) {
|
||||||
const playlistObject = activity.object as PlaylistObject
|
const playlistObject = activity.object as PlaylistObject
|
||||||
const byAccount = byActor.Account
|
const byAccount = byActor.Account
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/utils'
|
||||||
import { Redis } from '../../redis'
|
import { Redis } from '../../redis'
|
||||||
import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub'
|
import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
|
import { SignatureActorModel } from '../../../typings/models'
|
||||||
|
|
||||||
async function processViewActivity (options: APProcessorOptions<ActivityCreate | ActivityView>) {
|
async function processViewActivity (options: APProcessorOptions<ActivityCreate | ActivityView>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
@ -18,7 +18,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processCreateView (activity: ActivityView | ActivityCreate, byActor: ActorModel) {
|
async function processCreateView (activity: ActivityView | ActivityCreate, byActor: SignatureActorModel) {
|
||||||
const videoObject = activity.type === 'View' ? activity.object : (activity.object as ViewObject).object
|
const videoObject = activity.type === 'View' ? activity.object : (activity.object as ViewObject).object
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { processDislikeActivity } from './process-dislike'
|
||||||
import { processFlagActivity } from './process-flag'
|
import { processFlagActivity } from './process-flag'
|
||||||
import { processViewActivity } from './process-view'
|
import { processViewActivity } from './process-view'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
|
import { SignatureActorModel } from '../../../typings/models'
|
||||||
|
|
||||||
const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Activity>) => Promise<any> } = {
|
const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Activity>) => Promise<any> } = {
|
||||||
Create: processCreateActivity,
|
Create: processCreateActivity,
|
||||||
|
@ -35,7 +36,7 @@ const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Act
|
||||||
async function processActivities (
|
async function processActivities (
|
||||||
activities: Activity[],
|
activities: Activity[],
|
||||||
options: {
|
options: {
|
||||||
signatureActor?: ActorModel
|
signatureActor?: SignatureActorModel
|
||||||
inboxActor?: ActorModel
|
inboxActor?: ActorModel
|
||||||
outboxUrl?: string
|
outboxUrl?: string
|
||||||
fromFetch?: boolean
|
fromFetch?: boolean
|
||||||
|
@ -43,7 +44,7 @@ async function processActivities (
|
||||||
) {
|
) {
|
||||||
const { outboxUrl, signatureActor, inboxActor, fromFetch = false } = options
|
const { outboxUrl, signatureActor, inboxActor, fromFetch = false } = options
|
||||||
|
|
||||||
const actorsCache: { [ url: string ]: ActorModel } = {}
|
const actorsCache: { [ url: string ]: SignatureActorModel } = {}
|
||||||
|
|
||||||
for (const activity of activities) {
|
for (const activity of activities) {
|
||||||
if (!signatureActor && [ 'Create', 'Announce', 'Like' ].includes(activity.type) === false) {
|
if (!signatureActor && [ 'Create', 'Announce', 'Like' ].includes(activity.type) === false) {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { ActivityAccept, ActivityFollow } from '../../../../shared/models/activitypub'
|
import { ActivityAccept, ActivityFollow } from '../../../../shared/models/activitypub'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
|
||||||
import { getActorFollowAcceptActivityPubUrl, getActorFollowActivityPubUrl } from '../url'
|
import { getActorFollowAcceptActivityPubUrl, getActorFollowActivityPubUrl } from '../url'
|
||||||
import { unicastTo } from './utils'
|
import { unicastTo } from './utils'
|
||||||
import { buildFollowActivity } from './send-follow'
|
import { buildFollowActivity } from './send-follow'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
|
import { ActorFollowModelLight } from '../../../typings/models/actor-follow'
|
||||||
|
import { ActorModelOnly } from '../../../typings/models'
|
||||||
|
|
||||||
async function sendAccept (actorFollow: ActorFollowModel) {
|
async function sendAccept (actorFollow: ActorFollowModelLight) {
|
||||||
const follower = actorFollow.ActorFollower
|
const follower = actorFollow.ActorFollower
|
||||||
const me = actorFollow.ActorFollowing
|
const me = actorFollow.ActorFollowing
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function buildAcceptActivity (url: string, byActor: ActorModel, followActivityData: ActivityFollow): ActivityAccept {
|
function buildAcceptActivity (url: string, byActor: ActorModelOnly, followActivityData: ActivityFollow): ActivityAccept {
|
||||||
return {
|
return {
|
||||||
type: 'Accept',
|
type: 'Accept',
|
||||||
id: url,
|
id: url,
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub'
|
import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { VideoModel } from '../../../models/video/video'
|
import { VideoModel } from '../../../models/video/video'
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
|
||||||
import { broadcastToFollowers } from './utils'
|
import { broadcastToFollowers } from './utils'
|
||||||
import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
|
import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
|
import { ActorModelOnly } from '../../../typings/models'
|
||||||
|
import { VideoShareModelOnly } from '../../../typings/models/video-share'
|
||||||
|
|
||||||
async function buildAnnounceWithVideoAudience (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
|
async function buildAnnounceWithVideoAudience (
|
||||||
|
byActor: ActorModelOnly,
|
||||||
|
videoShare: VideoShareModelOnly,
|
||||||
|
video: VideoModel,
|
||||||
|
t: Transaction
|
||||||
|
) {
|
||||||
const announcedObject = video.url
|
const announcedObject = video.url
|
||||||
|
|
||||||
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
|
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
|
||||||
|
@ -18,7 +23,7 @@ async function buildAnnounceWithVideoAudience (byActor: ActorModel, videoShare:
|
||||||
return { activity, actorsInvolvedInVideo }
|
return { activity, actorsInvolvedInVideo }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendVideoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
|
async function sendVideoAnnounce (byActor: ActorModelOnly, videoShare: VideoShareModelOnly, video: VideoModel, t: Transaction) {
|
||||||
const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t)
|
const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t)
|
||||||
|
|
||||||
logger.info('Creating job to send announce %s.', videoShare.url)
|
logger.info('Creating job to send announce %s.', videoShare.url)
|
||||||
|
@ -27,7 +32,7 @@ async function sendVideoAnnounce (byActor: ActorModel, videoShare: VideoShareMod
|
||||||
return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, t, followersException)
|
return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, t, followersException)
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildAnnounceActivity (url: string, byActor: ActorModel, object: string, audience?: ActivityAudience): ActivityAnnounce {
|
function buildAnnounceActivity (url: string, byActor: ActorModelOnly, object: string, audience?: ActivityAudience): ActivityAnnounce {
|
||||||
if (!audience) audience = getAudience(byActor)
|
if (!audience) audience = getAudience(byActor)
|
||||||
|
|
||||||
return audiencify({
|
return audiencify({
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { ActivityFollow } from '../../../../shared/models/activitypub'
|
import { ActivityFollow } from '../../../../shared/models/activitypub'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
||||||
import { getActorFollowActivityPubUrl } from '../url'
|
import { getActorFollowActivityPubUrl } from '../url'
|
||||||
import { unicastTo } from './utils'
|
import { unicastTo } from './utils'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
|
import { ActorModelOnly } from '../../../typings/models'
|
||||||
|
|
||||||
function sendFollow (actorFollow: ActorFollowModel, t: Transaction) {
|
function sendFollow (actorFollow: ActorFollowModel, t: Transaction) {
|
||||||
const me = actorFollow.ActorFollower
|
const me = actorFollow.ActorFollower
|
||||||
|
@ -21,7 +21,7 @@ function sendFollow (actorFollow: ActorFollowModel, t: Transaction) {
|
||||||
t.afterCommit(() => unicastTo(data, me, following.inboxUrl))
|
t.afterCommit(() => unicastTo(data, me, following.inboxUrl))
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildFollowActivity (url: string, byActor: ActorModel, targetActor: ActorModel): ActivityFollow {
|
function buildFollowActivity (url: string, byActor: ActorModelOnly, targetActor: ActorModelOnly): ActivityFollow {
|
||||||
return {
|
return {
|
||||||
type: 'Follow',
|
type: 'Follow',
|
||||||
id: url,
|
id: url,
|
||||||
|
|
|
@ -4,8 +4,9 @@ import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from
|
||||||
import { unicastTo } from './utils'
|
import { unicastTo } from './utils'
|
||||||
import { buildFollowActivity } from './send-follow'
|
import { buildFollowActivity } from './send-follow'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
|
import { SignatureActorModel } from '../../../typings/models'
|
||||||
|
|
||||||
async function sendReject (follower: ActorModel, following: ActorModel) {
|
async function sendReject (follower: SignatureActorModel, following: ActorModel) {
|
||||||
if (!follower.serverId) { // This should never happen
|
if (!follower.serverId) { // This should never happen
|
||||||
logger.warn('Do not sending reject to local follower.')
|
logger.warn('Do not sending reject to local follower.')
|
||||||
return
|
return
|
||||||
|
|
|
@ -8,21 +8,24 @@ import { VideoModel } from '../../../models/video/video'
|
||||||
import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
|
import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
|
||||||
import { getServerActor } from '../../../helpers/utils'
|
import { getServerActor } from '../../../helpers/utils'
|
||||||
import { afterCommitIfTransaction } from '../../../helpers/database-utils'
|
import { afterCommitIfTransaction } from '../../../helpers/database-utils'
|
||||||
|
import { ActorFollowerException, ActorModelId, ActorModelOnly } from '../../../typings/models'
|
||||||
|
|
||||||
async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
|
async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
|
||||||
byActor: ActorModel,
|
byActor: ActorModelOnly,
|
||||||
video: VideoModel,
|
video: VideoModel,
|
||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
}) {
|
}) {
|
||||||
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(options.video, options.transaction)
|
const { byActor, video, transaction } = options
|
||||||
|
|
||||||
|
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, transaction)
|
||||||
|
|
||||||
// Send to origin
|
// Send to origin
|
||||||
if (options.video.isOwned() === false) {
|
if (video.isOwned() === false) {
|
||||||
const audience = getRemoteVideoAudience(options.video, actorsInvolvedInVideo)
|
const audience = getRemoteVideoAudience(video, actorsInvolvedInVideo)
|
||||||
const activity = activityBuilder(audience)
|
const activity = activityBuilder(audience)
|
||||||
|
|
||||||
return afterCommitIfTransaction(options.transaction, () => {
|
return afterCommitIfTransaction(transaction, () => {
|
||||||
return unicastTo(activity, options.byActor, options.video.VideoChannel.Account.Actor.sharedInboxUrl)
|
return unicastTo(activity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,15 +33,15 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud
|
||||||
const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo)
|
const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo)
|
||||||
const activity = activityBuilder(audience)
|
const activity = activityBuilder(audience)
|
||||||
|
|
||||||
const actorsException = [ options.byActor ]
|
const actorsException = [ byActor ]
|
||||||
|
|
||||||
return broadcastToFollowers(activity, options.byActor, actorsInvolvedInVideo, options.transaction, actorsException)
|
return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, transaction, actorsException)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function forwardVideoRelatedActivity (
|
async function forwardVideoRelatedActivity (
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
t: Transaction,
|
t: Transaction,
|
||||||
followersException: ActorModel[] = [],
|
followersException: ActorFollowerException[] = [],
|
||||||
video: VideoModel
|
video: VideoModel
|
||||||
) {
|
) {
|
||||||
// Mastodon does not add our announces in audience, so we forward to them manually
|
// Mastodon does not add our announces in audience, so we forward to them manually
|
||||||
|
@ -51,7 +54,7 @@ async function forwardVideoRelatedActivity (
|
||||||
async function forwardActivity (
|
async function forwardActivity (
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
t: Transaction,
|
t: Transaction,
|
||||||
followersException: ActorModel[] = [],
|
followersException: ActorFollowerException[] = [],
|
||||||
additionalFollowerUrls: string[] = []
|
additionalFollowerUrls: string[] = []
|
||||||
) {
|
) {
|
||||||
logger.info('Forwarding activity %s.', activity.id)
|
logger.info('Forwarding activity %s.', activity.id)
|
||||||
|
@ -85,10 +88,10 @@ async function forwardActivity (
|
||||||
|
|
||||||
async function broadcastToFollowers (
|
async function broadcastToFollowers (
|
||||||
data: any,
|
data: any,
|
||||||
byActor: ActorModel,
|
byActor: ActorModelId,
|
||||||
toFollowersOf: ActorModel[],
|
toFollowersOf: ActorModelId[],
|
||||||
t: Transaction,
|
t: Transaction,
|
||||||
actorsException: ActorModel[] = []
|
actorsException: ActorFollowerException[] = []
|
||||||
) {
|
) {
|
||||||
const uris = await computeFollowerUris(toFollowersOf, actorsException, t)
|
const uris = await computeFollowerUris(toFollowersOf, actorsException, t)
|
||||||
|
|
||||||
|
@ -97,16 +100,16 @@ async function broadcastToFollowers (
|
||||||
|
|
||||||
async function broadcastToActors (
|
async function broadcastToActors (
|
||||||
data: any,
|
data: any,
|
||||||
byActor: ActorModel,
|
byActor: ActorModelId,
|
||||||
toActors: ActorModel[],
|
toActors: ActorModelOnly[],
|
||||||
t?: Transaction,
|
t?: Transaction,
|
||||||
actorsException: ActorModel[] = []
|
actorsException: ActorFollowerException[] = []
|
||||||
) {
|
) {
|
||||||
const uris = await computeUris(toActors, actorsException)
|
const uris = await computeUris(toActors, actorsException)
|
||||||
return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor))
|
return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor))
|
||||||
}
|
}
|
||||||
|
|
||||||
function broadcastTo (uris: string[], data: any, byActor: ActorModel) {
|
function broadcastTo (uris: string[], data: any, byActor: ActorModelId) {
|
||||||
if (uris.length === 0) return undefined
|
if (uris.length === 0) return undefined
|
||||||
|
|
||||||
logger.debug('Creating broadcast job.', { uris })
|
logger.debug('Creating broadcast job.', { uris })
|
||||||
|
@ -120,7 +123,7 @@ function broadcastTo (uris: string[], data: any, byActor: ActorModel) {
|
||||||
return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })
|
return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })
|
||||||
}
|
}
|
||||||
|
|
||||||
function unicastTo (data: any, byActor: ActorModel, toActorUrl: string) {
|
function unicastTo (data: any, byActor: ActorModelId, toActorUrl: string) {
|
||||||
logger.debug('Creating unicast job.', { uri: toActorUrl })
|
logger.debug('Creating unicast job.', { uri: toActorUrl })
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
|
@ -145,7 +148,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function computeFollowerUris (toFollowersOf: ActorModel[], actorsException: ActorModel[], t: Transaction) {
|
async function computeFollowerUris (toFollowersOf: ActorModelId[], actorsException: ActorFollowerException[], t: Transaction) {
|
||||||
const toActorFollowerIds = toFollowersOf.map(a => a.id)
|
const toActorFollowerIds = toFollowersOf.map(a => a.id)
|
||||||
|
|
||||||
const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t)
|
const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t)
|
||||||
|
@ -154,7 +157,7 @@ async function computeFollowerUris (toFollowersOf: ActorModel[], actorsException
|
||||||
return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
|
return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function computeUris (toActors: ActorModel[], actorsException: ActorModel[] = []) {
|
async function computeUris (toActors: ActorModelOnly[], actorsException: ActorFollowerException[] = []) {
|
||||||
const serverActor = await getServerActor()
|
const serverActor = await getServerActor()
|
||||||
const targetUrls = toActors
|
const targetUrls = toActors
|
||||||
.filter(a => a.id !== serverActor.id) // Don't send to ourselves
|
.filter(a => a.id !== serverActor.id) // Don't send to ourselves
|
||||||
|
@ -167,7 +170,7 @@ async function computeUris (toActors: ActorModel[], actorsException: ActorModel[
|
||||||
.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
|
.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildSharedInboxesException (actorsException: ActorModel[]) {
|
async function buildSharedInboxesException (actorsException: ActorFollowerException[]) {
|
||||||
const serverActor = await getServerActor()
|
const serverActor = await getServerActor()
|
||||||
|
|
||||||
return actorsException
|
return actorsException
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { WEBSERVER } from '../../initializers/constants'
|
import { WEBSERVER } from '../../initializers/constants'
|
||||||
import { ActorModel } from '../../models/activitypub/actor'
|
|
||||||
import { ActorFollowModel } from '../../models/activitypub/actor-follow'
|
|
||||||
import { VideoModel } from '../../models/video/video'
|
import { VideoModel } from '../../models/video/video'
|
||||||
import { VideoAbuseModel } from '../../models/video/video-abuse'
|
import { VideoAbuseModel } from '../../models/video/video-abuse'
|
||||||
import { VideoCommentModel } from '../../models/video/video-comment'
|
import { VideoCommentModel } from '../../models/video/video-comment'
|
||||||
import { VideoFileModel } from '../../models/video/video-file'
|
import { VideoFileModel } from '../../models/video/video-file'
|
||||||
import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist'
|
import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist'
|
||||||
import { VideoPlaylistModel } from '../../models/video/video-playlist'
|
import { VideoPlaylistModel } from '../../models/video/video-playlist'
|
||||||
|
import { ActorModelOnly, ActorModelUrl } from '../../typings/models'
|
||||||
|
import { ActorFollowModelLight } from '../../typings/models/actor-follow'
|
||||||
|
|
||||||
function getVideoActivityPubUrl (video: VideoModel) {
|
function getVideoActivityPubUrl (video: VideoModel) {
|
||||||
return WEBSERVER.URL + '/videos/watch/' + video.uuid
|
return WEBSERVER.URL + '/videos/watch/' + video.uuid
|
||||||
|
@ -46,15 +46,15 @@ function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseModel) {
|
||||||
return WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id
|
return WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoViewActivityPubUrl (byActor: ActorModel, video: VideoModel) {
|
function getVideoViewActivityPubUrl (byActor: ActorModelUrl, video: VideoModel) {
|
||||||
return byActor.url + '/views/videos/' + video.id + '/' + new Date().toISOString()
|
return byActor.url + '/views/videos/' + video.id + '/' + new Date().toISOString()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoLikeActivityPubUrl (byActor: ActorModel, video: VideoModel | { id: number }) {
|
function getVideoLikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) {
|
||||||
return byActor.url + '/likes/' + video.id
|
return byActor.url + '/likes/' + video.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoDislikeActivityPubUrl (byActor: ActorModel, video: VideoModel | { id: number }) {
|
function getVideoDislikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) {
|
||||||
return byActor.url + '/dislikes/' + video.id
|
return byActor.url + '/dislikes/' + video.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,22 +74,22 @@ function getVideoDislikesActivityPubUrl (video: VideoModel) {
|
||||||
return video.url + '/dislikes'
|
return video.url + '/dislikes'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActorFollowActivityPubUrl (follower: ActorModel, following: ActorModel) {
|
function getActorFollowActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) {
|
||||||
return follower.url + '/follows/' + following.id
|
return follower.url + '/follows/' + following.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModel) {
|
function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModelLight) {
|
||||||
const follower = actorFollow.ActorFollower
|
const follower = actorFollow.ActorFollower
|
||||||
const me = actorFollow.ActorFollowing
|
const me = actorFollow.ActorFollowing
|
||||||
|
|
||||||
return follower.url + '/accepts/follows/' + me.id
|
return follower.url + '/accepts/follows/' + me.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActorFollowRejectActivityPubUrl (follower: ActorModel, following: ActorModel) {
|
function getActorFollowRejectActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) {
|
||||||
return follower.url + '/rejects/follows/' + following.id
|
return follower.url + '/rejects/follows/' + following.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoAnnounceActivityPubUrl (byActor: ActorModel, video: VideoModel) {
|
function getVideoAnnounceActivityPubUrl (byActor: ActorModelOnly, video: VideoModel) {
|
||||||
return video.url + '/announces/' + byActor.id
|
return video.url + '/announces/' + byActor.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ import {
|
||||||
import { ActorModel } from '../../models/activitypub/actor'
|
import { ActorModel } from '../../models/activitypub/actor'
|
||||||
import { TagModel } from '../../models/video/tag'
|
import { TagModel } from '../../models/video/tag'
|
||||||
import { VideoModel } from '../../models/video/video'
|
import { VideoModel } from '../../models/video/video'
|
||||||
import { VideoChannelModel } from '../../models/video/video-channel'
|
|
||||||
import { VideoFileModel } from '../../models/video/video-file'
|
import { VideoFileModel } from '../../models/video/video-file'
|
||||||
import { getOrCreateActorAndServerAndModel } from './actor'
|
import { getOrCreateActorAndServerAndModel } from './actor'
|
||||||
import { addVideoComments } from './video-comments'
|
import { addVideoComments } from './video-comments'
|
||||||
|
@ -54,9 +53,9 @@ import { ThumbnailModel } from '../../models/video/thumbnail'
|
||||||
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
|
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { FilteredModelAttributes } from '../../typings/sequelize'
|
import { FilteredModelAttributes } from '../../typings/sequelize'
|
||||||
import { Hooks } from '../plugins/hooks'
|
|
||||||
import { autoBlacklistVideoIfNeeded } from '../video-blacklist'
|
import { autoBlacklistVideoIfNeeded } from '../video-blacklist'
|
||||||
import { ActorFollowScoreCache } from '../files-cache'
|
import { ActorFollowScoreCache } from '../files-cache'
|
||||||
|
import { AccountModelIdActor, VideoChannelModelId, VideoChannelModelIdActor } from '../../typings/models'
|
||||||
|
|
||||||
async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) {
|
async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) {
|
||||||
if (
|
if (
|
||||||
|
@ -239,8 +238,8 @@ async function getOrCreateVideoAndAccountAndChannel (options: {
|
||||||
async function updateVideoFromAP (options: {
|
async function updateVideoFromAP (options: {
|
||||||
video: VideoModel,
|
video: VideoModel,
|
||||||
videoObject: VideoTorrentObject,
|
videoObject: VideoTorrentObject,
|
||||||
account: AccountModel,
|
account: AccountModelIdActor,
|
||||||
channel: VideoChannelModel,
|
channel: VideoChannelModelIdActor,
|
||||||
overrideTo?: string[]
|
overrideTo?: string[]
|
||||||
}) {
|
}) {
|
||||||
const { video, videoObject, account, channel, overrideTo } = options
|
const { video, videoObject, account, channel, overrideTo } = options
|
||||||
|
@ -550,7 +549,7 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
|
||||||
}
|
}
|
||||||
|
|
||||||
async function videoActivityObjectToDBAttributes (
|
async function videoActivityObjectToDBAttributes (
|
||||||
videoChannel: VideoChannelModel,
|
videoChannel: VideoChannelModelId,
|
||||||
videoObject: VideoTorrentObject,
|
videoObject: VideoTorrentObject,
|
||||||
to: string[] = []
|
to: string[] = []
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { Activity } from '../../shared/models/activitypub'
|
import { Activity } from '../../shared/models/activitypub'
|
||||||
import { ActorModel } from '../models/activitypub/actor'
|
import { ActorModel } from '../models/activitypub/actor'
|
||||||
|
import { SignatureActorModel } from './models'
|
||||||
|
|
||||||
export type APProcessorOptions<T extends Activity> = {
|
export type APProcessorOptions<T extends Activity> = {
|
||||||
activity: T
|
activity: T
|
||||||
byActor: ActorModel
|
byActor: SignatureActorModel
|
||||||
inboxActor?: ActorModel
|
inboxActor?: ActorModel
|
||||||
fromFetch?: boolean
|
fromFetch?: boolean
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { VideoCaptionModel } from '../models/video/video-caption'
|
||||||
import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
|
import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
|
||||||
import { RegisteredPlugin } from '../lib/plugins/plugin-manager'
|
import { RegisteredPlugin } from '../lib/plugins/plugin-manager'
|
||||||
import { PluginModel } from '../models/server/plugin'
|
import { PluginModel } from '../models/server/plugin'
|
||||||
|
import { SignatureActorModel } from './models'
|
||||||
|
|
||||||
declare module 'express' {
|
declare module 'express' {
|
||||||
|
|
||||||
|
@ -75,7 +76,7 @@ declare module 'express' {
|
||||||
}
|
}
|
||||||
|
|
||||||
signature?: {
|
signature?: {
|
||||||
actor: ActorModel
|
actor: SignatureActorModel
|
||||||
}
|
}
|
||||||
|
|
||||||
authenticated?: boolean
|
authenticated?: boolean
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { ActorFollowModel } from '../../models/activitypub/actor-follow'
|
||||||
|
import { ActorModelOnly } from './actor'
|
||||||
|
|
||||||
|
export type ActorFollowModelOnly = Omit<ActorFollowModel, 'ActorFollower' | 'ActorFollowing'>
|
||||||
|
export type ActorFollowModelLight = ActorFollowModelOnly & {
|
||||||
|
ActorFollower: ActorModelOnly
|
||||||
|
ActorFollowing: ActorModelOnly
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { ActorModel } from '../../models/activitypub/actor'
|
||||||
|
import { VideoChannelModel } from '../../models/video/video-channel'
|
||||||
|
import { AccountModel } from '../../models/account/account'
|
||||||
|
import { FunctionProperties } from '../utils'
|
||||||
|
|
||||||
|
export type VideoChannelModelId = FunctionProperties<VideoChannelModel>
|
||||||
|
export type AccountModelId = FunctionProperties<AccountModel> | Pick<AccountModel, 'id'>
|
||||||
|
|
||||||
|
export type VideoChannelModelIdActor = VideoChannelModelId & Pick<VideoChannelModel, 'Actor'>
|
||||||
|
export type AccountModelIdActor = AccountModelId & Pick<AccountModel, 'Actor'>
|
||||||
|
|
||||||
|
export type ActorModelUrl = Pick<ActorModel, 'url'>
|
||||||
|
export type ActorModelOnly = Omit<ActorModel, 'Account' | 'VideoChannel' | 'ActorFollowing' | 'Avatar' | 'ActorFollowers' | 'Server'>
|
||||||
|
export type ActorModelId = Pick<ActorModelOnly, 'id'>
|
||||||
|
|
||||||
|
export type SignatureActorModel = ActorModelOnly & {
|
||||||
|
VideoChannel: VideoChannelModelIdActor
|
||||||
|
|
||||||
|
Account: AccountModelIdActor
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ActorFollowerException = Pick<ActorModel, 'sharedInboxUrl' | 'inboxUrl'>
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './actor'
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { VideoShareModel } from '../../models/video/video-share'
|
||||||
|
|
||||||
|
export type VideoShareModelOnly = Omit<VideoShareModel, 'Actor' | 'Video'>
|
|
@ -0,0 +1,3 @@
|
||||||
|
export type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T]
|
||||||
|
|
||||||
|
export type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>
|
Loading…
Reference in New Issue