<div class="margin-content">
+ <div class="no-results" i18n *ngIf="channelPagination.totalItems === 0">This account does not have channels.</div>
+
<div class="channels" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true">
<div class="section channel" *ngFor="let videoChannel of videoChannels">
<div class="section-title">
export abstract class Actor implements ActorServer {
id: number
- uuid: string
url: string
name: string
host: string
protected constructor (hash: ActorServer) {
this.id = hash.id
- this.uuid = hash.uuid
this.url = hash.url
this.name = hash.name
this.host = hash.host
}
dropRedis () {
- redis-cli KEYS "bull-localhost:900$1*" | grep -v empty | xargs --no-run-if-empty redis-cli DEL
- redis-cli KEYS "redis-localhost:900$1*" | grep -v empty | xargs --no-run-if-empty redis-cli DEL
+ port=$((9000+$1))
+
+ redis-cli KEYS "bull-localhost:$port*" | grep -v empty | xargs --no-run-if-empty redis-cli DEL
+ redis-cli KEYS "redis-localhost:$port*" | grep -v empty | xargs --no-run-if-empty redis-cli DEL
+ redis-cli KEYS "*redis-localhost:$port-" | grep -v empty | xargs --no-run-if-empty redis-cli DEL
}
seq=$(seq 1 6)
set -eu
npm run build:server
+npm run setup:cli
+
npm run travis -- lint
mocha --exit --require ts-node/register/type-check --bail server/tests/index.ts
user: {
id: user.id,
account: {
- id: account.id,
- uuid: account.Actor.uuid
+ id: account.id
}
}
}).end()
})
setAsyncActorKeys(videoChannelCreated.Actor)
- .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err }))
+ .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.url, { err }))
auditLogger.create(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannelCreated.toFormattedJSON()))
- logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid)
+ logger.info('Video channel %s created.', videoChannelCreated.Actor.url)
return res.json({
videoChannel: {
- id: videoChannelCreated.id,
- uuid: videoChannelCreated.Actor.uuid
+ id: videoChannelCreated.id
}
}).end()
}
new VideoChannelAuditView(videoChannelInstanceUpdated.toFormattedJSON()),
oldVideoChannelAuditKeys
)
- logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid)
+ logger.info('Video channel %s updated.', videoChannelInstance.Actor.url)
})
} catch (err) {
logger.debug('Cannot update the video channel.', { err })
await videoChannelInstance.destroy({ transaction: t })
auditLogger.delete(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannelInstance.toFormattedJSON()))
- logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid)
+ logger.info('Video channel %s deleted.', videoChannelInstance.Actor.url)
})
return res.type('json').status(204).end()
import * as Bluebird from 'bluebird'
import { Response } from 'express'
import 'express-validator'
-import * as validator from 'validator'
import { AccountModel } from '../../models/account/account'
import { isUserDescriptionValid, isUserUsernameValid } from './users'
import { exists } from './misc'
return isUserDescriptionValid(value)
}
-function doesAccountIdExist (id: number | string, res: Response, sendNotFound = true) {
- let promise: Bluebird<AccountModel>
-
- if (validator.isInt('' + id)) {
- promise = AccountModel.load(+id)
- } else { // UUID
- promise = AccountModel.loadByUUID('' + id)
- }
+function doesAccountIdExist (id: number, res: Response, sendNotFound = true) {
+ const promise = AccountModel.load(id)
return doesAccountExist(promise, res, sendNotFound)
}
return processVideoChannelExist(videoChannel, res)
}
-async function doesVideoChannelIdExist (id: number | string, res: express.Response) {
- let videoChannel: VideoChannelModel
- if (validator.isInt('' + id)) {
- videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id)
- } else { // UUID
- videoChannel = await VideoChannelModel.loadByUUIDAndPopulateAccount('' + id)
- }
+async function doesVideoChannelIdExist (id: number, res: express.Response) {
+ const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id)
return processVideoChannelExist(videoChannel, res)
}
// ---------------------------------------------------------------------------
-const LAST_MIGRATION_VERSION = 380
+const LAST_MIGRATION_VERSION = 385
// ---------------------------------------------------------------------------
--- /dev/null
+import * as Sequelize from 'sequelize'
+
+async function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize,
+ db: any
+}): Promise<void> {
+ await utils.queryInterface.removeColumn('actor', 'uuid')
+}
+
+function down (options) {
+ throw new Error('Not implemented.')
+}
+
+export {
+ up,
+ down
+}
return actor.save()
})
.catch(err => {
- logger.error('Cannot set public/private keys of actor %d.', actor.uuid, { err })
+ logger.error('Cannot set public/private keys of actor %d.', actor.url, { err })
return actor
})
}
const followersCount = await fetchActorTotalItems(attributes.followers)
const followingCount = await fetchActorTotalItems(attributes.following)
- actorInstance.set('type', attributes.type)
- actorInstance.set('uuid', attributes.uuid)
- actorInstance.set('preferredUsername', attributes.preferredUsername)
- actorInstance.set('url', attributes.id)
- actorInstance.set('publicKey', attributes.publicKey.publicKeyPem)
- actorInstance.set('followersCount', followersCount)
- actorInstance.set('followingCount', followingCount)
- actorInstance.set('inboxUrl', attributes.inbox)
- actorInstance.set('outboxUrl', attributes.outbox)
- actorInstance.set('sharedInboxUrl', attributes.endpoints.sharedInbox)
- actorInstance.set('followersUrl', attributes.followers)
- actorInstance.set('followingUrl', attributes.following)
+ actorInstance.type = attributes.type
+ actorInstance.preferredUsername = attributes.preferredUsername
+ actorInstance.url = attributes.id
+ actorInstance.publicKey = attributes.publicKey.publicKeyPem
+ actorInstance.followersCount = followersCount
+ actorInstance.followingCount = followingCount
+ actorInstance.inboxUrl = attributes.inbox
+ actorInstance.outboxUrl = attributes.outbox
+ actorInstance.sharedInboxUrl = attributes.endpoints.sharedInbox
+ actorInstance.followersUrl = attributes.followers
+ actorInstance.followingUrl = attributes.following
}
async function updateActorAvatarInstance (actorInstance: ActorModel, avatarName: string, t: Transaction) {
const actor = new ActorModel({
type: actorJSON.type,
- uuid: actorJSON.uuid,
preferredUsername: actorJSON.preferredUsername,
url: actorJSON.id,
publicKey: actorJSON.publicKey.publicKeyPem,
}
async function processDeleteAccount (accountToRemove: AccountModel) {
- logger.debug('Removing remote account "%s".', accountToRemove.Actor.uuid)
+ logger.debug('Removing remote account "%s".', accountToRemove.Actor.url)
await sequelizeTypescript.transaction(async t => {
await accountToRemove.destroy({ transaction: t })
})
- logger.info('Remote account with uuid %s removed.', accountToRemove.Actor.uuid)
+ logger.info('Remote account %s removed.', accountToRemove.Actor.url)
}
async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelModel) {
- logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.uuid)
+ logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.url)
await sequelizeTypescript.transaction(async t => {
await videoChannelToRemove.destroy({ transaction: t })
})
- logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.Actor.uuid)
+ logger.info('Remote video channel %s removed.', videoChannelToRemove.Actor.url)
}
function processDeleteVideoComment (byActor: ActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) {
async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) {
const actorAttributesToUpdate = activity.object as ActivityPubActor
- logger.debug('Updating remote account "%s".', actorAttributesToUpdate.uuid)
+ logger.debug('Updating remote account "%s".', actorAttributesToUpdate.url)
let accountOrChannelInstance: AccountModel | VideoChannelModel
let actorFieldsSave: object
let accountOrChannelFieldsSave: object
await accountOrChannelInstance.save({ transaction: t })
})
- logger.info('Remote account with uuid %s updated', actorAttributesToUpdate.uuid)
+ logger.info('Remote account %s updated', actorAttributesToUpdate.url)
} catch (err) {
if (actor !== undefined && actorFieldsSave !== undefined) {
resetSequelizeInstance(actor, actorFieldsSave)
import * as express from 'express'
import { param, query } from 'express-validator/check'
-import { doesAccountIdExist, isAccountNameValid, doesAccountNameWithHostExist } from '../../helpers/custom-validators/accounts'
-import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
+import { doesAccountIdExist, doesAccountNameWithHostExist } from '../../helpers/custom-validators/accounts'
+import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc'
import { logger } from '../../helpers/logger'
import { areValidationErrors } from './utils'
import { isValidRSSFeed } from '../../helpers/custom-validators/feeds'
import { doesVideoChannelIdExist, doesVideoChannelNameWithHostExist } from '../../helpers/custom-validators/video-channels'
import { doesVideoExist } from '../../helpers/custom-validators/videos'
-import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor'
const videoFeedsValidator = [
param('format').optional().custom(isValidRSSFeed).withMessage('Should have a valid format (rss, atom, json)'),
query('format').optional().custom(isValidRSSFeed).withMessage('Should have a valid format (rss, atom, json)'),
- query('accountId').optional().custom(isIdOrUUIDValid),
- query('accountName').optional().custom(isAccountNameValid),
- query('videoChannelId').optional().custom(isIdOrUUIDValid),
- query('videoChannelName').optional().custom(isActorPreferredUsernameValid),
+ query('accountId').optional().custom(isIdValid),
+ query('accountName').optional(),
+ query('videoChannelId').optional().custom(isIdValid),
+ query('videoChannelName').optional(),
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking feeds parameters', { parameters: req.query })
attributes: [ 'id', 'name' ],
include: [
{
- attributes: [ 'id', 'uuid', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
+ attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
model: ActorModel.unscoped(),
required: true,
where: whereActor,
return AccountModel.findByPk(id, { transaction })
}
- static loadByUUID (uuid: string) {
- const query = {
- include: [
- {
- model: ActorModel,
- required: true,
- where: {
- uuid
- }
- }
- ]
- }
-
- return AccountModel.findOne(query)
- }
-
static loadByNameWithHost (nameWithHost: string) {
const [ accountName, host ] = nameWithHost.split('@')
return {
id: this.id,
- uuid: actor.uuid,
name: actor.name,
displayName: this.getDisplayName(),
url: actor.url,
Column,
CreatedAt,
DataType,
- Default,
DefaultScope,
ForeignKey,
HasMany,
HasOne,
Is,
- IsUUID,
Model,
Scopes,
Table,
{
fields: [ 'avatarId' ]
},
- {
- fields: [ 'uuid' ],
- unique: true
- },
{
fields: [ 'followersUrl' ]
}
@Column(DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES)))
type: ActivityPubActorType
- @AllowNull(false)
- @Default(DataType.UUIDV4)
- @IsUUID(4)
- @Column(DataType.UUID)
- uuid: string
-
@AllowNull(false)
@Is('ActorPreferredUsername', value => throwIfNotValid(value, isActorPreferredUsernameValid, 'actor preferred username'))
@Column
return {
id: this.id,
url: this.url,
- uuid: this.uuid,
name: this.preferredUsername,
host: this.getHost(),
hostRedundancyAllowed: this.getRedundancyAllowed(),
endpoints: {
sharedInbox: this.sharedInboxUrl
},
- uuid: this.uuid,
publicKey: {
id: this.getPublicKeyUrl(),
owner: this.url,
attributes: [ 'name', 'description', 'id', 'actorId' ],
include: [
{
- attributes: [ 'uuid', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
+ attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ],
model: ActorModel.unscoped(),
required: true,
include: [
.findByPk(id)
}
- static loadByUUIDAndPopulateAccount (uuid: string) {
- const query = {
- include: [
- {
- model: ActorModel,
- required: true,
- where: {
- uuid
- }
- }
- ]
- }
-
- return VideoChannelModel
- .scope([ ScopeNames.WITH_ACCOUNT ])
- .findOne(query)
- }
-
static loadByUrlAndPopulateAccount (url: string) {
const query = {
include: [
return {
id: this.id,
- uuid: actor.uuid,
name: actor.name,
displayName: this.getDisplayName(),
url: actor.url,
import { Account } from '../../../../shared/models/actors'
import {
checkTmpIsEmpty,
- checkVideoFilesWereRemoved, cleanupTests,
+ checkVideoFilesWereRemoved,
+ cleanupTests,
createUser,
doubleFollow,
flushAndRunMultipleServers,
updateMyUser,
userLogin
} from '../../../../shared/extra-utils'
-import {
- getMyUserInformation,
- killallServers,
- ServerInfo,
- testImage,
- updateMyAvatar,
- uploadVideo
-} from '../../../../shared/extra-utils/index'
+import { getMyUserInformation, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../../../shared/extra-utils/index'
import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../../../shared/extra-utils/users/accounts'
import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
import { User } from '../../../../shared/models/users'
describe('Test users with multiple servers', function () {
let servers: ServerInfo[] = []
let user: User
- let userAccountName: string
- let userAccountUUID: string
- let userVideoChannelUUID: string
let userId: number
let videoUUID: string
let userAccessToken: string
+ let userAvatarFilename: string
before(async function () {
this.timeout(120000)
userAccessToken = await userLogin(servers[ 0 ], user)
}
- {
- const res = await getMyUserInformation(servers[0].url, userAccessToken)
- const account: Account = res.body.account
- userAccountName = account.name + '@' + account.host
- userAccountUUID = account.uuid
- }
-
- {
- const res = await getMyUserInformation(servers[ 0 ].url, servers[ 0 ].accessToken)
- const user: User = res.body
- userVideoChannelUUID = user.videoChannels[0].uuid
- }
-
{
const resVideo = await uploadVideo(servers[ 0 ].url, userAccessToken, {})
videoUUID = resVideo.body.video.uuid
const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
user = res.body
+
+ const account: Account = user.account
expect(user.account.displayName).to.equal('my super display name')
await waitJobs(servers)
const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
user = res.body
- await testImage(servers[0].url, 'avatar2-resized', user.account.avatar.path, '.png')
+ userAvatarFilename = user.account.avatar.path
+
+ await testImage(servers[0].url, 'avatar2-resized', userAvatarFilename, '.png')
await waitJobs(servers)
})
it('Should list account videos', async function () {
for (const server of servers) {
- const res = await getAccountVideos(server.url, server.accessToken, userAccountName, 0, 5)
+ const res = await getAccountVideos(server.url, server.accessToken, 'user1@localhost:' + servers[0].port, 0, 5)
expect(res.body.total).to.equal(1)
expect(res.body.data).to.be.an('array')
it('Should not have actor files', async () => {
for (const server of servers) {
- await checkActorFilesWereRemoved(userAccountUUID, server.internalServerNumber)
- await checkActorFilesWereRemoved(userVideoChannelUUID, server.internalServerNumber)
+ await checkActorFilesWereRemoved(userAvatarFilename, server.internalServerNumber)
}
})
import {
addVideoChannel,
deleteVideoChannel,
- flushTests,
getAccountVideoChannelsList,
getMyUserInformation,
getVideoChannel,
getVideoChannelsList,
- killallServers,
ServerInfo,
setAccessTokensToServers,
updateVideoChannel
describe('Test video channels', function () {
let servers: ServerInfo[]
let userInfo: User
- let accountUUID: string
let firstVideoChannelId: number
let secondVideoChannelId: number
let videoUUID: string
{
const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
const user: User = res.body
- accountUUID = user.account.uuid
firstVideoChannelId = user.videoChannels[0].id
}
createUser,
doubleFollow,
flushAndRunMultipleServers,
- flushTests,
- getJSONfeed, getMyUserInformation,
+ getJSONfeed,
+ getMyUserInformation,
getXMLfeed,
- killallServers,
ServerInfo,
setAccessTokensToServers,
- uploadVideo, userLogin
+ uploadVideo,
+ userLogin
} from '../../../shared/extra-utils'
import * as libxmljs from 'libxmljs'
import { addVideoCommentThread } from '../../../shared/extra-utils/videos/video-comments'
describe('Test syndication feeds', () => {
let servers: ServerInfo[] = []
let userAccessToken: string
- let rootAccountUUID: string
- let rootChannelUUID: string
- let userAccountUUID: string
- let userChannelUUID: string
+ let rootAccountId: number
+ let rootChannelId: number
+ let userAccountId: number
+ let userChannelId: number
before(async function () {
this.timeout(120000)
{
const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
const user: User = res.body
- rootAccountUUID = user.account.uuid
- rootChannelUUID = user.videoChannels[0].uuid
+ rootAccountId = user.account.id
+ rootChannelId = user.videoChannels[0].id
}
{
const res = await getMyUserInformation(servers[0].url, userAccessToken)
const user: User = res.body
- userAccountUUID = user.account.uuid
- userChannelUUID = user.videoChannels[0].uuid
+ userAccountId = user.account.id
+ userChannelId = user.videoChannels[0].id
}
{
})
it('Should filter by account', async function () {
+ {
+ const json = await getJSONfeed(servers[0].url, 'videos', { accountId: rootAccountId })
+ const jsonObj = JSON.parse(json.text)
+ expect(jsonObj.items.length).to.be.equal(1)
+ expect(jsonObj.items[ 0 ].title).to.equal('my super name for server 1')
+ expect(jsonObj.items[ 0 ].author.name).to.equal('root')
+ }
+
+ {
+ const json = await getJSONfeed(servers[0].url, 'videos', { accountId: userAccountId })
+ const jsonObj = JSON.parse(json.text)
+ expect(jsonObj.items.length).to.be.equal(1)
+ expect(jsonObj.items[ 0 ].title).to.equal('user video')
+ expect(jsonObj.items[ 0 ].author.name).to.equal('john')
+ }
+
for (const server of servers) {
{
- const json = await getJSONfeed(server.url, 'videos', { accountId: rootAccountUUID })
+ const json = await getJSONfeed(server.url, 'videos', { accountName: 'root@localhost:' + servers[0].port })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(1)
expect(jsonObj.items[ 0 ].title).to.equal('my super name for server 1')
- expect(jsonObj.items[ 0 ].author.name).to.equal('root')
}
{
- const json = await getJSONfeed(server.url, 'videos', { accountId: userAccountUUID })
+ const json = await getJSONfeed(server.url, 'videos', { accountName: 'john@localhost:' + servers[0].port })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(1)
expect(jsonObj.items[ 0 ].title).to.equal('user video')
- expect(jsonObj.items[ 0 ].author.name).to.equal('john')
}
}
+ })
+ it('Should filter by video channel', async function () {
{
- const json = await getJSONfeed(servers[0].url, 'videos', { accountName: 'root' })
+ const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelId: rootChannelId })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(1)
expect(jsonObj.items[ 0 ].title).to.equal('my super name for server 1')
+ expect(jsonObj.items[ 0 ].author.name).to.equal('root')
}
{
- const json = await getJSONfeed(servers[0].url, 'videos', { accountName: 'john' })
+ const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelId: userChannelId })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(1)
expect(jsonObj.items[ 0 ].title).to.equal('user video')
+ expect(jsonObj.items[ 0 ].author.name).to.equal('john')
}
- })
- it('Should filter by video channel', async function () {
for (const server of servers) {
{
- const json = await getJSONfeed(server.url, 'videos', { videoChannelId: rootChannelUUID })
+ const json = await getJSONfeed(server.url, 'videos', { videoChannelName: 'root_channel@localhost:' + servers[0].port })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(1)
expect(jsonObj.items[ 0 ].title).to.equal('my super name for server 1')
- expect(jsonObj.items[ 0 ].author.name).to.equal('root')
}
{
- const json = await getJSONfeed(server.url, 'videos', { videoChannelId: userChannelUUID })
+ const json = await getJSONfeed(server.url, 'videos', { videoChannelName: 'john_channel@localhost:' + servers[0].port })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(1)
expect(jsonObj.items[ 0 ].title).to.equal('user video')
- expect(jsonObj.items[ 0 ].author.name).to.equal('john')
}
}
-
- {
- const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelName: 'root_channel' })
- const jsonObj = JSON.parse(json.text)
- expect(jsonObj.items.length).to.be.equal(1)
- expect(jsonObj.items[ 0 ].title).to.equal('my super name for server 1')
- }
-
- {
- const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelName: 'john_channel' })
- const jsonObj = JSON.parse(json.text)
- expect(jsonObj.items.length).to.be.equal(1)
- expect(jsonObj.items[ 0 ].title).to.equal('user video')
- }
})
})
expect(account.followingCount).to.equal(followingCount, message)
}
-async function checkActorFilesWereRemoved (actorUUID: string, serverNumber: number) {
+async function checkActorFilesWereRemoved (filename: string, serverNumber: number) {
const testDirectory = 'test' + serverNumber
for (const directory of [ 'avatars' ]) {
const files = await readdir(directoryPath)
for (const file of files) {
- expect(file).to.not.contain(actorUUID)
+ expect(file).to.not.contain(filename)
}
}
}
expect(video.nsfw).to.equal(attributes.nsfw)
expect(video.description).to.equal(attributes.description)
expect(video.account.id).to.be.a('number')
- expect(video.account.uuid).to.be.a('string')
expect(video.account.host).to.equal(attributes.account.host)
expect(video.account.name).to.equal(attributes.account.name)
expect(video.channel.displayName).to.equal(attributes.channel.displayName)
attributedTo: ActivityPubAttributedTo[]
support?: string
- uuid: string
publicKey: {
id: string
owner: string
export interface AccountSummary {
id: number
- uuid: string
name: string
displayName: string
url: string
export interface Actor {
id: number
- uuid: string
url: string
name: string
host: string
export interface VideoChannelSummary {
id: number
- uuid: string
name: string
displayName: string
url: string