From 8a19bee1a1ee39f973bb37429e4f73c3f2873cdb Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 17 Aug 2018 15:45:42 +0200 Subject: Add ability to set a name to a channel --- .../account-video-channels.component.html | 2 +- .../account-video-channels.component.ts | 2 +- .../my-account-video-channel-create.component.ts | 6 +++ .../my-account-video-channel-edit.component.html | 16 ++++++ .../my-account-video-channel-edit.component.scss | 9 ++++ .../my-account-video-channels.component.html | 9 ++-- .../my-account-video-channels.component.scss | 3 ++ .../+video-channels/video-channels.component.html | 1 + .../video-channel-validators.service.ts | 16 ++++++ .../shared/video-channel/video-channel.model.ts | 2 + .../shared/video-channel/video-channel.service.ts | 4 +- client/src/app/shared/video/video.service.ts | 2 +- client/src/app/signup/signup.component.html | 15 ++++-- client/src/app/signup/signup.component.scss | 9 ++++ client/src/app/signup/signup.component.ts | 4 ++ .../videos/+video-watch/video-watch.component.html | 2 +- client/src/sass/include/_mixins.scss | 11 +++- server/controllers/activitypub/client.ts | 16 +++--- server/controllers/api/accounts.ts | 2 +- server/controllers/api/video-channel.ts | 19 ++++--- server/helpers/custom-validators/accounts.ts | 2 +- .../helpers/custom-validators/activitypub/actor.ts | 2 +- server/helpers/custom-validators/video-channels.ts | 22 +++++--- server/lib/activitypub/url.ts | 4 +- server/lib/user.ts | 11 +++- server/lib/video-channel.ts | 5 +- server/middlewares/validators/feeds.ts | 11 ++-- server/middlewares/validators/video-channels.ts | 23 ++++---- server/models/account/account.ts | 2 +- server/models/activitypub/actor.ts | 5 +- server/models/video/video-channel.ts | 56 +++++++++++++++----- .../tests/api/check-params/user-subscriptions.ts | 19 +++---- server/tests/api/check-params/video-channels.ts | 47 +++++++---------- server/tests/api/server/follows.ts | 2 +- server/tests/api/server/handle-down.ts | 2 +- server/tests/api/users/user-subscriptions.ts | 37 ++++++------- server/tests/api/users/users-multiple-servers.ts | 9 ++-- server/tests/api/videos/multiple-servers.ts | 12 ++--- server/tests/api/videos/single-server.ts | 4 +- server/tests/api/videos/video-channels.ts | 61 ++++++++++++++++------ server/tests/cli/update-host.ts | 1 + server/tests/real-world/populate-database.ts | 1 + server/tests/utils/videos/video-channels.ts | 4 +- server/tests/utils/videos/videos.ts | 4 +- .../videos/channel/video-channel-create.model.ts | 1 + 45 files changed, 317 insertions(+), 180 deletions(-) diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html index bcd3beaf0..114a9e517 100644 --- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html +++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html @@ -1,6 +1,6 @@
Avatar diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts b/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts index ebc671113..44f5626bb 100644 --- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts +++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts @@ -2,10 +2,10 @@ import { Component, OnDestroy, OnInit } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { Account } from '@app/shared/account/account.model' import { AccountService } from '@app/shared/account/account.service' -import { VideoChannel } from '../../../../../shared/models/videos' import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' import { flatMap, map, tap } from 'rxjs/operators' import { Subscription } from 'rxjs' +import { VideoChannel } from '@app/shared/video-channel/video-channel.model' @Component({ selector: 'my-account-video-channels', diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts index c0eaa4763..79ac07c93 100644 --- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts +++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts @@ -29,8 +29,13 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE super() } + get instanceHost () { + return window.location.host + } + ngOnInit () { this.buildForm({ + name: this.videoChannelValidatorsService.VIDEO_CHANNEL_NAME, 'display-name': this.videoChannelValidatorsService.VIDEO_CHANNEL_DISPLAY_NAME, description: this.videoChannelValidatorsService.VIDEO_CHANNEL_DESCRIPTION, support: this.videoChannelValidatorsService.VIDEO_CHANNEL_SUPPORT @@ -42,6 +47,7 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE const body = this.form.value const videoChannelCreate: VideoChannelCreate = { + name: body.name, displayName: body['display-name'], description: body.description || null, support: body.support || null diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html index f7ca2ec43..81fb11f45 100644 --- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html +++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html @@ -8,6 +8,22 @@
{{ error }}
+
+ +
+ +
+ @{{ instanceHost }} +
+
+
+ {{ formErrors['name'] }} +
+
+
- + Avatar
- +
{{ videoChannel.displayName }}
- - +
{{ videoChannel.name }}
{{ videoChannel.followersCount }} subscribers
@@ -24,7 +23,7 @@
- +
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss index f047bb411..f8fd2684e 100644 --- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss +++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss @@ -30,7 +30,9 @@ a.video-channel-names { @include disable-default-a-behaviour; + width: fit-content; display: flex; + align-items: baseline; color: #000; .video-channel-display-name { @@ -41,6 +43,7 @@ .video-channel-name { font-size: 14px; color: #777272; + margin-left: 5px; } } } diff --git a/client/src/app/+video-channels/video-channels.component.html b/client/src/app/+video-channels/video-channels.component.html index a52894cac..5a69a82a0 100644 --- a/client/src/app/+video-channels/video-channels.component.html +++ b/client/src/app/+video-channels/video-channels.component.html @@ -7,6 +7,7 @@
{{ videoChannel.displayName }}
+
{{ videoChannel.nameWithHost }}
{{ videoChannel.followersCount }} subscribers
diff --git a/client/src/app/shared/forms/form-validators/video-channel-validators.service.ts b/client/src/app/shared/forms/form-validators/video-channel-validators.service.ts index 28b063f89..1ce3a0dca 100644 --- a/client/src/app/shared/forms/form-validators/video-channel-validators.service.ts +++ b/client/src/app/shared/forms/form-validators/video-channel-validators.service.ts @@ -5,11 +5,27 @@ import { BuildFormValidator } from '@app/shared' @Injectable() export class VideoChannelValidatorsService { + readonly VIDEO_CHANNEL_NAME: BuildFormValidator readonly VIDEO_CHANNEL_DISPLAY_NAME: BuildFormValidator readonly VIDEO_CHANNEL_DESCRIPTION: BuildFormValidator readonly VIDEO_CHANNEL_SUPPORT: BuildFormValidator constructor (private i18n: I18n) { + this.VIDEO_CHANNEL_NAME = { + VALIDATORS: [ + Validators.required, + Validators.minLength(3), + Validators.maxLength(20), + Validators.pattern(/^[a-z0-9._]+$/) + ], + MESSAGES: { + 'required': this.i18n('Name is required.'), + 'minlength': this.i18n('Name must be at least 3 characters long.'), + 'maxlength': this.i18n('Name cannot be more than 20 characters long.'), + 'pattern': this.i18n('Name should be only lowercase alphanumeric characters.') + } + } + this.VIDEO_CHANNEL_DISPLAY_NAME = { VALIDATORS: [ Validators.required, diff --git a/client/src/app/shared/video-channel/video-channel.model.ts b/client/src/app/shared/video-channel/video-channel.model.ts index b6862b681..309b614ae 100644 --- a/client/src/app/shared/video-channel/video-channel.model.ts +++ b/client/src/app/shared/video-channel/video-channel.model.ts @@ -7,6 +7,7 @@ export class VideoChannel extends Actor implements ServerVideoChannel { description: string support: string isLocal: boolean + nameWithHost: string ownerAccount?: Account ownerBy?: string ownerAvatarUrl?: string @@ -18,6 +19,7 @@ export class VideoChannel extends Actor implements ServerVideoChannel { this.description = hash.description this.support = hash.support this.isLocal = hash.isLocal + this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host) if (hash.ownerAccount) { this.ownerAccount = hash.ownerAccount diff --git a/client/src/app/shared/video-channel/video-channel.service.ts b/client/src/app/shared/video-channel/video-channel.service.ts index 8c000665f..510dc9c3d 100644 --- a/client/src/app/shared/video-channel/video-channel.service.ts +++ b/client/src/app/shared/video-channel/video-channel.service.ts @@ -22,8 +22,8 @@ export class VideoChannelService { private restExtractor: RestExtractor ) {} - getVideoChannel (videoChannelUUID: string) { - return this.authHttp.get(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelUUID) + getVideoChannel (videoChannelName: string) { + return this.authHttp.get(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelName) .pipe( map(videoChannelHash => new VideoChannel(videoChannelHash)), tap(videoChannel => this.videoChannelLoaded.next(videoChannel)), diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts index e2a62c701..e44f1ee65 100644 --- a/client/src/app/shared/video/video.service.ts +++ b/client/src/app/shared/video/video.service.ts @@ -150,7 +150,7 @@ export class VideoService { params = this.restService.addRestGetParams(params, pagination, sort) return this.authHttp - .get>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.uuid + '/videos', { params }) + .get>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.name + '/videos', { params }) .pipe( switchMap(res => this.extractVideos(res)), catchError(err => this.restExtractor.handleError(err)) diff --git a/client/src/app/signup/signup.component.html b/client/src/app/signup/signup.component.html index 565b695d9..5fd630b09 100644 --- a/client/src/app/signup/signup.component.html +++ b/client/src/app/signup/signup.component.html @@ -23,10 +23,17 @@
- + +
+ +
+ @{{ instanceHost }} +
+
+
{{ formErrors.username }}
diff --git a/client/src/app/signup/signup.component.scss b/client/src/app/signup/signup.component.scss index e6d484297..1c992faf5 100644 --- a/client/src/app/signup/signup.component.scss +++ b/client/src/app/signup/signup.component.scss @@ -14,9 +14,18 @@ margin: 30px 0; } +.input-group { + @include peertube-input-group(340px); +} + input:not([type=submit]) { @include peertube-input-text(340px); display: block; + + &#username { + width: auto; + flex-grow: 1; + } } input[type=submit] { diff --git a/client/src/app/signup/signup.component.ts b/client/src/app/signup/signup.component.ts index 076dac454..ed68487ae 100644 --- a/client/src/app/signup/signup.component.ts +++ b/client/src/app/signup/signup.component.ts @@ -34,6 +34,10 @@ export class SignupComponent extends FormReactive implements OnInit { return this.serverService.getConfig().user.videoQuota } + get instanceHost () { + return window.location.host + } + ngOnInit () { this.buildForm({ username: this.userValidatorsService.USER_USERNAME, diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html index 8d4a4a5ca..c275258ef 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html @@ -37,7 +37,7 @@
- + {{ video.channel.displayName }} Video channel avatar diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss index 3d518394a..b0b0f544c 100644 --- a/client/src/sass/include/_mixins.scss +++ b/client/src/sass/include/_mixins.scss @@ -36,9 +36,16 @@ border-radius: 3px; padding-left: 15px; padding-right: 15px; +} - &::placeholder { - color: #585858; +@mixin peertube-input-group($width) { + width: $width; + height: $button-height; + padding-top: 0; + padding-bottom: 0; + + .input-group-text{ + font-size: 14px; } } diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index c90c3f931..54cf44419 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts @@ -6,8 +6,8 @@ import { CONFIG, ROUTE_CACHE_LIFETIME } from '../../initializers' import { buildVideoAnnounce } from '../../lib/activitypub/send' import { audiencify, getAudience } from '../../lib/activitypub/audience' import { createActivityData } from '../../lib/activitypub/send/send-create' -import { asyncMiddleware, executeIfActivityPub, localAccountValidator } from '../../middlewares' -import { videoChannelsGetValidator, videosGetValidator, videosShareValidator } from '../../middlewares/validators' +import { asyncMiddleware, executeIfActivityPub, localAccountValidator, localVideoChannelValidator } from '../../middlewares' +import { videosGetValidator, videosShareValidator } from '../../middlewares/validators' import { videoCommentGetValidator } from '../../middlewares/validators/video-comments' import { AccountModel } from '../../models/account/account' import { ActorModel } from '../../models/activitypub/actor' @@ -80,16 +80,16 @@ activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/activity executeIfActivityPub(asyncMiddleware(videoCommentController)) ) -activityPubClientRouter.get('/video-channels/:id', - executeIfActivityPub(asyncMiddleware(videoChannelsGetValidator)), +activityPubClientRouter.get('/video-channels/:name', + executeIfActivityPub(asyncMiddleware(localVideoChannelValidator)), executeIfActivityPub(asyncMiddleware(videoChannelController)) ) -activityPubClientRouter.get('/video-channels/:id/followers', - executeIfActivityPub(asyncMiddleware(videoChannelsGetValidator)), +activityPubClientRouter.get('/video-channels/:name/followers', + executeIfActivityPub(asyncMiddleware(localVideoChannelValidator)), executeIfActivityPub(asyncMiddleware(videoChannelFollowersController)) ) -activityPubClientRouter.get('/video-channels/:id/following', - executeIfActivityPub(asyncMiddleware(videoChannelsGetValidator)), +activityPubClientRouter.get('/video-channels/:name/following', + executeIfActivityPub(asyncMiddleware(localVideoChannelValidator)), executeIfActivityPub(asyncMiddleware(videoChannelFollowingController)) ) diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts index 308970abc..7b7e5e740 100644 --- a/server/controllers/api/accounts.ts +++ b/server/controllers/api/accounts.ts @@ -78,7 +78,7 @@ async function listAccountVideos (req: express.Request, res: express.Response, n start: req.query.start, count: req.query.count, sort: req.query.sort, - includeLocalVideos: false, + includeLocalVideos: true, categoryOneOf: req.query.categoryOneOf, licenceOneOf: req.query.licenceOneOf, languageOneOf: req.query.languageOneOf, diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index 6ffc09f87..3f51f03f4 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts @@ -10,13 +10,12 @@ import { setDefaultPagination, setDefaultSort, videoChannelsAddValidator, - videoChannelsGetValidator, videoChannelsRemoveValidator, videoChannelsSortValidator, videoChannelsUpdateValidator } from '../../middlewares' import { VideoChannelModel } from '../../models/video/video-channel' -import { videosSortValidator } from '../../middlewares/validators' +import { videoChannelsNameWithHostValidator, videosSortValidator } from '../../middlewares/validators' import { sendUpdateActor } from '../../lib/activitypub/send' import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' import { createVideoChannel } from '../../lib/video-channel' @@ -50,7 +49,7 @@ videoChannelRouter.post('/', asyncRetryTransactionMiddleware(addVideoChannel) ) -videoChannelRouter.post('/:id/avatar/pick', +videoChannelRouter.post('/:nameWithHost/avatar/pick', authenticate, reqAvatarFile, // Check the rights @@ -59,25 +58,25 @@ videoChannelRouter.post('/:id/avatar/pick', asyncMiddleware(updateVideoChannelAvatar) ) -videoChannelRouter.put('/:id', +videoChannelRouter.put('/:nameWithHost', authenticate, asyncMiddleware(videoChannelsUpdateValidator), asyncRetryTransactionMiddleware(updateVideoChannel) ) -videoChannelRouter.delete('/:id', +videoChannelRouter.delete('/:nameWithHost', authenticate, asyncMiddleware(videoChannelsRemoveValidator), asyncRetryTransactionMiddleware(removeVideoChannel) ) -videoChannelRouter.get('/:id', - asyncMiddleware(videoChannelsGetValidator), +videoChannelRouter.get('/:nameWithHost', + asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(getVideoChannel) ) -videoChannelRouter.get('/:id/videos', - asyncMiddleware(videoChannelsGetValidator), +videoChannelRouter.get('/:nameWithHost/videos', + asyncMiddleware(videoChannelsNameWithHostValidator), paginationValidator, videosSortValidator, setDefaultSort, @@ -215,7 +214,7 @@ async function listVideoChannelVideos (req: express.Request, res: express.Respon start: req.query.start, count: req.query.count, sort: req.query.sort, - includeLocalVideos: false, + includeLocalVideos: true, categoryOneOf: req.query.categoryOneOf, licenceOneOf: req.query.licenceOneOf, languageOneOf: req.query.languageOneOf, diff --git a/server/helpers/custom-validators/accounts.ts b/server/helpers/custom-validators/accounts.ts index 0607d661c..191de1496 100644 --- a/server/helpers/custom-validators/accounts.ts +++ b/server/helpers/custom-validators/accounts.ts @@ -42,7 +42,7 @@ function isAccountNameWithHostExist (nameWithDomain: string, res: Response, send let promise: Bluebird if (!host || host === CONFIG.WEBSERVER.HOST) promise = AccountModel.loadLocalByName(accountName) - else promise = AccountModel.loadLocalByNameAndHost(accountName, host) + else promise = AccountModel.loadByNameAndHost(accountName, host) return isAccountExist(promise, res, sendNotFound) } diff --git a/server/helpers/custom-validators/activitypub/actor.ts b/server/helpers/custom-validators/activitypub/actor.ts index ae5014f8f..c3a62c12d 100644 --- a/server/helpers/custom-validators/activitypub/actor.ts +++ b/server/helpers/custom-validators/activitypub/actor.ts @@ -27,7 +27,7 @@ function isActorPublicKeyValid (publicKey: string) { validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACTORS.PUBLIC_KEY) } -const actorNameRegExp = new RegExp('[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_]+') +const actorNameRegExp = new RegExp('^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\-_]+$') function isActorPreferredUsernameValid (preferredUsername: string) { return exists(preferredUsername) && validator.matches(preferredUsername, actorNameRegExp) } diff --git a/server/helpers/custom-validators/video-channels.ts b/server/helpers/custom-validators/video-channels.ts index 32faf36f7..f13519c1d 100644 --- a/server/helpers/custom-validators/video-channels.ts +++ b/server/helpers/custom-validators/video-channels.ts @@ -2,10 +2,9 @@ import * as express from 'express' import 'express-validator' import 'multer' import * as validator from 'validator' -import { CONSTRAINTS_FIELDS } from '../../initializers' +import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers' import { VideoChannelModel } from '../../models/video/video-channel' import { exists } from './misc' -import { Response } from 'express' const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS @@ -21,13 +20,13 @@ function isVideoChannelSupportValid (value: string) { return value === null || (exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.SUPPORT)) } -async function isLocalVideoChannelNameExist (name: string, res: Response) { - const videoChannel = await VideoChannelModel.loadLocalByName(name) +async function isLocalVideoChannelNameExist (name: string, res: express.Response) { + const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name) return processVideoChannelExist(videoChannel, res) } -async function isVideoChannelExist (id: string, res: express.Response) { +async function isVideoChannelIdExist (id: string, res: express.Response) { let videoChannel: VideoChannelModel if (validator.isInt(id)) { videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id) @@ -38,14 +37,25 @@ async function isVideoChannelExist (id: string, res: express.Response) { return processVideoChannelExist(videoChannel, res) } +async function isVideoChannelNameWithHostExist (nameWithDomain: string, res: express.Response) { + const [ name, host ] = nameWithDomain.split('@') + let videoChannel: VideoChannelModel + + if (!host || host === CONFIG.WEBSERVER.HOST) videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name) + else videoChannel = await VideoChannelModel.loadByNameAndHostAndPopulateAccount(name, host) + + return processVideoChannelExist(videoChannel, res) +} + // --------------------------------------------------------------------------- export { + isVideoChannelNameWithHostExist, isLocalVideoChannelNameExist, isVideoChannelDescriptionValid, isVideoChannelNameValid, isVideoChannelSupportValid, - isVideoChannelExist + isVideoChannelIdExist } function processVideoChannelExist (videoChannel: VideoChannelModel, res: express.Response) { diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts index ba3bf688d..262463310 100644 --- a/server/lib/activitypub/url.ts +++ b/server/lib/activitypub/url.ts @@ -13,8 +13,8 @@ function getVideoCommentActivityPubUrl (video: VideoModel, videoComment: VideoCo return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id } -function getVideoChannelActivityPubUrl (videoChannelUUID: string) { - return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannelUUID +function getVideoChannelActivityPubUrl (videoChannelName: string) { + return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannelName } function getAccountActivityPubUrl (accountName: string) { diff --git a/server/lib/user.ts b/server/lib/user.ts index e7a45f5aa..db29469eb 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts @@ -1,4 +1,5 @@ import * as Sequelize from 'sequelize' +import * as uuidv4 from 'uuid/v4' import { ActivityPubActorType } from '../../shared/models/activitypub' import { sequelizeTypescript, SERVER_ACTOR_NAME } from '../initializers' import { AccountModel } from '../models/account/account' @@ -7,6 +8,7 @@ import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from import { createVideoChannel } from './video-channel' import { VideoChannelModel } from '../models/video/video-channel' import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model' +import { ActorModel } from '../models/activitypub/actor' async function createUserAccountAndChannel (userToCreate: UserModel, validateUser = true) { const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { @@ -19,8 +21,15 @@ async function createUserAccountAndChannel (userToCreate: UserModel, validateUse const accountCreated = await createLocalAccountWithoutKeys(userToCreate.username, userToCreate.id, null, t) userCreated.Account = accountCreated - const videoChannelDisplayName = `Default ${userCreated.username} channel` + let channelName = userCreated.username + '_channel' + + // Conflict, generate uuid instead + const actor = await ActorModel.loadLocalByName(channelName) + if (actor) channelName = uuidv4() + + const videoChannelDisplayName = `Main ${userCreated.username} channel` const videoChannelInfo = { + name: channelName, displayName: videoChannelDisplayName } const videoChannel = await createVideoChannel(videoChannelInfo, accountCreated, t) diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts index 600316cda..0fe95ca09 100644 --- a/server/lib/video-channel.ts +++ b/server/lib/video-channel.ts @@ -7,9 +7,8 @@ import { buildActorInstance, getVideoChannelActivityPubUrl } from './activitypub async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) { const uuid = uuidv4() - const url = getVideoChannelActivityPubUrl(uuid) - // We use the name as uuid - const actorInstance = buildActorInstance('Group', url, uuid, uuid) + const url = getVideoChannelActivityPubUrl(videoChannelInfo.name) + const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid) const actorInstanceCreated = await actorInstance.save({ transaction: t }) diff --git a/server/middlewares/validators/feeds.ts b/server/middlewares/validators/feeds.ts index 3c8532bd9..c1054ad9b 100644 --- a/server/middlewares/validators/feeds.ts +++ b/server/middlewares/validators/feeds.ts @@ -1,13 +1,13 @@ import * as express from 'express' import { param, query } from 'express-validator/check' -import { isAccountIdExist, isAccountNameValid } from '../../helpers/custom-validators/accounts' -import { join } from 'path' +import { isAccountIdExist, isAccountNameValid, isAccountNameWithHostExist } from '../../helpers/custom-validators/accounts' import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' import { logger } from '../../helpers/logger' import { areValidationErrors } from './utils' import { isValidRSSFeed } from '../../helpers/custom-validators/feeds' -import { isVideoChannelExist } from '../../helpers/custom-validators/video-channels' +import { isVideoChannelIdExist, isVideoChannelNameWithHostExist } from '../../helpers/custom-validators/video-channels' import { isVideoExist } 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)'), @@ -15,6 +15,7 @@ const videoFeedsValidator = [ query('accountId').optional().custom(isIdOrUUIDValid), query('accountName').optional().custom(isAccountNameValid), query('videoChannelId').optional().custom(isIdOrUUIDValid), + query('videoChannelName').optional().custom(isActorPreferredUsernameValid), async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking feeds parameters', { parameters: req.query }) @@ -22,7 +23,9 @@ const videoFeedsValidator = [ if (areValidationErrors(req, res)) return if (req.query.accountId && !await isAccountIdExist(req.query.accountId, res)) return - if (req.query.videoChannelId && !await isVideoChannelExist(req.query.videoChannelId, res)) return + if (req.query.videoChannelName && !await isVideoChannelIdExist(req.query.videoChannelName, res)) return + if (req.query.accountName && !await isAccountNameWithHostExist(req.query.accountName, res)) return + if (req.query.videoChannelName && !await isVideoChannelNameWithHostExist(req.query.videoChannelName, res)) return return next() } diff --git a/server/middlewares/validators/video-channels.ts b/server/middlewares/validators/video-channels.ts index d354c7e05..79587b028 100644 --- a/server/middlewares/validators/video-channels.ts +++ b/server/middlewares/validators/video-channels.ts @@ -2,18 +2,18 @@ import * as express from 'express' import { body, param } from 'express-validator/check' import { UserRight } from '../../../shared' import { isAccountNameWithHostExist } from '../../helpers/custom-validators/accounts' -import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' import { isLocalVideoChannelNameExist, isVideoChannelDescriptionValid, - isVideoChannelExist, isVideoChannelNameValid, + isVideoChannelNameWithHostExist, isVideoChannelSupportValid } from '../../helpers/custom-validators/video-channels' import { logger } from '../../helpers/logger' import { UserModel } from '../../models/account/user' import { VideoChannelModel } from '../../models/video/video-channel' import { areValidationErrors } from './utils' +import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor' const listVideoAccountChannelsValidator = [ param('accountName').exists().withMessage('Should have a valid account name'), @@ -29,6 +29,7 @@ const listVideoAccountChannelsValidator = [ ] const videoChannelsAddValidator = [ + body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), body('displayName').custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), @@ -43,7 +44,7 @@ const videoChannelsAddValidator = [ ] const videoChannelsUpdateValidator = [ - param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), + param('nameWithHost').exists().withMessage('Should have an video channel name with host'), body('displayName').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), @@ -52,7 +53,7 @@ const videoChannelsUpdateValidator = [ logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body }) if (areValidationErrors(req, res)) return - if (!await isVideoChannelExist(req.params.id, res)) return + if (!await isVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return // We need to make additional checks if (res.locals.videoChannel.Actor.isOwned() === false) { @@ -72,13 +73,13 @@ const videoChannelsUpdateValidator = [ ] const videoChannelsRemoveValidator = [ - param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), + param('nameWithHost').exists().withMessage('Should have an video channel name with host'), async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params }) if (areValidationErrors(req, res)) return - if (!await isVideoChannelExist(req.params.id, res)) return + if (!await isVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return if (!checkUserCanDeleteVideoChannel(res.locals.oauth.token.User, res.locals.videoChannel, res)) return if (!await checkVideoChannelIsNotTheLastOne(res)) return @@ -87,15 +88,15 @@ const videoChannelsRemoveValidator = [ } ] -const videoChannelsGetValidator = [ - param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), +const videoChannelsNameWithHostValidator = [ + param('nameWithHost').exists().withMessage('Should have an video channel name with host'), async (req: express.Request, res: express.Response, next: express.NextFunction) => { - logger.debug('Checking videoChannelsGet parameters', { parameters: req.params }) + logger.debug('Checking videoChannelsNameWithHostValidator parameters', { parameters: req.params }) if (areValidationErrors(req, res)) return - if (!await isVideoChannelExist(req.params.id, res)) return + if (!await isVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return return next() } @@ -121,7 +122,7 @@ export { videoChannelsAddValidator, videoChannelsUpdateValidator, videoChannelsRemoveValidator, - videoChannelsGetValidator, + videoChannelsNameWithHostValidator, localVideoChannelValidator } diff --git a/server/models/account/account.ts b/server/models/account/account.ts index 66f5dcf2e..07539a04e 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts @@ -194,7 +194,7 @@ export class AccountModel extends Model { return AccountModel.findOne(query) } - static loadLocalByNameAndHost (name: string, host: string) { + static loadByNameAndHost (name: string, host: string) { const query = { include: [ { diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts index 35d7c35e8..2abf40713 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/activitypub/actor.ts @@ -260,12 +260,13 @@ export class ActorModel extends Model { return ActorModel.scope(ScopeNames.FULL).findAll(query) } - static loadLocalByName (preferredUsername: string) { + static loadLocalByName (preferredUsername: string, transaction?: Sequelize.Transaction) { const query = { where: { preferredUsername, serverId: null - } + }, + transaction } return ActorModel.scope(ScopeNames.FULL).findOne(query) diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 0273fab13..9f80e0b8d 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts @@ -29,6 +29,7 @@ import { getSort, throwIfNotValid } from '../utils' import { VideoModel } from './video' import { CONSTRAINTS_FIELDS } from '../../initializers' import { AvatarModel } from '../avatar/avatar' +import { ServerModel } from '../server/server' enum ScopeNames { WITH_ACCOUNT = 'WITH_ACCOUNT', @@ -206,7 +207,7 @@ export class VideoChannelModel extends Model { } static loadByIdAndAccount (id: number, accountId: number) { - const options = { + const query = { where: { id, accountId @@ -215,7 +216,7 @@ export class VideoChannelModel extends Model { return VideoChannelModel .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) - .findOne(options) + .findOne(query) } static loadAndPopulateAccount (id: number) { @@ -225,7 +226,7 @@ export class VideoChannelModel extends Model { } static loadByUUIDAndPopulateAccount (uuid: string) { - const options = { + const query = { include: [ { model: ActorModel, @@ -239,36 +240,63 @@ export class VideoChannelModel extends Model { return VideoChannelModel .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) - .findOne(options) + .findOne(query) } - static loadAndPopulateAccountAndVideos (id: number) { - const options = { + static loadLocalByNameAndPopulateAccount (name: string) { + const query = { include: [ - VideoModel + { + model: ActorModel, + required: true, + where: { + preferredUsername: name, + serverId: null + } + } ] } return VideoChannelModel - .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEOS ]) - .findById(id, options) + .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) + .findOne(query) } - static loadLocalByName (name: string) { + static loadByNameAndHostAndPopulateAccount (name: string, host: string) { const query = { include: [ { model: ActorModel, required: true, where: { - preferredUsername: name, - serverId: null - } + preferredUsername: name + }, + include: [ + { + model: ServerModel, + required: true, + where: { host } + } + ] } ] } - return VideoChannelModel.findOne(query) + return VideoChannelModel + .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) + .findOne(query) + } + + static loadAndPopulateAccountAndVideos (id: number) { + const options = { + include: [ + VideoModel + ] + } + + return VideoChannelModel + .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEOS ]) + .findById(id, options) } toFormattedJSON (): VideoChannel { diff --git a/server/tests/api/check-params/user-subscriptions.ts b/server/tests/api/check-params/user-subscriptions.ts index 9f7d15b27..628a74476 100644 --- a/server/tests/api/check-params/user-subscriptions.ts +++ b/server/tests/api/check-params/user-subscriptions.ts @@ -5,7 +5,6 @@ import 'mocha' import { createUser, flushTests, - getMyUserInformation, killallServers, makeDeleteRequest, makeGetRequest, @@ -21,7 +20,6 @@ describe('Test user subscriptions API validators', function () { const path = '/api/v1/users/me/subscriptions' let server: ServerInfo let userAccessToken = '' - let userChannelUUID: string // --------------------------------------------------------------- @@ -40,11 +38,6 @@ describe('Test user subscriptions API validators', function () { } await createUser(server.url, server.accessToken, user.username, user.password) userAccessToken = await userLogin(server, user) - - { - const res = await getMyUserInformation(server.url, server.accessToken) - userChannelUUID = res.body.videoChannels[ 0 ].uuid - } }) describe('When listing my subscriptions', function () { @@ -69,7 +62,7 @@ describe('Test user subscriptions API validators', function () { }) it('Should success with the correct parameters', async function () { - await await makeGetRequest({ + await makeGetRequest({ url: server.url, path, token: userAccessToken, @@ -102,7 +95,7 @@ describe('Test user subscriptions API validators', function () { }) it('Should success with the correct parameters', async function () { - await await makeGetRequest({ + await makeGetRequest({ url: server.url, path, token: userAccessToken, @@ -116,7 +109,7 @@ describe('Test user subscriptions API validators', function () { await makePostBodyRequest({ url: server.url, path, - fields: { uri: userChannelUUID + '@localhost:9001' }, + fields: { uri: 'user1_channel@localhost:9001' }, statusCodeExpected: 401 }) }) @@ -152,7 +145,7 @@ describe('Test user subscriptions API validators', function () { url: server.url, path, token: server.accessToken, - fields: { uri: userChannelUUID + '@localhost:9001' }, + fields: { uri: 'user1_channel@localhost:9001' }, statusCodeExpected: 204 }) }) @@ -162,7 +155,7 @@ describe('Test user subscriptions API validators', function () { it('Should fail with a non authenticated user', async function () { await makeDeleteRequest({ url: server.url, - path: path + '/' + userChannelUUID + '@localhost:9001', + path: path + '/user1_channel@localhost:9001', statusCodeExpected: 401 }) }) @@ -202,7 +195,7 @@ describe('Test user subscriptions API validators', function () { it('Should success with the correct parameters', async function () { await makeDeleteRequest({ url: server.url, - path: path + '/' + userChannelUUID + '@localhost:9001', + path: path + '/user1_channel@localhost:9001', token: server.accessToken, statusCodeExpected: 204 }) diff --git a/server/tests/api/check-params/video-channels.ts b/server/tests/api/check-params/video-channels.ts index 0980de73b..bcf4b7473 100644 --- a/server/tests/api/check-params/video-channels.ts +++ b/server/tests/api/check-params/video-channels.ts @@ -31,7 +31,6 @@ describe('Test video channels API validator', function () { const videoChannelPath = '/api/v1/video-channels' let server: ServerInfo let accessTokenUser: string - let videoChannelUUID: string // --------------------------------------------------------------- @@ -53,12 +52,6 @@ describe('Test video channels API validator', function () { await createUser(server.url, server.accessToken, user.username, user.password) accessTokenUser = await userLogin(server, user) } - - { - const res = await getMyUserInformation(server.url, server.accessToken) - const user: User = res.body - videoChannelUUID = user.videoChannels[0].uuid - } }) describe('When listing a video channels', function () { @@ -83,6 +76,7 @@ describe('Test video channels API validator', function () { describe('When adding a video channel', function () { const baseCorrectParams = { + name: 'super_channel', displayName: 'hello', description: 'super description', support: 'super support text' @@ -103,6 +97,16 @@ describe('Test video channels API validator', function () { await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) }) + it('Should fail without a name', async function () { + const fields = omit(baseCorrectParams, 'name') + await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) + }) + + it('Should fail with a bad name', async function () { + const fields = immutableAssign(baseCorrectParams, { name: 'super name' }) + await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) + }) + it('Should fail without a name', async function () { const fields = omit(baseCorrectParams, 'displayName') await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) @@ -142,7 +146,7 @@ describe('Test video channels API validator', function () { let path: string before(async function () { - path = videoChannelPath + '/' + videoChannelUUID + path = videoChannelPath + '/super_channel' }) it('Should fail with a non authenticated user', async function () { @@ -195,7 +199,7 @@ describe('Test video channels API validator', function () { let path: string before(async function () { - path = videoChannelPath + '/' + videoChannelUUID + path = videoChannelPath + '/super_channel' }) it('Should fail with an incorrect input file', async function () { @@ -255,18 +259,10 @@ describe('Test video channels API validator', function () { expect(res.body.data).to.be.an('array') }) - it('Should fail without a correct uuid', async function () { - await makeGetRequest({ - url: server.url, - path: videoChannelPath + '/coucou', - statusCodeExpected: 400 - }) - }) - it('Should return 404 with an incorrect video channel', async function () { await makeGetRequest({ url: server.url, - path: videoChannelPath + '/4da6fde3-88f7-4d16-b119-108df5630b06', + path: videoChannelPath + '/super_channel2', statusCodeExpected: 404 }) }) @@ -274,7 +270,7 @@ describe('Test video channels API validator', function () { it('Should succeed with the correct parameters', async function () { await makeGetRequest({ url: server.url, - path: videoChannelPath + '/' + videoChannelUUID, + path: videoChannelPath + '/super_channel', statusCodeExpected: 200 }) }) @@ -282,26 +278,23 @@ describe('Test video channels API validator', function () { describe('When deleting a video channel', function () { it('Should fail with a non authenticated user', async function () { - await deleteVideoChannel(server.url, 'coucou', videoChannelUUID, 401) + await deleteVideoChannel(server.url, 'coucou', 'super_channel', 401) }) it('Should fail with another authenticated user', async function () { - await deleteVideoChannel(server.url, accessTokenUser, videoChannelUUID, 403) + await deleteVideoChannel(server.url, accessTokenUser, 'super_channel', 403) }) it('Should fail with an unknown video channel id', async function () { - await deleteVideoChannel(server.url, server.accessToken,454554, 404) + await deleteVideoChannel(server.url, server.accessToken,'super_channel2', 404) }) it('Should succeed with the correct parameters', async function () { - await deleteVideoChannel(server.url, server.accessToken, videoChannelUUID) + await deleteVideoChannel(server.url, server.accessToken, 'super_channel') }) it('Should fail to delete the last user video channel', async function () { - const res = await getVideoChannelsList(server.url, 0, 1) - const lastVideoChannelUUID = res.body.data[0].uuid - - await deleteVideoChannel(server.url, server.accessToken, lastVideoChannelUUID, 409) + await deleteVideoChannel(server.url, server.accessToken, 'root_channel', 409) }) }) diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts index 1cad8998c..243fcd4e7 100644 --- a/server/tests/api/server/follows.ts +++ b/server/tests/api/server/follows.ts @@ -311,7 +311,7 @@ describe('Test follows', function () { likes: 1, dislikes: 1, channel: { - name: 'Default root channel', + name: 'Main root channel', description: '', isLocal }, diff --git a/server/tests/api/server/handle-down.ts b/server/tests/api/server/handle-down.ts index 18a0d9ce3..df35b36eb 100644 --- a/server/tests/api/server/handle-down.ts +++ b/server/tests/api/server/handle-down.ts @@ -71,7 +71,7 @@ describe('Test handle downs', function () { privacy: VideoPrivacy.PUBLIC, commentsEnabled: true, channel: { - name: 'Default root channel', + name: 'Main root channel', description: '', isLocal: false }, diff --git a/server/tests/api/users/user-subscriptions.ts b/server/tests/api/users/user-subscriptions.ts index 2ba6cdfaf..ba59a9a60 100644 --- a/server/tests/api/users/user-subscriptions.ts +++ b/server/tests/api/users/user-subscriptions.ts @@ -3,7 +3,7 @@ import * as chai from 'chai' import 'mocha' import { createUser, doubleFollow, flushAndRunMultipleServers, follow, getVideosList, unfollow, userLogin } from '../../utils' -import { getMyUserInformation, killallServers, ServerInfo, uploadVideo } from '../../utils/index' +import { killallServers, ServerInfo, uploadVideo } from '../../utils/index' import { setAccessTokensToServers } from '../../utils/users/login' import { Video, VideoChannel } from '../../../../shared/models/videos' import { waitJobs } from '../../utils/server/jobs' @@ -18,8 +18,7 @@ const expect = chai.expect describe('Test users subscriptions', function () { let servers: ServerInfo[] = [] - const users: { accessToken: string, videoChannelName: string }[] = [] - let rootChannelNameServer1: string + const users: { accessToken: string }[] = [] before(async function () { this.timeout(120000) @@ -32,19 +31,13 @@ describe('Test users subscriptions', function () { // Server 1 and server 2 follow each other await doubleFollow(servers[0], servers[1]) - const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) - rootChannelNameServer1 = res.body.videoChannels[0].name - { for (const server of servers) { const user = { username: 'user' + server.serverNumber, password: 'password' } await createUser(server.url, server.accessToken, user.username, user.password) const accessToken = await userLogin(server, user) - const res = await getMyUserInformation(server.url, accessToken) - const videoChannels: VideoChannel[] = res.body.videoChannels - - users.push({ accessToken, videoChannelName: videoChannels[0].name }) + users.push({ accessToken }) const videoName1 = 'video 1-' + server.serverNumber await uploadVideo(server.url, accessToken, { name: videoName1 }) @@ -64,10 +57,10 @@ describe('Test users subscriptions', function () { }) it('User of server 1 should follow user of server 3 and root of server 1', async function () { - this.timeout(30000) + this.timeout(60000) - await addUserSubscription(servers[0].url, users[0].accessToken, users[2].videoChannelName + '@localhost:9003') - await addUserSubscription(servers[0].url, users[0].accessToken, rootChannelNameServer1 + '@localhost:9001') + await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:9003') + await addUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:9001') await waitJobs(servers) @@ -103,8 +96,8 @@ describe('Test users subscriptions', function () { expect(subscriptions).to.be.an('array') expect(subscriptions).to.have.lengthOf(2) - expect(subscriptions[0].name).to.equal(users[2].videoChannelName) - expect(subscriptions[1].name).to.equal(rootChannelNameServer1) + expect(subscriptions[0].name).to.equal('user3_channel') + expect(subscriptions[1].name).to.equal('root_channel') } }) @@ -131,7 +124,7 @@ describe('Test users subscriptions', function () { }) it('Should upload a video by root on server 1 and see it in the subscription videos', async function () { - this.timeout(30000) + this.timeout(60000) const videoName = 'video server 1 added after follow' await uploadVideo(servers[0].url, servers[0].accessToken, { name: videoName }) @@ -172,7 +165,7 @@ describe('Test users subscriptions', function () { }) it('Should have server 1 follow server 3 and display server 3 videos', async function () { - this.timeout(30000) + this.timeout(60000) await follow(servers[0].url, [ servers[2].url ], servers[0].accessToken) @@ -190,7 +183,7 @@ describe('Test users subscriptions', function () { }) it('Should remove follow server 1 -> server 3 and hide server 3 videos', async function () { - this.timeout(30000) + this.timeout(60000) await unfollow(servers[0].url, servers[0].accessToken, servers[2]) @@ -230,7 +223,7 @@ describe('Test users subscriptions', function () { }) it('Should remove user of server 3 subscription', async function () { - await removeUserSubscription(servers[0].url, users[0].accessToken, users[2].videoChannelName + '@localhost:9003') + await removeUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:9003') await waitJobs(servers) }) @@ -249,7 +242,7 @@ describe('Test users subscriptions', function () { }) it('Should remove the root subscription and not display the videos anymore', async function () { - await removeUserSubscription(servers[0].url, users[0].accessToken, rootChannelNameServer1 + '@localhost:9001') + await removeUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:9001') await waitJobs(servers) @@ -275,9 +268,9 @@ describe('Test users subscriptions', function () { }) it('Should follow user of server 3 again', async function () { - this.timeout(30000) + this.timeout(60000) - await addUserSubscription(servers[0].url, users[0].accessToken, users[2].videoChannelName + '@localhost:9003') + await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:9003') await waitJobs(servers) diff --git a/server/tests/api/users/users-multiple-servers.ts b/server/tests/api/users/users-multiple-servers.ts index 81489021b..575e04546 100644 --- a/server/tests/api/users/users-multiple-servers.ts +++ b/server/tests/api/users/users-multiple-servers.ts @@ -12,10 +12,9 @@ import { getVideoChannelsList, removeUser, updateMyUser, - userLogin, - wait + userLogin } from '../../utils' -import { flushTests, getMyUserInformation, killallServers, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../utils/index' +import { getMyUserInformation, killallServers, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../utils/index' import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../utils/users/accounts' import { setAccessTokensToServers } from '../../utils/users/login' import { User } from '../../../../shared/models/users' @@ -172,7 +171,7 @@ describe('Test users with multiple servers', function () { const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) const videoChannelDeleted = resVideoChannels.body.data.find(a => { - return a.displayName === 'Default user1 channel' && a.host === 'localhost:9001' + return a.displayName === 'Main user1 channel' && a.host === 'localhost:9001' }) as VideoChannel expect(videoChannelDeleted).not.to.be.undefined } @@ -189,7 +188,7 @@ describe('Test users with multiple servers', function () { const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) const videoChannelDeleted = resVideoChannels.body.data.find(a => { - return a.name === 'Default user1 channel' && a.host === 'localhost:9001' + return a.name === 'Main user1 channel' && a.host === 'localhost:9001' }) as VideoChannel expect(videoChannelDeleted).to.be.undefined } diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts index cab096a12..3c3839338 100644 --- a/server/tests/api/videos/multiple-servers.ts +++ b/server/tests/api/videos/multiple-servers.ts @@ -6,7 +6,6 @@ import { join } from 'path' import * as request from 'supertest' import { VideoPrivacy } from '../../../../shared/models/videos' import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' - import { addVideoChannel, checkVideoFilesWereRemoved, @@ -60,6 +59,7 @@ describe('Test multiple servers', function () { { const videoChannel = { + name: 'super_channel_name', displayName: 'my channel', description: 'super channel' } @@ -201,7 +201,7 @@ describe('Test multiple servers', function () { tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ], privacy: VideoPrivacy.PUBLIC, channel: { - name: 'Default user1 channel', + name: 'Main user1 channel', description: 'super channel', isLocal }, @@ -307,7 +307,7 @@ describe('Test multiple servers', function () { tags: [ 'tag1p3' ], privacy: VideoPrivacy.PUBLIC, channel: { - name: 'Default root channel', + name: 'Main root channel', description: '', isLocal }, @@ -339,7 +339,7 @@ describe('Test multiple servers', function () { tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ], privacy: VideoPrivacy.PUBLIC, channel: { - name: 'Default root channel', + name: 'Main root channel', description: '', isLocal }, @@ -647,7 +647,7 @@ describe('Test multiple servers', function () { tags: [ 'tag_up_1', 'tag_up_2' ], privacy: VideoPrivacy.PUBLIC, channel: { - name: 'Default root channel', + name: 'Main root channel', description: '', isLocal }, @@ -967,7 +967,7 @@ describe('Test multiple servers', function () { tags: [ ], privacy: VideoPrivacy.PUBLIC, channel: { - name: 'Default root channel', + name: 'Main root channel', description: '', isLocal }, diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts index ba4920d1b..12181ad67 100644 --- a/server/tests/api/videos/single-server.ts +++ b/server/tests/api/videos/single-server.ts @@ -56,7 +56,7 @@ describe('Test a single server', function () { privacy: VideoPrivacy.PUBLIC, commentsEnabled: true, channel: { - name: 'Default root channel', + name: 'Main root channel', description: '', isLocal: true }, @@ -87,7 +87,7 @@ describe('Test a single server', function () { duration: 5, commentsEnabled: false, channel: { - name: 'Default root channel', + name: 'Main root channel', description: '', isLocal: true }, diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts index e4e3ce9d9..8138c65d6 100644 --- a/server/tests/api/videos/video-channels.ts +++ b/server/tests/api/videos/video-channels.ts @@ -4,12 +4,13 @@ import * as chai from 'chai' import 'mocha' import { User, Video } from '../../../../shared/index' import { + createUser, doubleFollow, flushAndRunMultipleServers, - getVideoChannelVideos, testImage, + getVideoChannelVideos, serverLogin, testImage, updateVideo, updateVideoChannelAvatar, - uploadVideo, wait + uploadVideo, wait, userLogin } from '../../utils' import { addVideoChannel, @@ -33,9 +34,7 @@ describe('Test video channels', function () { let userInfo: User let accountUUID: string let firstVideoChannelId: number - let firstVideoChannelUUID: string let secondVideoChannelId: number - let secondVideoChannelUUID: string let videoUUID: string before(async function () { @@ -54,7 +53,6 @@ describe('Test video channels', function () { accountUUID = user.account.uuid firstVideoChannelId = user.videoChannels[0].id - firstVideoChannelUUID = user.videoChannels[0].uuid } await waitJobs(servers) @@ -73,13 +71,13 @@ describe('Test video channels', function () { { const videoChannel = { + name: 'second_video_channel', displayName: 'second video channel', description: 'super video channel description', support: 'super video channel support text' } const res = await addVideoChannel(servers[ 0 ].url, servers[ 0 ].accessToken, videoChannel) secondVideoChannelId = res.body.videoChannel.id - secondVideoChannelUUID = res.body.videoChannel.uuid } // The channel is 1 is propagated to servers 2 @@ -99,7 +97,10 @@ describe('Test video channels', function () { expect(userInfo.videoChannels).to.have.lengthOf(2) const videoChannels = userInfo.videoChannels - expect(videoChannels[0].displayName).to.equal('Default root channel') + expect(videoChannels[0].name).to.equal('root_channel') + expect(videoChannels[0].displayName).to.equal('Main root channel') + + expect(videoChannels[1].name).to.equal('second_video_channel') expect(videoChannels[1].displayName).to.equal('second video channel') expect(videoChannels[1].description).to.equal('super video channel description') expect(videoChannels[1].support).to.equal('super video channel support text') @@ -112,7 +113,10 @@ describe('Test video channels', function () { expect(res.body.data).to.have.lengthOf(2) const videoChannels = res.body.data - expect(videoChannels[0].displayName).to.equal('Default root channel') + expect(videoChannels[0].name).to.equal('root_channel') + expect(videoChannels[0].displayName).to.equal('Main root channel') + + expect(videoChannels[1].name).to.equal('second_video_channel') expect(videoChannels[1].displayName).to.equal('second video channel') expect(videoChannels[1].description).to.equal('super video channel description') expect(videoChannels[1].support).to.equal('super video channel support text') @@ -125,6 +129,7 @@ describe('Test video channels', function () { expect(res.body.data).to.have.lengthOf(1) const videoChannels = res.body.data + expect(videoChannels[0].name).to.equal('second_video_channel') expect(videoChannels[0].displayName).to.equal('second video channel') expect(videoChannels[0].description).to.equal('super video channel description') expect(videoChannels[0].support).to.equal('super video channel support text') @@ -136,7 +141,8 @@ describe('Test video channels', function () { expect(res.body.total).to.equal(2) expect(res.body.data).to.be.an('array') expect(res.body.data).to.have.lengthOf(1) - expect(res.body.data[0].displayName).to.equal('Default root channel') + expect(res.body.data[0].name).to.equal('root_channel') + expect(res.body.data[0].displayName).to.equal('Main root channel') }) it('Should update video channel', async function () { @@ -148,7 +154,7 @@ describe('Test video channels', function () { support: 'video channel support text updated' } - await updateVideoChannel(servers[0].url, servers[0].accessToken, secondVideoChannelId, videoChannelAttributes) + await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes) await waitJobs(servers) }) @@ -160,6 +166,7 @@ describe('Test video channels', function () { expect(res.body.total).to.equal(2) expect(res.body.data).to.be.an('array') expect(res.body.data).to.have.lengthOf(1) + expect(res.body.data[0].name).to.equal('second_video_channel') expect(res.body.data[0].displayName).to.equal('video channel updated') expect(res.body.data[0].description).to.equal('video channel description updated') expect(res.body.data[0].support).to.equal('video channel support text updated') @@ -174,7 +181,7 @@ describe('Test video channels', function () { await updateVideoChannelAvatar({ url: servers[0].url, accessToken: servers[0].accessToken, - videoChannelId: secondVideoChannelId, + videoChannelName: 'second_video_channel', fixture }) @@ -192,9 +199,10 @@ describe('Test video channels', function () { }) it('Should get video channel', async function () { - const res = await getVideoChannel(servers[0].url, secondVideoChannelId) + const res = await getVideoChannel(servers[0].url, 'second_video_channel') const videoChannel = res.body + expect(videoChannel.name).to.equal('second_video_channel') expect(videoChannel.displayName).to.equal('video channel updated') expect(videoChannel.description).to.equal('video channel description updated') expect(videoChannel.support).to.equal('video channel support text updated') @@ -204,7 +212,8 @@ describe('Test video channels', function () { this.timeout(10000) for (const server of servers) { - const res1 = await getVideoChannelVideos(server.url, server.accessToken, secondVideoChannelUUID, 0, 5) + const channelURI = 'second_video_channel@localhost:9001' + const res1 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5) expect(res1.body.total).to.equal(1) expect(res1.body.data).to.be.an('array') expect(res1.body.data).to.have.lengthOf(1) @@ -224,10 +233,12 @@ describe('Test video channels', function () { this.timeout(10000) for (const server of servers) { - const res1 = await getVideoChannelVideos(server.url, server.accessToken, secondVideoChannelUUID, 0, 5) + const secondChannelURI = 'second_video_channel@localhost:9001' + const res1 = await getVideoChannelVideos(server.url, server.accessToken, secondChannelURI, 0, 5) expect(res1.body.total).to.equal(0) - const res2 = await getVideoChannelVideos(server.url, server.accessToken, firstVideoChannelUUID, 0, 5) + const channelURI = 'root_channel@localhost:9001' + const res2 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5) expect(res2.body.total).to.equal(1) const videos: Video[] = res2.body.data @@ -238,7 +249,7 @@ describe('Test video channels', function () { }) it('Should delete video channel', async function () { - await deleteVideoChannel(servers[0].url, servers[0].accessToken, secondVideoChannelId) + await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel') }) it('Should have video channel deleted', async function () { @@ -247,7 +258,23 @@ describe('Test video channels', function () { expect(res.body.total).to.equal(1) expect(res.body.data).to.be.an('array') expect(res.body.data).to.have.lengthOf(1) - expect(res.body.data[0].displayName).to.equal('Default root channel') + expect(res.body.data[0].displayName).to.equal('Main root channel') + }) + + it('Should create the main channel with an uuid if there is a conflict', async function () { + { + const videoChannel = { name: 'toto_channel', displayName: 'My toto channel' } + await addVideoChannel(servers[ 0 ].url, servers[ 0 ].accessToken, videoChannel) + } + + { + await createUser(servers[ 0 ].url, servers[ 0 ].accessToken, 'toto', 'password') + const accessToken = await userLogin(servers[ 0 ], { username: 'toto', password: 'password' }) + + const res = await getMyUserInformation(servers[ 0 ].url, accessToken) + const videoChannel = res.body.videoChannels[ 0 ] + expect(videoChannel.name).to.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/) + } }) after(async function () { diff --git a/server/tests/cli/update-host.ts b/server/tests/cli/update-host.ts index 968b7bd7f..7f54c0e70 100644 --- a/server/tests/cli/update-host.ts +++ b/server/tests/cli/update-host.ts @@ -54,6 +54,7 @@ describe('Test update host scripts', function () { // Create channel const videoChannel = { + name: 'second_channel', displayName: 'second video channel', description: 'super video channel description' } diff --git a/server/tests/real-world/populate-database.ts b/server/tests/real-world/populate-database.ts index d41ac8d36..a7fdbd1dc 100644 --- a/server/tests/real-world/populate-database.ts +++ b/server/tests/real-world/populate-database.ts @@ -66,6 +66,7 @@ function getRandomInt (min, max) { function createCustomChannel (server: ServerInfo) { const videoChannel = { + name: Date.now().toString(), displayName: Date.now().toString(), description: Date.now().toString() } diff --git a/server/tests/utils/videos/video-channels.ts b/server/tests/utils/videos/video-channels.ts index 3ca39469c..1eea22b31 100644 --- a/server/tests/utils/videos/video-channels.ts +++ b/server/tests/utils/videos/video-channels.ts @@ -97,10 +97,10 @@ function updateVideoChannelAvatar (options: { url: string, accessToken: string, fixture: string, - videoChannelId: string | number + videoChannelName: string | number }) { - const path = '/api/v1/video-channels/' + options.videoChannelId + '/avatar/pick' + const path = '/api/v1/video-channels/' + options.videoChannelName + '/avatar/pick' return updateAvatarRequest(Object.assign(options, { path })) } diff --git a/server/tests/utils/videos/videos.ts b/server/tests/utils/videos/videos.ts index b280cccda..592248144 100644 --- a/server/tests/utils/videos/videos.ts +++ b/server/tests/utils/videos/videos.ts @@ -199,13 +199,13 @@ function getAccountVideos ( function getVideoChannelVideos ( url: string, accessToken: string, - videoChannelId: number | string, + videoChannelName: string, start: number, count: number, sort?: string, query: { nsfw?: boolean } = {} ) { - const path = '/api/v1/video-channels/' + videoChannelId + '/videos' + const path = '/api/v1/video-channels/' + videoChannelName + '/videos' return makeGetRequest({ url, diff --git a/shared/models/videos/channel/video-channel-create.model.ts b/shared/models/videos/channel/video-channel-create.model.ts index 08cd5fb84..da8ce620c 100644 --- a/shared/models/videos/channel/video-channel-create.model.ts +++ b/shared/models/videos/channel/video-channel-create.model.ts @@ -1,4 +1,5 @@ export interface VideoChannelCreate { + name: string displayName: string description?: string support?: string -- cgit v1.2.3