aboutsummaryrefslogtreecommitdiffhomepage
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/src/app/+page-not-found/page-not-found.component.ts3
-rw-r--r--client/src/app/app-routing.module.ts28
-rw-r--r--client/src/app/shared/form-validators/user-validators.ts4
-rw-r--r--client/src/app/shared/shared-main/account/actor.service.ts37
-rw-r--r--client/src/app/shared/shared-main/account/index.ts1
-rw-r--r--client/src/app/shared/shared-main/index.ts3
-rw-r--r--client/src/app/shared/shared-main/router/actor-redirect-guard.service.ts46
-rw-r--r--client/src/app/shared/shared-main/router/index.ts1
-rw-r--r--client/src/app/shared/shared-main/shared-main.module.ts10
9 files changed, 78 insertions, 55 deletions
diff --git a/client/src/app/+page-not-found/page-not-found.component.ts b/client/src/app/+page-not-found/page-not-found.component.ts
index 94b4c8d27..695568898 100644
--- a/client/src/app/+page-not-found/page-not-found.component.ts
+++ b/client/src/app/+page-not-found/page-not-found.component.ts
@@ -3,6 +3,7 @@ import { Title } from '@angular/platform-browser'
3import { Router } from '@angular/router' 3import { Router } from '@angular/router'
4import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
5 5
6
6@Component({ 7@Component({
7 selector: 'my-page-not-found', 8 selector: 'my-page-not-found',
8 templateUrl: './page-not-found.component.html', 9 templateUrl: './page-not-found.component.html',
@@ -10,7 +11,7 @@ import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
10}) 11})
11export class PageNotFoundComponent implements OnInit { 12export class PageNotFoundComponent implements OnInit {
12 status = HttpStatusCode.NOT_FOUND_404 13 status = HttpStatusCode.NOT_FOUND_404
13 type: string 14 type: 'video' | 'other' = 'other'
14 15
15 public constructor ( 16 public constructor (
16 private titleService: Title, 17 private titleService: Title,
diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts
index 4619c4046..444b6f134 100644
--- a/client/src/app/app-routing.module.ts
+++ b/client/src/app/app-routing.module.ts
@@ -5,7 +5,8 @@ import { MenuGuards } from '@app/core/routing/menu-guard.service'
5import { POSSIBLE_LOCALES } from '@shared/core-utils/i18n' 5import { POSSIBLE_LOCALES } from '@shared/core-utils/i18n'
6import { MetaGuard, PreloadSelectedModulesList } from './core' 6import { MetaGuard, PreloadSelectedModulesList } from './core'
7import { EmptyComponent } from './empty.component' 7import { EmptyComponent } from './empty.component'
8import { RootComponent } from './root.component' 8import { USER_USERNAME_REGEX_CHARACTERS } from './shared/form-validators/user-validators'
9import { ActorRedirectGuard } from './shared/shared-main'
9 10
10const routes: Routes = [ 11const routes: Routes = [
11 { 12 {
@@ -17,7 +18,8 @@ const routes: Routes = [
17 }, 18 },
18 { 19 {
19 path: 'home', 20 path: 'home',
20 loadChildren: () => import('./+home/home.module').then(m => m.HomeModule) 21 loadChildren: () => import('./+home/home.module').then(m => m.HomeModule),
22 canActivateChild: [ MetaGuard ]
21 }, 23 },
22 { 24 {
23 path: 'my-account', 25 path: 'my-account',
@@ -94,18 +96,22 @@ const routes: Routes = [
94 { 96 {
95 matcher: (url): UrlMatchResult => { 97 matcher: (url): UrlMatchResult => {
96 // Matches /@:actorName 98 // Matches /@:actorName
97 if (url.length === 1 && url[0].path.match(/^@[\w]+$/gm)) { 99 const regex = new RegExp(`^@(${USER_USERNAME_REGEX_CHARACTERS}+)$`)
98 return { 100 if (url.length !== 1) return null
99 consumed: url, 101
100 posParams: { 102 const matchResult = url[0].path.match(regex)
101 actorName: new UrlSegment(url[0].path.substr(1), {}) 103 if (!matchResult) return null
102 } 104
105 return {
106 consumed: url,
107 posParams: {
108 actorName: new UrlSegment(matchResult[1], {})
103 } 109 }
104 } 110 }
105
106 return null
107 }, 111 },
108 component: RootComponent 112 pathMatch: 'full',
113 canActivate: [ ActorRedirectGuard ],
114 component: EmptyComponent
109 }, 115 },
110 { 116 {
111 path: '', 117 path: '',
diff --git a/client/src/app/shared/form-validators/user-validators.ts b/client/src/app/shared/form-validators/user-validators.ts
index fee37e95f..976c97b87 100644
--- a/client/src/app/shared/form-validators/user-validators.ts
+++ b/client/src/app/shared/form-validators/user-validators.ts
@@ -1,12 +1,14 @@
1import { Validators } from '@angular/forms' 1import { Validators } from '@angular/forms'
2import { BuildFormValidator } from './form-validator.model' 2import { BuildFormValidator } from './form-validator.model'
3 3
4export const USER_USERNAME_REGEX_CHARACTERS = '[a-z0-9][a-z0-9._]'
5
4export const USER_USERNAME_VALIDATOR: BuildFormValidator = { 6export const USER_USERNAME_VALIDATOR: BuildFormValidator = {
5 VALIDATORS: [ 7 VALIDATORS: [
6 Validators.required, 8 Validators.required,
7 Validators.minLength(1), 9 Validators.minLength(1),
8 Validators.maxLength(50), 10 Validators.maxLength(50),
9 Validators.pattern(/^[a-z0-9][a-z0-9._]*$/) 11 Validators.pattern(new RegExp(`^${USER_USERNAME_REGEX_CHARACTERS}*$`))
10 ], 12 ],
11 MESSAGES: { 13 MESSAGES: {
12 'required': $localize`Username is required.`, 14 'required': $localize`Username is required.`,
diff --git a/client/src/app/shared/shared-main/account/actor.service.ts b/client/src/app/shared/shared-main/account/actor.service.ts
deleted file mode 100644
index 464ed4519..000000000
--- a/client/src/app/shared/shared-main/account/actor.service.ts
+++ /dev/null
@@ -1,37 +0,0 @@
1import { Observable, ReplaySubject } from 'rxjs'
2import { catchError, map, tap } from 'rxjs/operators'
3import { HttpClient } from '@angular/common/http'
4import { Injectable } from '@angular/core'
5import { RestExtractor } from '@app/core'
6import { Account as ServerAccount, VideoChannel as ServerVideoChannel } from '@shared/models'
7import { environment } from '../../../../environments/environment'
8
9type KeysOfUnion<T> = T extends T ? keyof T: never
10type ServerActor = KeysOfUnion<ServerAccount | ServerVideoChannel>
11
12@Injectable()
13export class ActorService {
14 static BASE_ACTOR_API_URL = environment.apiUrl + '/api/v1/actors/'
15
16 actorLoaded = new ReplaySubject<string>(1)
17
18 constructor (
19 private authHttp: HttpClient,
20 private restExtractor: RestExtractor
21 ) {}
22
23 getActorType (actorName: string): Observable<string> {
24 return this.authHttp.get<ServerActor>(ActorService.BASE_ACTOR_API_URL + actorName)
25 .pipe(
26 map(actorHash => {
27 if (actorHash[ 'userId' ]) {
28 return 'Account'
29 }
30
31 return 'VideoChannel'
32 }),
33 tap(actor => this.actorLoaded.next(actor)),
34 catchError(res => this.restExtractor.handleError(res))
35 )
36 }
37}
diff --git a/client/src/app/shared/shared-main/account/index.ts b/client/src/app/shared/shared-main/account/index.ts
index c6cdcd574..b80ddb9f5 100644
--- a/client/src/app/shared/shared-main/account/index.ts
+++ b/client/src/app/shared/shared-main/account/index.ts
@@ -1,4 +1,3 @@
1export * from './account.model' 1export * from './account.model'
2export * from './account.service' 2export * from './account.service'
3export * from './actor.model' 3export * from './actor.model'
4export * from './actor.service'
diff --git a/client/src/app/shared/shared-main/index.ts b/client/src/app/shared/shared-main/index.ts
index a4d813c06..3a7fd4c34 100644
--- a/client/src/app/shared/shared-main/index.ts
+++ b/client/src/app/shared/shared-main/index.ts
@@ -5,6 +5,9 @@ export * from './date'
5export * from './feeds' 5export * from './feeds'
6export * from './loaders' 6export * from './loaders'
7export * from './misc' 7export * from './misc'
8export * from './peertube-modal'
9export * from './plugins'
10export * from './router'
8export * from './users' 11export * from './users'
9export * from './video' 12export * from './video'
10export * from './video-caption' 13export * from './video-caption'
diff --git a/client/src/app/shared/shared-main/router/actor-redirect-guard.service.ts b/client/src/app/shared/shared-main/router/actor-redirect-guard.service.ts
new file mode 100644
index 000000000..49d61f945
--- /dev/null
+++ b/client/src/app/shared/shared-main/router/actor-redirect-guard.service.ts
@@ -0,0 +1,46 @@
1import { forkJoin, of } from 'rxjs'
2import { catchError, map } from 'rxjs/operators'
3import { Injectable } from '@angular/core'
4import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'
5import { AccountService } from '../account'
6import { VideoChannelService } from '../video-channel'
7
8@Injectable()
9export class ActorRedirectGuard implements CanActivate {
10
11 constructor (
12 private router: Router,
13 private accountService: AccountService,
14 private channelService: VideoChannelService
15 ) {}
16
17 canActivate (route: ActivatedRouteSnapshot) {
18 const actorName = route.params.actorName
19
20 return forkJoin([
21 this.accountService.getAccount(actorName).pipe(this.orUndefined()),
22 this.channelService.getVideoChannel(actorName).pipe(this.orUndefined())
23 ]).pipe(
24 map(([ account, channel ]) => {
25 if (!account && !channel) {
26 this.router.navigate([ '/404' ])
27 return false
28 }
29
30 if (account) {
31 this.router.navigate([ `/a/${actorName}` ], { skipLocationChange: true })
32 }
33
34 if (channel) {
35 this.router.navigate([ `/c/${actorName}` ], { skipLocationChange: true })
36 }
37
38 return true
39 })
40 )
41 }
42
43 private orUndefined () {
44 return catchError(() => of(undefined))
45 }
46}
diff --git a/client/src/app/shared/shared-main/router/index.ts b/client/src/app/shared/shared-main/router/index.ts
new file mode 100644
index 000000000..f4000b674
--- /dev/null
+++ b/client/src/app/shared/shared-main/router/index.ts
@@ -0,0 +1 @@
export * from './actor-redirect-guard.service'
diff --git a/client/src/app/shared/shared-main/shared-main.module.ts b/client/src/app/shared/shared-main/shared-main.module.ts
index f06f25ca5..c8dd01429 100644
--- a/client/src/app/shared/shared-main/shared-main.module.ts
+++ b/client/src/app/shared/shared-main/shared-main.module.ts
@@ -4,7 +4,7 @@ import { CommonModule, DatePipe } from '@angular/common'
4import { HttpClientModule } from '@angular/common/http' 4import { HttpClientModule } from '@angular/common/http'
5import { NgModule } from '@angular/core' 5import { NgModule } from '@angular/core'
6import { FormsModule, ReactiveFormsModule } from '@angular/forms' 6import { FormsModule, ReactiveFormsModule } from '@angular/forms'
7import { RouterModule } from '@angular/router' 7import { ActivatedRouteSnapshot, RouterModule } from '@angular/router'
8import { 8import {
9 NgbButtonsModule, 9 NgbButtonsModule,
10 NgbCollapseModule, 10 NgbCollapseModule,
@@ -17,7 +17,7 @@ import {
17import { LoadingBarModule } from '@ngx-loading-bar/core' 17import { LoadingBarModule } from '@ngx-loading-bar/core'
18import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client' 18import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client'
19import { SharedGlobalIconModule } from '../shared-icons' 19import { SharedGlobalIconModule } from '../shared-icons'
20import { AccountService, ActorService } from './account' 20import { AccountService } from './account'
21import { 21import {
22 AutofocusDirective, 22 AutofocusDirective,
23 BytesPipe, 23 BytesPipe,
@@ -39,6 +39,7 @@ import { UserHistoryService, UserNotificationsComponent, UserNotificationService
39import { RedundancyService, VideoImportService, VideoOwnershipService, VideoService } from './video' 39import { RedundancyService, VideoImportService, VideoOwnershipService, VideoService } from './video'
40import { VideoCaptionService } from './video-caption' 40import { VideoCaptionService } from './video-caption'
41import { VideoChannelService } from './video-channel' 41import { VideoChannelService } from './video-channel'
42import { ActorRedirectGuard } from './router'
42 43
43@NgModule({ 44@NgModule({
44 imports: [ 45 imports: [
@@ -161,7 +162,6 @@ import { VideoChannelService } from './video-channel'
161 AUTH_INTERCEPTOR_PROVIDER, 162 AUTH_INTERCEPTOR_PROVIDER,
162 163
163 AccountService, 164 AccountService,
164 ActorService,
165 165
166 UserHistoryService, 166 UserHistoryService,
167 UserNotificationService, 167 UserNotificationService,
@@ -175,7 +175,9 @@ import { VideoChannelService } from './video-channel'
175 175
176 VideoChannelService, 176 VideoChannelService,
177 177
178 CustomPageService 178 CustomPageService,
179
180 ActorRedirectGuard
179 ] 181 ]
180}) 182})
181export class SharedMainModule { } 183export class SharedMainModule { }