Add outbox

pull/128/head
Chocobozzz 2017-11-21 18:23:10 +01:00
parent b1cbc0dd3e
commit e71bcc0f4b
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
8 changed files with 170 additions and 28 deletions

View File

@ -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)
}

View File

@ -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)
// ---------------------------------------------------------------------------

View File

@ -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()
}

View File

@ -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)

View File

@ -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
}

View File

@ -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 },

View File

@ -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> {

View File

@ -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,