diff --git a/client/src/app/+about/about-instance/about-instance.component.ts b/client/src/app/+about/about-instance/about-instance.component.ts
index e74b5daeb..92ecd5263 100644
--- a/client/src/app/+about/about-instance/about-instance.component.ts
+++ b/client/src/app/+about/about-instance/about-instance.component.ts
@@ -3,7 +3,7 @@ import { AfterViewChecked, Component, OnInit, ViewChild } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-admin-modal.component'
import { Notifier } from '@app/core'
-import { copyToClipboard } from '../../../assets/player/utils'
+import { copyToClipboard } from '../../../root-helpers/utils'
import { InstanceService } from '@app/shared/shared-instance'
import { ServerConfig } from '@shared/models'
import { ResolverData } from './about-instance.resolver'
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
index 1d0e56bfd..aa6b5d0a9 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
@@ -161,7 +161,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
getVideoEmbed (entry: VideoBlacklist) {
return buildVideoOrPlaylistEmbed(
buildVideoLink({
- baseUrl: `${environment.embedUrl}/videos/embed/${entry.video.uuid}`,
+ baseUrl: `${environment.originServerUrl}/videos/embed/${entry.video.uuid}`,
title: false,
warningTitle: false
})
diff --git a/client/src/app/+my-account/my-account-applications/my-account-applications.component.html b/client/src/app/+my-account/my-account-applications/my-account-applications.component.html
index 62e2cb59b..53a9c91ac 100644
--- a/client/src/app/+my-account/my-account-applications/my-account-applications.component.html
+++ b/client/src/app/+my-account/my-account-applications/my-account-applications.component.html
@@ -7,8 +7,8 @@
diff --git a/client/src/app/+my-account/my-account-applications/my-account-applications.component.ts b/client/src/app/+my-account/my-account-applications/my-account-applications.component.ts
index c3f09dfe3..233e42c83 100644
--- a/client/src/app/+my-account/my-account-applications/my-account-applications.component.ts
+++ b/client/src/app/+my-account/my-account-applications/my-account-applications.component.ts
@@ -1,10 +1,10 @@
import { Component, OnInit } from '@angular/core'
-import { AuthService, Notifier, ConfirmService } from '@app/core'
+import { AuthService, Notifier, ConfirmService, ScopedTokensService } from '@app/core'
import { VideoService } from '@app/shared/shared-main'
import { FeedFormat } from '@shared/models'
-import { Subject, merge } from 'rxjs'
-import { debounceTime } from 'rxjs/operators'
+import { ScopedToken } from '@shared/models/users/user-scoped-token'
+import { environment } from '../../../environments/environment'
@Component({
selector: 'my-account-applications',
@@ -15,11 +15,11 @@ export class MyAccountApplicationsComponent implements OnInit {
feedUrl: string
feedToken: string
- private baseURL = window.location.protocol + '//' + window.location.host
- private tokenStream = new Subject()
+ private baseURL = environment.originServerUrl
constructor (
private authService: AuthService,
+ private scopedTokensService: ScopedTokensService,
private videoService: VideoService,
private notifier: Notifier,
private confirmService: ConfirmService
@@ -27,31 +27,40 @@ export class MyAccountApplicationsComponent implements OnInit {
ngOnInit () {
this.feedUrl = this.baseURL
+ this.scopedTokensService.getScopedTokens()
+ .subscribe(
+ tokens => this.regenApplications(tokens),
- merge(
- this.tokenStream,
- this.authService.userInformationLoaded
- ).pipe(debounceTime(400))
- .subscribe(
- _ => {
- const user = this.authService.getUser()
- this.videoService.getVideoSubscriptionFeedUrls(user.account.id)
- .then(feeds => this.feedUrl = this.baseURL + feeds.find(f => f.format === FeedFormat.RSS).url)
- .then(_ => this.authService.getScopedTokens().then(tokens => this.feedToken = tokens.feedToken))
- },
-
- err => {
- this.notifier.error(err.message)
- }
- )
+ err => {
+ this.notifier.error(err.message)
+ }
+ )
}
async renewToken () {
- const res = await this.confirmService.confirm('Renewing the token will disallow previously configured clients from retrieving the feed until they use the new token. Proceed?', 'Renew token')
+ const res = await this.confirmService.confirm(
+ $localize`Renewing the token will disallow previously configured clients from retrieving the feed until they use the new token. Proceed?`,
+ $localize`Renew token`
+ )
if (res === false) return
- await this.authService.renewScopedTokens()
- this.notifier.success('Token renewed. Update your client configuration accordingly.')
- this.tokenStream.next()
+ this.scopedTokensService.renewScopedTokens().subscribe(
+ tokens => {
+ this.regenApplications(tokens)
+ this.notifier.success($localize`Token renewed. Update your client configuration accordingly.`)
+ },
+
+ err => {
+ this.notifier.error(err.message)
+ }
+ )
+
+ }
+
+ private regenApplications (tokens: ScopedToken) {
+ const user = this.authService.getUser()
+ const feeds = this.videoService.getVideoSubscriptionFeedUrls(user.account.id, tokens.feedToken)
+ this.feedUrl = this.baseURL + feeds.find(f => f.format === FeedFormat.RSS).url
+ this.feedToken = tokens.feedToken
}
}
diff --git a/client/src/app/+my-account/my-account.component.ts b/client/src/app/+my-account/my-account.component.ts
index 12966aebb..eaf8a72e9 100644
--- a/client/src/app/+my-account/my-account.component.ts
+++ b/client/src/app/+my-account/my-account.component.ts
@@ -41,11 +41,6 @@ export class MyAccountComponent implements OnInit {
label: $localize`Abuse reports`,
routerLink: '/my-account/abuses',
iconName: 'flag'
- },
- {
- label: $localize`Applications`,
- routerLink: '/my-account/applications',
- iconName: 'codesandbox'
}
]
}
@@ -61,6 +56,11 @@ export class MyAccountComponent implements OnInit {
routerLink: '/my-account/notifications'
},
+ {
+ label: $localize`Applications`,
+ routerLink: '/my-account/applications'
+ },
+
moderationEntries
]
}
diff --git a/client/src/app/+my-account/my-account.module.ts b/client/src/app/+my-account/my-account.module.ts
index 70bf58aae..076864563 100644
--- a/client/src/app/+my-account/my-account.module.ts
+++ b/client/src/app/+my-account/my-account.module.ts
@@ -20,8 +20,8 @@ import { MyAccountDangerZoneComponent } from './my-account-settings/my-account-d
import { MyAccountNotificationPreferencesComponent } from './my-account-settings/my-account-notification-preferences'
import { MyAccountProfileComponent } from './my-account-settings/my-account-profile/my-account-profile.component'
import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component'
+import { MyAccountApplicationsComponent } from './my-account-applications/my-account-applications.component'
import { MyAccountComponent } from './my-account.component'
-import { VideoChangeOwnershipComponent } from './my-account-applications/my-account-applications.component'
@NgModule({
imports: [
@@ -46,13 +46,13 @@ import { VideoChangeOwnershipComponent } from './my-account-applications/my-acco
MyAccountChangePasswordComponent,
MyAccountProfileComponent,
MyAccountChangeEmailComponent,
+ MyAccountApplicationsComponent,
MyAccountDangerZoneComponent,
MyAccountBlocklistComponent,
MyAccountAbusesListComponent,
MyAccountServerBlocklistComponent,
MyAccountNotificationsComponent,
- MyAccountNotificationPreferencesComponent,
MyAccountNotificationPreferencesComponent
],
diff --git a/client/src/app/+videos/video-list/video-user-subscriptions.component.ts b/client/src/app/+videos/video-list/video-user-subscriptions.component.ts
index 10031d6cc..03881c295 100644
--- a/client/src/app/+videos/video-list/video-user-subscriptions.component.ts
+++ b/client/src/app/+videos/video-list/video-user-subscriptions.component.ts
@@ -1,6 +1,6 @@
import { Component, OnDestroy, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
-import { AuthService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core'
+import { AuthService, LocalStorageService, Notifier, ScopedTokensService, ScreenService, ServerService, UserService } from '@app/core'
import { HooksService } from '@app/core/plugins/hooks.service'
import { immutableAssign } from '@app/helpers'
import { VideoService } from '@app/shared/shared-main'
@@ -9,6 +9,7 @@ import { AbstractVideoList, OwnerDisplayType } from '@app/shared/shared-video-mi
import { VideoSortField, FeedFormat } from '@shared/models'
import { copyToClipboard } from '../../../root-helpers/utils'
import { environment } from '../../../environments/environment'
+import { forkJoin } from 'rxjs'
@Component({
selector: 'my-videos-user-subscriptions',
@@ -32,7 +33,8 @@ export class VideoUserSubscriptionsComponent extends AbstractVideoList implement
protected storageService: LocalStorageService,
private userSubscription: UserSubscriptionService,
private hooks: HooksService,
- private videoService: VideoService
+ private videoService: VideoService,
+ private scopedTokensService: ScopedTokensService
) {
super()
@@ -49,9 +51,19 @@ export class VideoUserSubscriptionsComponent extends AbstractVideoList implement
super.ngOnInit()
const user = this.authService.getUser()
- let feedUrl = environment.embedUrl
- this.videoService.getVideoSubscriptionFeedUrls(user.account.id)
- .then((feeds: any) => feedUrl = feedUrl + feeds.find((f: any) => f.format === FeedFormat.RSS).url)
+ let feedUrl = environment.originServerUrl
+
+ this.scopedTokensService.getScopedTokens().subscribe(
+ tokens => {
+ const feeds = this.videoService.getVideoSubscriptionFeedUrls(user.account.id, tokens.feedToken)
+ feedUrl = feedUrl + feeds.find((f: any) => f.format === FeedFormat.RSS).url
+ },
+
+ err => {
+ this.notifier.error(err.message)
+ }
+ )
+
this.actions.unshift({
label: $localize`Feed`,
iconName: 'syndication',
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts
index 224f35f82..fd6062d3f 100644
--- a/client/src/app/core/auth/auth.service.ts
+++ b/client/src/app/core/auth/auth.service.ts
@@ -11,7 +11,6 @@ import { environment } from '../../../environments/environment'
import { RestExtractor } from '../rest/rest-extractor.service'
import { AuthStatus } from './auth-status.model'
import { AuthUser } from './auth-user.model'
-import { ScopedTokenType, ScopedToken } from '@shared/models/users/user-scoped-token'
interface UserLoginWithUsername extends UserLogin {
access_token: string
@@ -27,7 +26,6 @@ export class AuthService {
private static BASE_CLIENT_URL = environment.apiUrl + '/api/v1/oauth-clients/local'
private static BASE_TOKEN_URL = environment.apiUrl + '/api/v1/users/token'
private static BASE_REVOKE_TOKEN_URL = environment.apiUrl + '/api/v1/users/revoke-token'
- private static BASE_SCOPED_TOKENS_URL = environment.apiUrl + '/api/v1/users/scoped-tokens'
private static BASE_USER_INFORMATION_URL = environment.apiUrl + '/api/v1/users/me'
private static LOCAL_STORAGE_OAUTH_CLIENT_KEYS = {
CLIENT_ID: 'client_id',
@@ -43,7 +41,6 @@ export class AuthService {
private loginChanged: Subject
private user: AuthUser = null
private refreshingTokenObservable: Observable
- private scopedTokens: ScopedToken
constructor (
private http: HttpClient,
@@ -247,48 +244,6 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
)
}
- getScopedTokens (): Promise {
- return new Promise((res, rej) => {
- if (this.scopedTokens) return res(this.scopedTokens)
-
- const authHeaderValue = this.getRequestHeaderValue()
- const headers = new HttpHeaders().set('Authorization', authHeaderValue)
-
- this.http.get(AuthService.BASE_SCOPED_TOKENS_URL, { headers })
- .subscribe(
- scopedTokens => {
- this.scopedTokens = scopedTokens
- res(this.scopedTokens)
- },
-
- err => {
- console.error(err)
- rej(err)
- }
- )
- })
- }
-
- renewScopedTokens (): Promise {
- return new Promise((res, rej) => {
- const authHeaderValue = this.getRequestHeaderValue()
- const headers = new HttpHeaders().set('Authorization', authHeaderValue)
-
- this.http.post(AuthService.BASE_SCOPED_TOKENS_URL, {}, { headers })
- .subscribe(
- scopedTokens => {
- this.scopedTokens = scopedTokens
- res(this.scopedTokens)
- },
-
- err => {
- console.error(err)
- rej(err)
- }
- )
- })
- }
-
private mergeUserInformation (obj: UserLoginWithUsername): Observable {
// User is not loaded yet, set manually auth header
const headers = new HttpHeaders().set('Authorization', `${obj.token_type} ${obj.access_token}`)
diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts
index 6c0a2245d..f51f1920d 100644
--- a/client/src/app/core/core.module.ts
+++ b/client/src/app/core/core.module.ts
@@ -12,6 +12,7 @@ import { LoadingBarModule } from '@ngx-loading-bar/core'
import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client'
import { LoadingBarRouterModule } from '@ngx-loading-bar/router'
import { AuthService } from './auth'
+import { ScopedTokensService } from './scoped-tokens'
import { ConfirmService } from './confirm'
import { CheatSheetComponent } from './hotkeys'
import { MenuService } from './menu'
@@ -57,6 +58,7 @@ import { LocalStorageService, ScreenService, SessionStorageService } from './wra
providers: [
AuthService,
+ ScopedTokensService,
ConfirmService,
ServerService,
ThemeService,
diff --git a/client/src/app/core/index.ts b/client/src/app/core/index.ts
index a0c34543d..9245ff6fc 100644
--- a/client/src/app/core/index.ts
+++ b/client/src/app/core/index.ts
@@ -1,4 +1,5 @@
export * from './auth'
+export * from './scoped-tokens'
export * from './confirm'
export * from './hotkeys'
export * from './menu'
diff --git a/client/src/app/core/scoped-tokens/index.ts b/client/src/app/core/scoped-tokens/index.ts
new file mode 100644
index 000000000..c9a48ffcd
--- /dev/null
+++ b/client/src/app/core/scoped-tokens/index.ts
@@ -0,0 +1 @@
+export * from './scoped-tokens.service'
diff --git a/client/src/app/core/scoped-tokens/scoped-tokens.service.ts b/client/src/app/core/scoped-tokens/scoped-tokens.service.ts
new file mode 100644
index 000000000..8e3697c31
--- /dev/null
+++ b/client/src/app/core/scoped-tokens/scoped-tokens.service.ts
@@ -0,0 +1,33 @@
+import { Injectable } from '@angular/core'
+import { HttpClient } from '@angular/common/http'
+import { environment } from '../../../environments/environment'
+import { AuthService } from '../auth'
+import { ScopedToken } from '@shared/models/users/user-scoped-token'
+import { catchError } from 'rxjs/operators'
+import { RestExtractor } from '../rest'
+
+@Injectable()
+export class ScopedTokensService {
+ private static BASE_SCOPED_TOKENS_URL = environment.apiUrl + '/api/v1/users/scoped-tokens'
+
+ constructor (
+ private authHttp: HttpClient,
+ private restExtractor: RestExtractor
+ ) {}
+
+ getScopedTokens () {
+ return this.authHttp
+ .get(ScopedTokensService.BASE_SCOPED_TOKENS_URL)
+ .pipe(
+ catchError(res => this.restExtractor.handleError(res))
+ )
+ }
+
+ renewScopedTokens () {
+ return this.authHttp
+ .post(ScopedTokensService.BASE_SCOPED_TOKENS_URL, {})
+ .pipe(
+ catchError(res => this.restExtractor.handleError(res))
+ )
+ }
+}
diff --git a/client/src/app/helpers/utils.ts b/client/src/app/helpers/utils.ts
index a22507f46..9c805b4ca 100644
--- a/client/src/app/helpers/utils.ts
+++ b/client/src/app/helpers/utils.ts
@@ -58,7 +58,7 @@ function getAbsoluteAPIUrl () {
}
function getAbsoluteEmbedUrl () {
- let absoluteEmbedUrl = environment.embedUrl
+ let absoluteEmbedUrl = environment.originServerUrl
if (!absoluteEmbedUrl) {
// The Embed is on the same domain
absoluteEmbedUrl = window.location.origin
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
index ca0d23699..807665b9c 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
@@ -112,7 +112,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
getVideoEmbed (abuse: AdminAbuse) {
return buildVideoOrPlaylistEmbed(
buildVideoLink({
- baseUrl: `${environment.embedUrl}/videos/embed/${abuse.video.uuid}`,
+ baseUrl: `${environment.originServerUrl}/videos/embed/${abuse.video.uuid}`,
title: false,
warningTitle: false,
startTime: abuse.video.startAt,
diff --git a/client/src/app/shared/shared-forms/input-readonly-copy.component.ts b/client/src/app/shared/shared-forms/input-readonly-copy.component.ts
index 520827a53..b04d69d05 100644
--- a/client/src/app/shared/shared-forms/input-readonly-copy.component.ts
+++ b/client/src/app/shared/shared-forms/input-readonly-copy.component.ts
@@ -1,6 +1,5 @@
import { Component, Input } from '@angular/core'
import { Notifier } from '@app/core'
-import { FormGroup } from '@angular/forms'
@Component({
selector: 'my-input-readonly-copy',
diff --git a/client/src/app/shared/shared-main/video/video.service.ts b/client/src/app/shared/shared-main/video/video.service.ts
index b81540e8d..70be5d7d2 100644
--- a/client/src/app/shared/shared-main/video/video.service.ts
+++ b/client/src/app/shared/shared-main/video/video.service.ts
@@ -18,8 +18,7 @@ import {
VideoFilter,
VideoPrivacy,
VideoSortField,
- VideoUpdate,
- VideoCreate
+ VideoUpdate
} from '@shared/models'
import { environment } from '../../../../environments/environment'
import { Account } from '../account/account.model'
@@ -44,13 +43,13 @@ export interface VideosProvider {
export class VideoService implements VideosProvider {
static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/'
static BASE_FEEDS_URL = environment.apiUrl + '/feeds/videos.'
+ static BASE_SUBSCRIPTION_FEEDS_URL = environment.apiUrl + '/feeds/subscriptions.'
constructor (
private authHttp: HttpClient,
private restExtractor: RestExtractor,
private restService: RestService,
- private serverService: ServerService,
- private authService: AuthService
+ private serverService: ServerService
) {}
getVideoViewUrl (uuid: string) {
@@ -238,22 +237,22 @@ export class VideoService implements VideosProvider {
)
}
- buildBaseFeedUrls (params: HttpParams) {
+ buildBaseFeedUrls (params: HttpParams, base = VideoService.BASE_FEEDS_URL) {
const feeds = [
{
format: FeedFormat.RSS,
label: 'media rss 2.0',
- url: VideoService.BASE_FEEDS_URL + FeedFormat.RSS.toLowerCase()
+ url: base + FeedFormat.RSS.toLowerCase()
},
{
format: FeedFormat.ATOM,
label: 'atom 1.0',
- url: VideoService.BASE_FEEDS_URL + FeedFormat.ATOM.toLowerCase()
+ url: base + FeedFormat.ATOM.toLowerCase()
},
{
format: FeedFormat.JSON,
label: 'json 1.0',
- url: VideoService.BASE_FEEDS_URL + FeedFormat.JSON.toLowerCase()
+ url: base + FeedFormat.JSON.toLowerCase()
}
]
@@ -294,14 +293,12 @@ export class VideoService implements VideosProvider {
return this.buildBaseFeedUrls(params)
}
- async getVideoSubscriptionFeedUrls (accountId: number) {
+ getVideoSubscriptionFeedUrls (accountId: number, feedToken: string) {
let params = this.restService.addRestGetParams(new HttpParams())
params = params.set('accountId', accountId.toString())
-
- const { feedToken } = await this.authService.getScopedTokens()
params = params.set('token', feedToken)
- return this.buildBaseFeedUrls(params)
+ return this.buildBaseFeedUrls(params, VideoService.BASE_SUBSCRIPTION_FEEDS_URL)
}
getVideoFileMetadata (metadataUrl: string) {
diff --git a/client/src/environments/environment.e2e.ts b/client/src/environments/environment.e2e.ts
index b33ff9f86..a1a58e36f 100644
--- a/client/src/environments/environment.e2e.ts
+++ b/client/src/environments/environment.e2e.ts
@@ -2,5 +2,5 @@ export const environment = {
production: false,
hmr: false,
apiUrl: 'http://localhost:9001',
- embedUrl: 'http://localhost:9001'
+ originServerUrl: 'http://localhost:9001'
}
diff --git a/client/src/environments/environment.hmr.ts b/client/src/environments/environment.hmr.ts
index 3b6eff302..ab7631920 100644
--- a/client/src/environments/environment.hmr.ts
+++ b/client/src/environments/environment.hmr.ts
@@ -2,5 +2,5 @@ export const environment = {
production: false,
hmr: true,
apiUrl: '',
- embedUrl: 'http://localhost:9000'
+ originServerUrl: 'http://localhost:9000'
}
diff --git a/client/src/environments/environment.prod.ts b/client/src/environments/environment.prod.ts
index 2e9b9fefe..e1b736c61 100644
--- a/client/src/environments/environment.prod.ts
+++ b/client/src/environments/environment.prod.ts
@@ -2,5 +2,5 @@ export const environment = {
production: true,
hmr: false,
apiUrl: '',
- embedUrl: ''
+ originServerUrl: ''
}
diff --git a/client/src/environments/environment.ts b/client/src/environments/environment.ts
index e00523976..5d7011265 100644
--- a/client/src/environments/environment.ts
+++ b/client/src/environments/environment.ts
@@ -12,5 +12,5 @@ export const environment = {
production: true,
hmr: false,
apiUrl: '',
- embedUrl: ''
+ originServerUrl: ''
}
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss
index 4d70110fe..e6491b492 100644
--- a/client/src/sass/include/_mixins.scss
+++ b/client/src/sass/include/_mixins.scss
@@ -225,7 +225,7 @@
line-height: $button-height;
border-radius: 3px;
text-align: center;
- padding: 0 13px 0 13px;
+ padding: 0 17px 0 13px;
cursor: pointer;
}
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts
index 6e9f7e60c..5c95069fc 100644
--- a/server/controllers/feeds.ts
+++ b/server/controllers/feeds.ts
@@ -18,7 +18,6 @@ import { cacheRoute } from '../middlewares/cache'
import { VideoModel } from '../models/video/video'
import { VideoCommentModel } from '../models/video/video-comment'
import { VideoFilter } from '../../shared/models/videos/video-query.type'
-import { logger } from '../helpers/logger'
const feedsRouter = express.Router()
@@ -47,10 +46,24 @@ feedsRouter.get('/feeds/videos.:format',
})(ROUTE_CACHE_LIFETIME.FEEDS)),
commonVideosFiltersValidator,
asyncMiddleware(videoFeedsValidator),
- asyncMiddleware(videoSubscriptonFeedsValidator),
asyncMiddleware(generateVideoFeed)
)
+feedsRouter.get('/feeds/subscriptions.:format',
+ videosSortValidator,
+ setDefaultVideosSort,
+ feedsFormatValidator,
+ setFeedFormatContentType,
+ asyncMiddleware(cacheRoute({
+ headerBlacklist: [
+ 'Content-Type'
+ ]
+ })(ROUTE_CACHE_LIFETIME.FEEDS)),
+ commonVideosFiltersValidator,
+ asyncMiddleware(videoSubscriptonFeedsValidator),
+ asyncMiddleware(generateVideoFeedForSubscriptions)
+)
+
// ---------------------------------------------------------------------------
export {
@@ -61,7 +74,6 @@ export {
async function generateVideoCommentsFeed (req: express.Request, res: express.Response) {
const start = 0
-
const video = res.locals.videoAll
const account = res.locals.account
const videoChannel = res.locals.videoChannel
@@ -125,10 +137,8 @@ async function generateVideoCommentsFeed (req: express.Request, res: express.Res
async function generateVideoFeed (req: express.Request, res: express.Response) {
const start = 0
-
const account = res.locals.account
const videoChannel = res.locals.videoChannel
- const token = req.query.token
const nsfw = buildNSFWFilter(res, req.query.nsfw)
let name: string
@@ -152,21 +162,10 @@ async function generateVideoFeed (req: express.Request, res: express.Response) {
queryString: new URL(WEBSERVER.URL + req.url).search
})
- /**
- * We have two ways to query video results:
- * - one with account and token -> get subscription videos
- * - one with either account, channel, or nothing: just videos with these filters
- */
- const options = token && token !== '' && res.locals.user
- ? {
- followerActorId: res.locals.user.Account.Actor.id,
- user: res.locals.user,
- includeLocalVideos: false
- }
- : {
- accountId: account ? account.id : null,
- videoChannelId: videoChannel ? videoChannel.id : null
- }
+ const options = {
+ accountId: account ? account.id : null,
+ videoChannelId: videoChannel ? videoChannel.id : null
+ }
const resultList = await VideoModel.listForApi({
start,
@@ -179,10 +178,86 @@ async function generateVideoFeed (req: express.Request, res: express.Response) {
...options
})
+ addVideosToFeed(feed, resultList.data)
+
+ // Now the feed generation is done, let's send it!
+ return sendFeed(feed, req, res)
+}
+
+async function generateVideoFeedForSubscriptions (req: express.Request, res: express.Response) {
+ const start = 0
+ const account = res.locals.account
+ const nsfw = buildNSFWFilter(res, req.query.nsfw)
+ const name = account.getDisplayName()
+ const description = account.description
+
+ const feed = initFeed({
+ name,
+ description,
+ resourceType: 'videos',
+ queryString: new URL(WEBSERVER.URL + req.url).search
+ })
+
+ const options = {
+ followerActorId: res.locals.user.Account.Actor.id,
+ user: res.locals.user
+ }
+
+ const resultList = await VideoModel.listForApi({
+ start,
+ count: FEEDS.COUNT,
+ sort: req.query.sort,
+ includeLocalVideos: true,
+ nsfw,
+ filter: req.query.filter as VideoFilter,
+ withFiles: true,
+ ...options
+ })
+
+ addVideosToFeed(feed, resultList.data)
+
+ // Now the feed generation is done, let's send it!
+ return sendFeed(feed, req, res)
+}
+
+function initFeed (parameters: {
+ name: string
+ description: string
+ resourceType?: 'videos' | 'video-comments'
+ queryString?: string
+}) {
+ const webserverUrl = WEBSERVER.URL
+ const { name, description, resourceType, queryString } = parameters
+
+ return new Feed({
+ title: name,
+ description,
+ // updated: TODO: somehowGetLatestUpdate, // optional, default = today
+ id: webserverUrl,
+ link: webserverUrl,
+ image: webserverUrl + '/client/assets/images/icons/icon-96x96.png',
+ favicon: webserverUrl + '/client/assets/images/favicon.png',
+ copyright: `All rights reserved, unless otherwise specified in the terms specified at ${webserverUrl}/about` +
+ ` and potential licenses granted by each content's rightholder.`,
+ generator: `Toraifōsu`, // ^.~
+ feedLinks: {
+ json: `${webserverUrl}/feeds/${resourceType}.json${queryString}`,
+ atom: `${webserverUrl}/feeds/${resourceType}.atom${queryString}`,
+ rss: `${webserverUrl}/feeds/${resourceType}.xml${queryString}`
+ },
+ author: {
+ name: 'Instance admin of ' + CONFIG.INSTANCE.NAME,
+ email: CONFIG.ADMIN.EMAIL,
+ link: `${webserverUrl}/about`
+ }
+ })
+}
+
+function addVideosToFeed (feed, videos: VideoModel[]) {
/**
* Adding video items to the feed object, one at a time
*/
- resultList.data.forEach(video => {
+ for (const video of videos) {
const formattedVideoFiles = video.getFormattedVideoFilesJSON()
const torrents = formattedVideoFiles.map(videoFile => ({
@@ -252,43 +327,7 @@ async function generateVideoFeed (req: express.Request, res: express.Response) {
}
]
})
- })
-
- // Now the feed generation is done, let's send it!
- return sendFeed(feed, req, res)
-}
-
-function initFeed (parameters: {
- name: string
- description: string
- resourceType?: 'videos' | 'video-comments'
- queryString?: string
-}) {
- const webserverUrl = WEBSERVER.URL
- const { name, description, resourceType, queryString } = parameters
-
- return new Feed({
- title: name,
- description,
- // updated: TODO: somehowGetLatestUpdate, // optional, default = today
- id: webserverUrl,
- link: webserverUrl,
- image: webserverUrl + '/client/assets/images/icons/icon-96x96.png',
- favicon: webserverUrl + '/client/assets/images/favicon.png',
- copyright: `All rights reserved, unless otherwise specified in the terms specified at ${webserverUrl}/about` +
- ` and potential licenses granted by each content's rightholder.`,
- generator: `Toraifōsu`, // ^.~
- feedLinks: {
- json: `${webserverUrl}/feeds/${resourceType}.json${queryString}`,
- atom: `${webserverUrl}/feeds/${resourceType}.atom${queryString}`,
- rss: `${webserverUrl}/feeds/${resourceType}.xml${queryString}`
- },
- author: {
- name: 'Instance admin of ' + CONFIG.INSTANCE.NAME,
- email: CONFIG.ADMIN.EMAIL,
- link: `${webserverUrl}/about`
- }
- })
+ }
}
function sendFeed (feed, req: express.Request, res: express.Response) {
diff --git a/server/helpers/middlewares/accounts.ts b/server/helpers/middlewares/accounts.ts
index 9be80167c..fa4a51e6c 100644
--- a/server/helpers/middlewares/accounts.ts
+++ b/server/helpers/middlewares/accounts.ts
@@ -28,8 +28,7 @@ async function doesAccountExist (p: Bluebird, res: Response, se
if (!account) {
if (sendNotFound === true) {
res.status(404)
- .send({ error: 'Account not found' })
- .end()
+ .json({ error: 'Account not found' })
}
return false
@@ -41,12 +40,11 @@ async function doesAccountExist (p: Bluebird, res: Response, se
}
async function doesUserFeedTokenCorrespond (id: number | string, token: string, res: Response) {
- const user = await UserModel.loadById(parseInt(id + '', 10))
+ const user = await UserModel.loadByIdWithChannels(parseInt(id + '', 10))
if (token !== user.feedToken) {
res.status(401)
- .send({ error: 'User and token mismatch' })
- .end()
+ .json({ error: 'User and token mismatch' })
return false
}
diff --git a/server/middlewares/validators/feeds.ts b/server/middlewares/validators/feeds.ts
index 5c76a679f..35080ffca 100644
--- a/server/middlewares/validators/feeds.ts
+++ b/server/middlewares/validators/feeds.ts
@@ -64,8 +64,8 @@ const videoFeedsValidator = [
]
const videoSubscriptonFeedsValidator = [
- query('accountId').optional().custom(isIdValid),
- query('token').optional(),
+ query('accountId').custom(isIdValid),
+ query('token'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking feeds parameters', { parameters: req.query })
@@ -74,6 +74,7 @@ const videoSubscriptonFeedsValidator = [
// a token alone is erroneous
if (req.query.token && !req.query.accountId) return
+ if (req.query.accountId && !await doesAccountIdExist(req.query.accountId, res)) return
if (req.query.token && !await doesUserFeedTokenCorrespond(res.locals.account.userId, req.query.token, res)) return
return next()
diff --git a/server/tests/feeds/feeds.ts b/server/tests/feeds/feeds.ts
index 2cd9b2d0a..175ea9102 100644
--- a/server/tests/feeds/feeds.ts
+++ b/server/tests/feeds/feeds.ts
@@ -326,7 +326,7 @@ describe('Test syndication feeds', () => {
const res = await listUserSubscriptionVideos(servers[0].url, feeduserAccessToken)
expect(res.body.total).to.equal(0)
- const json = await getJSONfeed(servers[0].url, 'videos', { accountId: feeduserAccountId, token: feeduserFeedToken })
+ const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: feeduserFeedToken })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos
}
@@ -337,7 +337,7 @@ describe('Test syndication feeds', () => {
const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken)
expect(res.body.total).to.equal(0)
- const json = await getJSONfeed(servers[0].url, 'videos', { accountId: userAccountId, token: userFeedToken })
+ const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos
}
@@ -354,7 +354,7 @@ describe('Test syndication feeds', () => {
expect(res.body.total).to.equal(1)
expect(res.body.data[0].name).to.equal('user video')
- const json = await getJSONfeed(servers[0].url, 'videos', { accountId: userAccountId, token: userFeedToken, version: 1 })
+ const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 1 })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(1) // subscribed to self, it should not list the instance's videos but list john's
}
@@ -370,7 +370,7 @@ describe('Test syndication feeds', () => {
const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken)
expect(res.body.total).to.equal(2, "there should be 2 videos part of the subscription")
- const json = await getJSONfeed(servers[0].url, 'videos', { accountId: userAccountId, token: userFeedToken, version: 2 })
+ const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 2 })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(2) // subscribed to root, it should not list the instance's videos but list root/john's
}
diff --git a/shared/extra-utils/feeds/feeds.ts b/shared/extra-utils/feeds/feeds.ts
index af6df2b20..bafbb9f94 100644
--- a/shared/extra-utils/feeds/feeds.ts
+++ b/shared/extra-utils/feeds/feeds.ts
@@ -1,6 +1,6 @@
import * as request from 'supertest'
-type FeedType = 'videos' | 'video-comments'
+type FeedType = 'videos' | 'video-comments' | 'subscriptions'
function getXMLfeed (url: string, feed: FeedType, format?: string) {
const path = '/feeds/' + feed + '.xml'