Add outbox
This commit is contained in:
parent
b1cbc0dd3e
commit
e71bcc0f4b
|
@ -6,7 +6,7 @@ import { executeIfActivityPub, localAccountValidator } from '../../middlewares'
|
|||
import { pageToStartAndCount } from '../../helpers'
|
||||
import { AccountInstance, VideoChannelInstance } from '../../models'
|
||||
import { activityPubCollectionPagination } from '../../helpers/activitypub'
|
||||
import { ACTIVITY_PUB } from '../../initializers/constants'
|
||||
import { ACTIVITY_PUB, CONFIG } from '../../initializers/constants'
|
||||
import { asyncMiddleware } from '../../middlewares/async'
|
||||
import { videosGetValidator } from '../../middlewares/validators/videos'
|
||||
import { VideoInstance } from '../../models/video/video-interface'
|
||||
|
@ -60,7 +60,7 @@ async function accountFollowersController (req: express.Request, res: express.Re
|
|||
const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)
|
||||
|
||||
const result = await db.AccountFollow.listAcceptedFollowerUrlsForApi([ account.id ], start, count)
|
||||
const activityPubResult = activityPubCollectionPagination(req.url, page, result)
|
||||
const activityPubResult = activityPubCollectionPagination(CONFIG.WEBSERVER.URL + req.url, page, result)
|
||||
|
||||
return res.json(activityPubResult)
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ async function accountFollowingController (req: express.Request, res: express.Re
|
|||
const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)
|
||||
|
||||
const result = await db.AccountFollow.listAcceptedFollowingUrlsForApi([ account.id ], start, count)
|
||||
const activityPubResult = activityPubCollectionPagination(req.url, page, result)
|
||||
const activityPubResult = activityPubCollectionPagination(CONFIG.WEBSERVER.URL + req.url, page, result)
|
||||
|
||||
return res.json(activityPubResult)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import * as express from 'express'
|
||||
import { activityPubClientRouter } from './client'
|
||||
import { inboxRouter } from './inbox'
|
||||
import { outboxRouter } from './outbox'
|
||||
|
||||
const activityPubRouter = express.Router()
|
||||
|
||||
activityPubRouter.use('/', inboxRouter)
|
||||
activityPubRouter.use('/', outboxRouter)
|
||||
activityPubRouter.use('/', activityPubClientRouter)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import * as express from 'express'
|
||||
import { Activity, ActivityAdd } from '../../../shared/models/activitypub/activity'
|
||||
import { activityPubCollectionPagination, activityPubContextify } from '../../helpers/activitypub'
|
||||
import { database as db } from '../../initializers'
|
||||
import { addActivityData } from '../../lib/activitypub/send/send-add'
|
||||
import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
|
||||
import { announceActivityData } from '../../lib/index'
|
||||
import { asyncMiddleware, localAccountValidator } from '../../middlewares'
|
||||
import { AccountInstance } from '../../models/account/account-interface'
|
||||
import { pageToStartAndCount } from '../../helpers/core-utils'
|
||||
import { ACTIVITY_PUB } from '../../initializers/constants'
|
||||
|
||||
const outboxRouter = express.Router()
|
||||
|
||||
outboxRouter.get('/account/:name/outbox',
|
||||
localAccountValidator,
|
||||
asyncMiddleware(outboxController)
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
outboxRouter
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function outboxController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
const account: AccountInstance = res.locals.account
|
||||
|
||||
const page = req.params.page || 1
|
||||
const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)
|
||||
|
||||
const data = await db.Video.listAllAndSharedByAccountForOutbox(account.id, start, count)
|
||||
const activities: Activity[] = []
|
||||
|
||||
console.log(account.url)
|
||||
|
||||
for (const video of data.data) {
|
||||
const videoObject = video.toActivityPubObject()
|
||||
let addActivity: ActivityAdd = await addActivityData(video.url, account, video, video.VideoChannel.url, videoObject)
|
||||
|
||||
// This is a shared video
|
||||
if (video.VideoShare !== undefined) {
|
||||
const url = getAnnounceActivityPubUrl(video.url, account)
|
||||
const announceActivity = await announceActivityData(url, account, addActivity)
|
||||
activities.push(announceActivity)
|
||||
} else {
|
||||
activities.push(addActivity)
|
||||
}
|
||||
}
|
||||
|
||||
const newResult = {
|
||||
data: activities,
|
||||
total: data.total
|
||||
}
|
||||
const json = activityPubCollectionPagination(account.url + '/outbox', page, newResult)
|
||||
|
||||
return res.json(json).end()
|
||||
}
|
|
@ -2,6 +2,7 @@ import { Activity } from '../../shared/models/activitypub/activity'
|
|||
import { ResultList } from '../../shared/models/result-list.model'
|
||||
import { AccountInstance } from '../models/account/account-interface'
|
||||
import { signObject } from './peertube-crypto'
|
||||
import { ACTIVITY_PUB } from '../initializers/constants'
|
||||
|
||||
function activityPubContextify <T> (data: T) {
|
||||
return Object.assign(data,{
|
||||
|
@ -24,20 +25,32 @@ function activityPubContextify <T> (data: T) {
|
|||
}
|
||||
|
||||
function activityPubCollectionPagination (url: string, page: number, result: ResultList<any>) {
|
||||
const baseUrl = url.split('?').shift
|
||||
let next: string
|
||||
let prev: string
|
||||
|
||||
// There are more results
|
||||
if (result.total > ((page + 1) * ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)) {
|
||||
next = url + '?page=' + (page + 1)
|
||||
}
|
||||
|
||||
if (page > 1) {
|
||||
prev = url + '?page=' + (page - 1)
|
||||
}
|
||||
|
||||
const orderedCollectionPagination = {
|
||||
id: url + '?page=' + page,
|
||||
type: 'OrderedCollectionPage',
|
||||
prev,
|
||||
next,
|
||||
partOf: url,
|
||||
orderedItems: result.data
|
||||
}
|
||||
|
||||
const obj = {
|
||||
id: baseUrl,
|
||||
type: 'Collection',
|
||||
id: url,
|
||||
type: 'OrderedCollection',
|
||||
totalItems: result.total,
|
||||
first: {
|
||||
id: baseUrl + '?page=' + page,
|
||||
type: 'CollectionPage',
|
||||
totalItems: result.total,
|
||||
next: baseUrl + '?page=' + (page + 1),
|
||||
partOf: baseUrl,
|
||||
items: result.data
|
||||
}
|
||||
orderedItems: orderedCollectionPagination
|
||||
}
|
||||
|
||||
return activityPubContextify(obj)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { Transaction } from 'sequelize'
|
||||
import { ActivityAdd } from '../../../../shared/index'
|
||||
import { ActivityAnnounce, ActivityCreate } from '../../../../shared/models/activitypub/activity'
|
||||
import { AccountInstance, VideoInstance } from '../../../models'
|
||||
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
||||
import { getAnnounceActivityPubUrl } from '../url'
|
||||
import { broadcastToFollowers } from './misc'
|
||||
import { addActivityData } from './send-add'
|
||||
import { createActivityData } from './send-create'
|
||||
import { getAnnounceActivityPubUrl } from '../url'
|
||||
|
||||
async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
|
||||
const url = getAnnounceActivityPubUrl(video.url, byAccount)
|
||||
|
@ -24,17 +26,8 @@ async function sendVideoChannelAnnounce (byAccount: AccountInstance, videoChanne
|
|||
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
sendVideoAnnounce,
|
||||
sendVideoChannelAnnounce
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function announceActivityData (url: string, byAccount: AccountInstance, object: any) {
|
||||
const activity = {
|
||||
async function announceActivityData (url: string, byAccount: AccountInstance, object: ActivityCreate | ActivityAdd) {
|
||||
const activity: ActivityAnnounce = {
|
||||
type: 'Announce',
|
||||
id: url,
|
||||
actor: byAccount.url,
|
||||
|
@ -43,3 +36,11 @@ async function announceActivityData (url: string, byAccount: AccountInstance, ob
|
|||
|
||||
return activity
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
sendVideoAnnounce,
|
||||
sendVideoChannelAnnounce,
|
||||
announceActivityData
|
||||
}
|
||||
|
|
|
@ -221,8 +221,8 @@ async function createListAcceptedFollowForApiQuery (
|
|||
'INNER JOIN "Accounts" AS "Follows" ON "AccountFollows"."' + secondJoin + '" = "Follows"."id" ' +
|
||||
'WHERE "Accounts"."id" = ANY ($accountIds) AND "AccountFollows"."state" = \'accepted\' '
|
||||
|
||||
if (start !== undefined) query += 'LIMIT ' + start
|
||||
if (count !== undefined) query += ', ' + count
|
||||
if (count !== undefined) query += 'LIMIT ' + count
|
||||
if (start !== undefined) query += ' OFFSET ' + start
|
||||
|
||||
const options = {
|
||||
bind: { accountIds },
|
||||
|
|
|
@ -7,6 +7,7 @@ import { Video as FormattedVideo, VideoDetails as FormattedDetailsVideo } from '
|
|||
import { TagAttributes, TagInstance } from './tag-interface'
|
||||
import { VideoChannelInstance } from './video-channel-interface'
|
||||
import { VideoFileAttributes, VideoFileInstance } from './video-file-interface'
|
||||
import { VideoShareInstance } from './video-share-interface'
|
||||
|
||||
export namespace VideoMethods {
|
||||
export type GetThumbnailName = (this: VideoInstance) => string
|
||||
|
@ -44,6 +45,11 @@ export namespace VideoMethods {
|
|||
export type ListOwnedAndPopulateAccountAndTags = () => Bluebird<VideoInstance[]>
|
||||
export type ListOwnedByAccount = (account: string) => Bluebird<VideoInstance[]>
|
||||
|
||||
export type ListAllAndSharedByAccountForOutbox = (
|
||||
accountId: number,
|
||||
start: number,
|
||||
count: number
|
||||
) => Bluebird< ResultList<VideoInstance> >
|
||||
export type ListForApi = (start: number, count: number, sort: string) => Bluebird< ResultList<VideoInstance> >
|
||||
export type ListUserVideosForApi = (userId: number, start: number, count: number, sort: string) => Bluebird< ResultList<VideoInstance> >
|
||||
export type SearchAndPopulateAccountAndServerAndTags = (
|
||||
|
@ -73,6 +79,7 @@ export namespace VideoMethods {
|
|||
export interface VideoClass {
|
||||
generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
|
||||
list: VideoMethods.List
|
||||
listAllAndSharedByAccountForOutbox: VideoMethods.ListAllAndSharedByAccountForOutbox
|
||||
listForApi: VideoMethods.ListForApi
|
||||
listUserVideosForApi: VideoMethods.ListUserVideosForApi
|
||||
listOwnedAndPopulateAccountAndTags: VideoMethods.ListOwnedAndPopulateAccountAndTags
|
||||
|
@ -115,6 +122,7 @@ export interface VideoAttributes {
|
|||
VideoChannel?: VideoChannelInstance
|
||||
Tags?: TagInstance[]
|
||||
VideoFiles?: VideoFileInstance[]
|
||||
VideoShare?: VideoShareInstance
|
||||
}
|
||||
|
||||
export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> {
|
||||
|
|
|
@ -78,6 +78,7 @@ let getLanguageLabel: VideoMethods.GetLanguageLabel
|
|||
let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
|
||||
let list: VideoMethods.List
|
||||
let listForApi: VideoMethods.ListForApi
|
||||
let listAllAndSharedByAccountForOutbox: VideoMethods.ListAllAndSharedByAccountForOutbox
|
||||
let listUserVideosForApi: VideoMethods.ListUserVideosForApi
|
||||
let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID
|
||||
let listOwnedAndPopulateAccountAndTags: VideoMethods.ListOwnedAndPopulateAccountAndTags
|
||||
|
@ -266,6 +267,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
|
|||
|
||||
generateThumbnailFromData,
|
||||
list,
|
||||
listAllAndSharedByAccountForOutbox,
|
||||
listForApi,
|
||||
listUserVideosForApi,
|
||||
listOwnedAndPopulateAccountAndTags,
|
||||
|
@ -348,6 +350,14 @@ function associate (models) {
|
|||
},
|
||||
onDelete: 'cascade'
|
||||
})
|
||||
|
||||
Video.hasMany(models.VideoShare, {
|
||||
foreignKey: {
|
||||
name: 'videoId',
|
||||
allowNull: false
|
||||
},
|
||||
onDelete: 'cascade'
|
||||
})
|
||||
}
|
||||
|
||||
function afterDestroy (video: VideoInstance) {
|
||||
|
@ -775,6 +785,54 @@ list = function () {
|
|||
return Video.findAll(query)
|
||||
}
|
||||
|
||||
listAllAndSharedByAccountForOutbox = function (accountId: number, start: number, count: number) {
|
||||
const queryVideo = 'SELECT "Video"."id" FROM "Videos" AS "Video" ' +
|
||||
'INNER JOIN "VideoChannels" AS "VideoChannel" ON "VideoChannel"."id" = "Video"."channelId" ' +
|
||||
'WHERE "VideoChannel"."accountId" = ' + accountId
|
||||
const queryVideoShare = 'SELECT "Video"."id" FROM "VideoShares" AS "VideoShare" ' +
|
||||
'INNER JOIN "Videos" AS "Video" ON "Video"."id" = "VideoShare"."videoId" ' +
|
||||
'INNER JOIN "VideoChannels" AS "VideoChannel" ON "VideoChannel"."id" = "Video"."channelId" ' +
|
||||
'WHERE "VideoShare"."accountId" = ' + accountId
|
||||
const rawQuery = `(${queryVideo}) UNION (${queryVideoShare}) LIMIT ${count} OFFSET ${start}`
|
||||
|
||||
const query = {
|
||||
distinct: true,
|
||||
offset: start,
|
||||
limit: count,
|
||||
order: [ getSort('createdAt'), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ],
|
||||
where: {
|
||||
id: {
|
||||
[Sequelize.Op.in]: Sequelize.literal('(' + rawQuery + ')')
|
||||
}
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: Video['sequelize'].models.VideoShare,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
model: Video['sequelize'].models.VideoChannel,
|
||||
required: true,
|
||||
include: [
|
||||
{
|
||||
model: Video['sequelize'].models.Account,
|
||||
required: true
|
||||
}
|
||||
]
|
||||
},
|
||||
Video['sequelize'].models.Tag,
|
||||
Video['sequelize'].models.VideoFile
|
||||
]
|
||||
}
|
||||
|
||||
return Video.findAndCountAll(query).then(({ rows, count }) => {
|
||||
return {
|
||||
data: rows,
|
||||
total: count
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
listUserVideosForApi = function (userId: number, start: number, count: number, sort: string) {
|
||||
const query = {
|
||||
distinct: true,
|
||||
|
|
Loading…
Reference in New Issue