From: Chocobozzz Date: Sun, 20 Nov 2016 16:18:15 +0000 (+0100) Subject: Client: split in angular modules X-Git-Tag: v0.0.1-alpha~604 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=693b1aba4675f7e3d850e0f6d07dbfc7bdff9b8c;p=oweals%2Fpeertube.git Client: split in angular modules --- diff --git a/client/package.json b/client/package.json index bce442c38..c767787a7 100644 --- a/client/package.json +++ b/client/package.json @@ -34,7 +34,7 @@ "@types/uglify-js": "^2.0.27", "@types/videojs": "0.0.30", "@types/webpack": "^1.12.29", - "angular-pipes": "^4.0.0", + "angular-pipes": "^5.0.0", "angular2-template-loader": "^0.6.0", "assets-webpack-plugin": "^3.4.0", "awesome-typescript-loader": "^2.2.1", @@ -53,7 +53,7 @@ "json-loader": "^0.5.4", "ng2-bootstrap": "1.1.16", "ng2-file-upload": "^1.1.0", - "ng2-meta": "github:chocobozzz/ng2-meta", + "ng2-meta": "^2.0.0", "node-sass": "^3.10.0", "normalize.css": "^5.0.0", "raw-loader": "^0.5.1", diff --git a/client/src/app/account/account-routing.module.ts b/client/src/app/account/account-routing.module.ts new file mode 100644 index 000000000..8c1033cd2 --- /dev/null +++ b/client/src/app/account/account-routing.module.ts @@ -0,0 +1,22 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { AccountComponent } from './account.component'; + +const accountRoutes: Routes = [ + { + path: 'account', + component: AccountComponent, + data: { + meta: { + titleSuffix: ' - My account' + } + } + } +]; + +@NgModule({ + imports: [ RouterModule.forChild(accountRoutes) ], + exports: [ RouterModule ] +}) +export class AccountRoutingModule {} diff --git a/client/src/app/account/account.module.ts b/client/src/app/account/account.module.ts new file mode 100644 index 000000000..53f6ba58e --- /dev/null +++ b/client/src/app/account/account.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; + +import { AccountRoutingModule } from './account-routing.module'; +import { AccountComponent } from './account.component'; +import { AccountService } from './account.service'; +import { SharedModule } from '../shared'; + +@NgModule({ + imports: [ + AccountRoutingModule, + SharedModule + ], + + declarations: [ + AccountComponent + ], + + exports: [ + AccountComponent + ], + + providers: [ + AccountService + ] +}) +export class AccountModule { } diff --git a/client/src/app/account/account.routes.ts b/client/src/app/account/account.routes.ts deleted file mode 100644 index c382a6deb..000000000 --- a/client/src/app/account/account.routes.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { AccountComponent } from './account.component'; - -export const AccountRoutes = [ - { - path: 'account', - component: AccountComponent, - data: { - meta: { - titleSuffix: ' - My account' - } - } - } -]; diff --git a/client/src/app/account/account.service.ts b/client/src/app/account/account.service.ts index 355bcef74..0635c2533 100644 --- a/client/src/app/account/account.service.ts +++ b/client/src/app/account/account.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; -import { AuthHttp, AuthService, RestExtractor } from '../shared'; +import { AuthService } from '../core'; +import { AuthHttp, RestExtractor } from '../shared'; @Injectable() export class AccountService { diff --git a/client/src/app/account/index.ts b/client/src/app/account/index.ts index 823d9fe5f..be03713cb 100644 --- a/client/src/app/account/index.ts +++ b/client/src/app/account/index.ts @@ -1,3 +1,4 @@ +export * from './account-routing.module'; export * from './account.component'; -export * from './account.routes'; +export * from './account.module'; export * from './account.service'; diff --git a/client/src/app/admin/admin-routing.module.ts b/client/src/app/admin/admin-routing.module.ts new file mode 100644 index 000000000..6bff25033 --- /dev/null +++ b/client/src/app/admin/admin-routing.module.ts @@ -0,0 +1,30 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { AdminComponent } from './admin.component'; +import { FriendsRoutes } from './friends'; +import { RequestsRoutes } from './requests'; +import { UsersRoutes } from './users'; + +const adminRoutes: Routes = [ + { + path: 'admin', + component: AdminComponent, + children: [ + { + path: '', + redirectTo: 'users', + pathMatch: 'full' + }, + ...FriendsRoutes, + ...RequestsRoutes, + ...UsersRoutes + ] + } +]; + +@NgModule({ + imports: [ RouterModule.forChild(adminRoutes) ], + exports: [ RouterModule ] +}) +export class AdminRoutingModule {} diff --git a/client/src/app/admin/admin.module.ts b/client/src/app/admin/admin.module.ts new file mode 100644 index 000000000..63d99a3db --- /dev/null +++ b/client/src/app/admin/admin.module.ts @@ -0,0 +1,45 @@ +import { NgModule } from '@angular/core'; + +import { AdminComponent } from './admin.component'; +import { AdminRoutingModule } from './admin-routing.module'; +import { FriendsComponent, FriendAddComponent, FriendListComponent, FriendService } from './friends'; +import { RequestsComponent, RequestStatsComponent, RequestService } from './requests'; +import { UsersComponent, UserAddComponent, UserListComponent, UserService } from './users'; +import { MenuAdminComponent } from './menu-admin.component'; +import { SharedModule } from '../shared'; + +@NgModule({ + imports: [ + AdminRoutingModule, + SharedModule + ], + + declarations: [ + AdminComponent, + + FriendsComponent, + FriendAddComponent, + FriendListComponent, + + RequestsComponent, + RequestStatsComponent, + + UsersComponent, + UserAddComponent, + UserListComponent, + + MenuAdminComponent + ], + + exports: [ + AdminComponent, + MenuAdminComponent + ], + + providers: [ + FriendService, + RequestService, + UserService + ] +}) +export class AdminModule { } diff --git a/client/src/app/admin/admin.routes.ts b/client/src/app/admin/admin.routes.ts deleted file mode 100644 index edb8ba49f..000000000 --- a/client/src/app/admin/admin.routes.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Routes } from '@angular/router'; - -import { AdminComponent } from './admin.component'; -import { FriendsRoutes } from './friends'; -import { RequestsRoutes } from './requests'; -import { UsersRoutes } from './users'; - -export const AdminRoutes: Routes = [ - { - path: 'admin', - component: AdminComponent, - children: [ - { - path: '', - redirectTo: 'users', - pathMatch: 'full' - }, - ...FriendsRoutes, - ...RequestsRoutes, - ...UsersRoutes - ] - } -]; diff --git a/client/src/app/admin/index.ts b/client/src/app/admin/index.ts index 493caed15..b75ff9b51 100644 --- a/client/src/app/admin/index.ts +++ b/client/src/app/admin/index.ts @@ -1,6 +1,7 @@ export * from './friends'; export * from './requests'; export * from './users'; +export * from './admin-routing.module'; +export * from './admin.module'; export * from './admin.component'; -export * from './admin.routes'; export * from './menu-admin.component'; diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts new file mode 100644 index 000000000..900a4c5b6 --- /dev/null +++ b/client/src/app/app-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +const routes: Routes = [ + { + path: '', + redirectTo: '/videos/list', + pathMatch: 'full' + } +]; + +@NgModule({ + imports: [ RouterModule.forRoot(routes) ], + exports: [ RouterModule ] +}) +export class AppRoutingModule {} + diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index 3f57897fb..555f412e7 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -1,62 +1,22 @@ import { ApplicationRef, NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { HttpModule, RequestOptions, XHRBackend } from '@angular/http'; -import { RouterModule } from '@angular/router'; import { removeNgStyles, createNewHosts } from '@angularclass/hmr'; -import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe'; +import { MetaModule, MetaConfig } from 'ng2-meta'; -import { DropdownModule } from 'ng2-bootstrap/components/dropdown'; -import { ProgressbarModule } from 'ng2-bootstrap/components/progressbar'; -import { PaginationModule } from 'ng2-bootstrap/components/pagination'; -import { ModalModule } from 'ng2-bootstrap/components/modal'; - -import { FileUploadModule } from 'ng2-file-upload/ng2-file-upload'; - -import { MetaConfig, MetaModule } from 'ng2-meta'; - -/* - * Platform and Environment providers/directives/pipes - */ import { ENV_PROVIDERS } from './environment'; -import { routes } from './app.routes'; -// App is our top level component +import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { AppState } from './app.service'; -import { - AdminComponent, - FriendsComponent, - FriendAddComponent, - FriendListComponent, - FriendService, - MenuAdminComponent, - RequestsComponent, - RequestStatsComponent, - RequestService, - UsersComponent, - UserAddComponent, - UserListComponent, - UserService -} from './admin'; -import { AccountComponent, AccountService } from './account'; -import { LoginComponent } from './login'; +import { AccountModule } from './account'; +import { AdminModule } from './admin'; +import { CoreModule } from './core'; +import { LoginModule } from './login'; +import { SharedModule } from './shared'; +import { VideosModule } from './videos'; + import { MenuComponent } from './menu.component'; -import { AuthService, AuthHttp, RestExtractor, RestService, SearchComponent, SearchService } from './shared'; -import { - LoaderComponent, - VideosComponent, - VideoAddComponent, - VideoListComponent, - VideoMiniatureComponent, - VideoSortComponent, - VideoWatchComponent, - VideoShareComponent, - VideoMagnetComponent, - VideoService, - WebTorrentService -} from './videos'; const metaConfig: MetaConfig = { //Append a title suffix such as a site name to all titles @@ -69,75 +29,31 @@ const metaConfig: MetaConfig = { // Application wide providers const APP_PROVIDERS = [ - AppState, - - { - provide: AuthHttp, - useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, authService: AuthService) => { - return new AuthHttp(backend, defaultOptions, authService); - }, - deps: [ XHRBackend, RequestOptions, AuthService ] - }, - - AuthService, - RestExtractor, - RestService, - - VideoService, - SearchService, - FriendService, - RequestService, - UserService, - AccountService, - WebTorrentService + AppState ]; -/** - * `AppModule` is the main entry point into Angular2's bootstraping process - */ + @NgModule({ bootstrap: [ AppComponent ], declarations: [ - AccountComponent, - AdminComponent, AppComponent, - BytesPipe, - FriendAddComponent, - FriendListComponent, - FriendsComponent, - LoaderComponent, - LoginComponent, - MenuAdminComponent, - MenuComponent, - RequestsComponent, - RequestStatsComponent, - SearchComponent, - UserAddComponent, - UserListComponent, - UsersComponent, - VideoAddComponent, - VideoListComponent, - VideoMiniatureComponent, - VideosComponent, - VideoSortComponent, - VideoWatchComponent, - VideoShareComponent, - VideoMagnetComponent + MenuComponent ], - imports: [ // import Angular's modules + imports: [ BrowserModule, - FormsModule, - ReactiveFormsModule, - HttpModule, - RouterModule.forRoot(routes), - DropdownModule, - ProgressbarModule, - PaginationModule, - ModalModule, + CoreModule, + SharedModule, + + AppRoutingModule, - FileUploadModule, + MetaModule.forRoot(metaConfig), - MetaModule.forRoot(metaConfig) + AccountModule, + AdminModule, + CoreModule, + LoginModule, + SharedModule, + VideosModule ], providers: [ // expose our Services and Providers into Angular's dependency injection ENV_PROVIDERS, diff --git a/client/src/app/app.routes.ts b/client/src/app/app.routes.ts deleted file mode 100644 index 03e2bce51..000000000 --- a/client/src/app/app.routes.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Routes } from '@angular/router'; - -import { AccountRoutes } from './account'; -import { LoginRoutes } from './login'; -import { AdminRoutes } from './admin'; -import { VideosRoutes } from './videos'; - -export const routes: Routes = [ - { - path: '', - redirectTo: '/videos/list', - pathMatch: 'full' - }, - ...AdminRoutes, - ...AccountRoutes, - ...LoginRoutes, - ...VideosRoutes -]; diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts new file mode 100644 index 000000000..1f0e322a9 --- /dev/null +++ b/client/src/app/core/auth/auth.service.ts @@ -0,0 +1,213 @@ +import { Injectable } from '@angular/core'; +import { Headers, Http, Response, URLSearchParams } from '@angular/http'; +import { Router } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; + +// Do not use the barrel (dependency loop) +import { AuthStatus } from '../../shared/auth/auth-status.model'; +import { AuthUser } from '../../shared/auth/auth-user.model'; +import { RestExtractor } from '../../shared/rest'; + +@Injectable() +export class AuthService { + private static BASE_CLIENT_URL = '/api/v1/clients/local'; + private static BASE_TOKEN_URL = '/api/v1/users/token'; + private static BASE_USER_INFORMATIONS_URL = '/api/v1/users/me'; + + loginChangedSource: Observable; + + private clientId: string; + private clientSecret: string; + private loginChanged: Subject; + private user: AuthUser = null; + + constructor( + private http: Http, + private restExtractor: RestExtractor, + private router: Router + ) { + this.loginChanged = new Subject(); + this.loginChangedSource = this.loginChanged.asObservable(); + + // Fetch the client_id/client_secret + // FIXME: save in local storage? + this.http.get(AuthService.BASE_CLIENT_URL) + .map(this.restExtractor.extractDataGet) + .catch((res) => this.restExtractor.handleError(res)) + .subscribe( + result => { + this.clientId = result.client_id; + this.clientSecret = result.client_secret; + console.log('Client credentials loaded.'); + }, + error => { + alert( + `Cannot retrieve OAuth Client credentials: ${error.text}. \n` + + 'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.' + ); + } + ); + + // Return null if there is nothing to load + this.user = AuthUser.load(); + } + + getRefreshToken() { + if (this.user === null) return null; + + return this.user.getRefreshToken(); + } + + getRequestHeaderValue() { + return `${this.getTokenType()} ${this.getAccessToken()}`; + } + + getAccessToken() { + if (this.user === null) return null; + + return this.user.getAccessToken(); + } + + getTokenType() { + if (this.user === null) return null; + + return this.user.getTokenType(); + } + + getUser(): AuthUser { + return this.user; + } + + isAdmin() { + if (this.user === null) return false; + + return this.user.isAdmin(); + } + + isLoggedIn() { + if (this.getAccessToken()) { + return true; + } else { + return false; + } + } + + login(username: string, password: string) { + let body = new URLSearchParams(); + body.set('client_id', this.clientId); + body.set('client_secret', this.clientSecret); + body.set('response_type', 'code'); + body.set('grant_type', 'password'); + body.set('scope', 'upload'); + body.set('username', username); + body.set('password', password); + + let headers = new Headers(); + headers.append('Content-Type', 'application/x-www-form-urlencoded'); + + let options = { + headers: headers + }; + + return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) + .map(this.restExtractor.extractDataGet) + .map(res => { + res.username = username; + return res; + }) + .flatMap(res => this.fetchUserInformations(res)) + .map(res => this.handleLogin(res)) + .catch((res) => this.restExtractor.handleError(res)); + } + + logout() { + // TODO: make an HTTP request to revoke the tokens + this.user = null; + + AuthUser.flush(); + + this.setStatus(AuthStatus.LoggedOut); + } + + refreshAccessToken() { + console.log('Refreshing token...'); + + const refreshToken = this.getRefreshToken(); + + let body = new URLSearchParams(); + body.set('refresh_token', refreshToken); + body.set('client_id', this.clientId); + body.set('client_secret', this.clientSecret); + body.set('response_type', 'code'); + body.set('grant_type', 'refresh_token'); + + let headers = new Headers(); + headers.append('Content-Type', 'application/x-www-form-urlencoded'); + + let options = { + headers: headers + }; + + return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) + .map(this.restExtractor.extractDataGet) + .map(res => this.handleRefreshToken(res)) + .catch((res: Response) => { + // The refresh token is invalid? + if (res.status === 400 && res.json() && res.json().error === 'invalid_grant') { + console.error('Cannot refresh token -> logout...'); + this.logout(); + this.router.navigate(['/login']); + + return Observable.throw({ + json: () => '', + text: () => 'You need to reconnect.' + }); + } + + return this.restExtractor.handleError(res); + }); + } + + private fetchUserInformations (obj: any) { + // Do not call authHttp here to avoid circular dependencies headaches + + const headers = new Headers(); + headers.set('Authorization', `Bearer ${obj.access_token}`); + + return this.http.get(AuthService.BASE_USER_INFORMATIONS_URL, { headers }) + .map(res => res.json()) + .map(res => { + obj.id = res.id; + obj.role = res.role; + return obj; + } + ); + } + + private handleLogin (obj: any) { + const id = obj.id; + const username = obj.username; + const role = obj.role; + const hashTokens = { + access_token: obj.access_token, + token_type: obj.token_type, + refresh_token: obj.refresh_token + }; + + this.user = new AuthUser({ id, username, role }, hashTokens); + this.user.save(); + + this.setStatus(AuthStatus.LoggedIn); + } + + private handleRefreshToken (obj: any) { + this.user.refreshTokens(obj.access_token, obj.refresh_token); + this.user.save(); + } + + private setStatus(status: AuthStatus) { + this.loginChanged.next(status); + } + +} diff --git a/client/src/app/core/auth/index.ts b/client/src/app/core/auth/index.ts new file mode 100644 index 000000000..cf52c9c7c --- /dev/null +++ b/client/src/app/core/auth/index.ts @@ -0,0 +1 @@ +export * from './auth.service' diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts new file mode 100644 index 000000000..be29b88da --- /dev/null +++ b/client/src/app/core/core.module.ts @@ -0,0 +1,21 @@ +import { NgModule, Optional, SkipSelf } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { HttpModule } from '@angular/http'; + +import { AuthService } from './auth'; +import { throwIfAlreadyLoaded } from './module-import-guard'; + +@NgModule({ + imports: [ + CommonModule, + HttpModule + ], + declarations: [ ], + exports: [ ], + providers: [ AuthService ] +}) +export class CoreModule { + constructor( @Optional() @SkipSelf() parentModule: CoreModule) { + throwIfAlreadyLoaded(parentModule, 'CoreModule'); + } +} diff --git a/client/src/app/core/index.ts b/client/src/app/core/index.ts new file mode 100644 index 000000000..fa951b215 --- /dev/null +++ b/client/src/app/core/index.ts @@ -0,0 +1,2 @@ +export * from './auth'; +export * from './core.module' diff --git a/client/src/app/core/module-import-guard.ts b/client/src/app/core/module-import-guard.ts new file mode 100644 index 000000000..445640c4f --- /dev/null +++ b/client/src/app/core/module-import-guard.ts @@ -0,0 +1,5 @@ +export function throwIfAlreadyLoaded(parentModule: any, moduleName: string) { + if (parentModule) { + throw new Error(`${moduleName} has already been loaded. Import Core modules in the AppModule only.`); + } +} diff --git a/client/src/app/login/index.ts b/client/src/app/login/index.ts index 64d3f2a3c..5639915f7 100644 --- a/client/src/app/login/index.ts +++ b/client/src/app/login/index.ts @@ -1,2 +1,3 @@ +export * from './login-routing.module'; export * from './login.component'; -export * from './login.routes'; +export * from './login.module'; diff --git a/client/src/app/login/login-routing.module.ts b/client/src/app/login/login-routing.module.ts new file mode 100644 index 000000000..da68519bd --- /dev/null +++ b/client/src/app/login/login-routing.module.ts @@ -0,0 +1,22 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { LoginComponent } from './login.component'; + +const loginRoutes: Routes = [ + { + path: 'login', + component: LoginComponent, + data: { + meta: { + titleSuffix: ' - Login' + } + } + } +]; + +@NgModule({ + imports: [ RouterModule.forChild(loginRoutes) ], + exports: [ RouterModule ] +}) +export class LoginRoutingModule {} diff --git a/client/src/app/login/login.component.ts b/client/src/app/login/login.component.ts index c4ff7050b..fd4a314cc 100644 --- a/client/src/app/login/login.component.ts +++ b/client/src/app/login/login.component.ts @@ -2,7 +2,8 @@ import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; -import { AuthService, FormReactive } from '../shared'; +import { AuthService } from '../core'; +import { FormReactive } from '../shared'; @Component({ selector: 'my-login', diff --git a/client/src/app/login/login.module.ts b/client/src/app/login/login.module.ts new file mode 100644 index 000000000..31a723b43 --- /dev/null +++ b/client/src/app/login/login.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; + +import { LoginRoutingModule } from './login-routing.module'; +import { LoginComponent } from './login.component'; +import { SharedModule } from '../shared'; + +@NgModule({ + imports: [ + LoginRoutingModule, + SharedModule + ], + + declarations: [ + LoginComponent + ], + + exports: [ + LoginComponent + ], + + providers: [ + ] +}) +export class LoginModule { } diff --git a/client/src/app/login/login.routes.ts b/client/src/app/login/login.routes.ts deleted file mode 100644 index 2f63af5e2..000000000 --- a/client/src/app/login/login.routes.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { LoginComponent } from './login.component'; - -export const LoginRoutes = [ - { - path: 'login', - component: LoginComponent, - data: { - meta: { - titleSuffix: ' - Login' - } - } - } -]; diff --git a/client/src/app/menu.component.ts b/client/src/app/menu.component.ts index 6cfc854dd..d1a1d51e7 100644 --- a/client/src/app/menu.component.ts +++ b/client/src/app/menu.component.ts @@ -1,7 +1,8 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { AuthService, AuthStatus } from './shared'; +import { AuthService } from './core'; +import { AuthStatus } from './shared'; @Component({ selector: 'my-menu', diff --git a/client/src/app/shared/auth/auth-http.service.ts b/client/src/app/shared/auth/auth-http.service.ts index 2392898ca..602726570 100644 --- a/client/src/app/shared/auth/auth-http.service.ts +++ b/client/src/app/shared/auth/auth-http.service.ts @@ -7,11 +7,12 @@ import { RequestMethod, RequestOptions, RequestOptionsArgs, - Response + Response, + XHRBackend } from '@angular/http'; import { Observable } from 'rxjs/Observable'; -import { AuthService } from './auth.service'; +import { AuthService } from '../../core'; @Injectable() export class AuthHttp extends Http { @@ -78,3 +79,13 @@ export class AuthHttp extends Http { headers.set('Authorization', this.authService.getRequestHeaderValue()); } } + +export const AUTH_HTTP_PROVIDERS = [ + { + provide: AuthHttp, + useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, authService: AuthService) => { + return new AuthHttp(backend, defaultOptions, authService); + }, + deps: [ XHRBackend, RequestOptions, AuthService ] + }, +]; diff --git a/client/src/app/shared/auth/auth.service.ts b/client/src/app/shared/auth/auth.service.ts deleted file mode 100644 index b7e0a44a7..000000000 --- a/client/src/app/shared/auth/auth.service.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Headers, Http, Response, URLSearchParams } from '@angular/http'; -import { Router } from '@angular/router'; -import { Observable } from 'rxjs/Observable'; -import { Subject } from 'rxjs/Subject'; - -import { AuthStatus } from './auth-status.model'; -import { AuthUser } from './auth-user.model'; -import { RestExtractor } from '../rest'; - -@Injectable() -export class AuthService { - private static BASE_CLIENT_URL = '/api/v1/clients/local'; - private static BASE_TOKEN_URL = '/api/v1/users/token'; - private static BASE_USER_INFORMATIONS_URL = '/api/v1/users/me'; - - loginChangedSource: Observable; - - private clientId: string; - private clientSecret: string; - private loginChanged: Subject; - private user: AuthUser = null; - - constructor( - private http: Http, - private restExtractor: RestExtractor, - private router: Router - ) { - this.loginChanged = new Subject(); - this.loginChangedSource = this.loginChanged.asObservable(); - - // Fetch the client_id/client_secret - // FIXME: save in local storage? - this.http.get(AuthService.BASE_CLIENT_URL) - .map(this.restExtractor.extractDataGet) - .catch((res) => this.restExtractor.handleError(res)) - .subscribe( - result => { - this.clientId = result.client_id; - this.clientSecret = result.client_secret; - console.log('Client credentials loaded.'); - }, - error => { - alert( - `Cannot retrieve OAuth Client credentials: ${error.text}. \n` + - 'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.' - ); - } - ); - - // Return null if there is nothing to load - this.user = AuthUser.load(); - } - - getRefreshToken() { - if (this.user === null) return null; - - return this.user.getRefreshToken(); - } - - getRequestHeaderValue() { - return `${this.getTokenType()} ${this.getAccessToken()}`; - } - - getAccessToken() { - if (this.user === null) return null; - - return this.user.getAccessToken(); - } - - getTokenType() { - if (this.user === null) return null; - - return this.user.getTokenType(); - } - - getUser(): AuthUser { - return this.user; - } - - isAdmin() { - if (this.user === null) return false; - - return this.user.isAdmin(); - } - - isLoggedIn() { - if (this.getAccessToken()) { - return true; - } else { - return false; - } - } - - login(username: string, password: string) { - let body = new URLSearchParams(); - body.set('client_id', this.clientId); - body.set('client_secret', this.clientSecret); - body.set('response_type', 'code'); - body.set('grant_type', 'password'); - body.set('scope', 'upload'); - body.set('username', username); - body.set('password', password); - - let headers = new Headers(); - headers.append('Content-Type', 'application/x-www-form-urlencoded'); - - let options = { - headers: headers - }; - - return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) - .map(this.restExtractor.extractDataGet) - .map(res => { - res.username = username; - return res; - }) - .flatMap(res => this.fetchUserInformations(res)) - .map(res => this.handleLogin(res)) - .catch((res) => this.restExtractor.handleError(res)); - } - - logout() { - // TODO: make an HTTP request to revoke the tokens - this.user = null; - - AuthUser.flush(); - - this.setStatus(AuthStatus.LoggedOut); - } - - refreshAccessToken() { - console.log('Refreshing token...'); - - const refreshToken = this.getRefreshToken(); - - let body = new URLSearchParams(); - body.set('refresh_token', refreshToken); - body.set('client_id', this.clientId); - body.set('client_secret', this.clientSecret); - body.set('response_type', 'code'); - body.set('grant_type', 'refresh_token'); - - let headers = new Headers(); - headers.append('Content-Type', 'application/x-www-form-urlencoded'); - - let options = { - headers: headers - }; - - return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) - .map(this.restExtractor.extractDataGet) - .map(res => this.handleRefreshToken(res)) - .catch((res: Response) => { - // The refresh token is invalid? - if (res.status === 400 && res.json() && res.json().error === 'invalid_grant') { - console.error('Cannot refresh token -> logout...'); - this.logout(); - this.router.navigate(['/login']); - - return Observable.throw({ - json: () => '', - text: () => 'You need to reconnect.' - }); - } - - return this.restExtractor.handleError(res); - }); - } - - private fetchUserInformations (obj: any) { - // Do not call authHttp here to avoid circular dependencies headaches - - const headers = new Headers(); - headers.set('Authorization', `Bearer ${obj.access_token}`); - - return this.http.get(AuthService.BASE_USER_INFORMATIONS_URL, { headers }) - .map(res => res.json()) - .map(res => { - obj.id = res.id; - obj.role = res.role; - return obj; - } - ); - } - - private handleLogin (obj: any) { - const id = obj.id; - const username = obj.username; - const role = obj.role; - const hashTokens = { - access_token: obj.access_token, - token_type: obj.token_type, - refresh_token: obj.refresh_token - }; - - this.user = new AuthUser({ id, username, role }, hashTokens); - this.user.save(); - - this.setStatus(AuthStatus.LoggedIn); - } - - private handleRefreshToken (obj: any) { - this.user.refreshTokens(obj.access_token, obj.refresh_token); - this.user.save(); - } - - private setStatus(status: AuthStatus) { - this.loginChanged.next(status); - } - -} diff --git a/client/src/app/shared/auth/index.ts b/client/src/app/shared/auth/index.ts index ebd9e14cd..ce0bd8adf 100644 --- a/client/src/app/shared/auth/index.ts +++ b/client/src/app/shared/auth/index.ts @@ -1,4 +1,3 @@ export * from './auth-http.service'; export * from './auth-status.model'; -export * from './auth.service'; export * from './auth-user.model'; diff --git a/client/src/app/shared/index.ts b/client/src/app/shared/index.ts index af34b4b64..52a647b83 100644 --- a/client/src/app/shared/index.ts +++ b/client/src/app/shared/index.ts @@ -3,3 +3,4 @@ export * from './forms'; export * from './rest'; export * from './search'; export * from './users'; +export * from './shared.module'; diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts new file mode 100644 index 000000000..141922322 --- /dev/null +++ b/client/src/app/shared/shared.module.ts @@ -0,0 +1,62 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { HttpModule } from '@angular/http'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterModule } from '@angular/router'; + +import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe'; +import { DropdownModule } from 'ng2-bootstrap/components/dropdown'; +import { ProgressbarModule } from 'ng2-bootstrap/components/progressbar'; +import { PaginationModule } from 'ng2-bootstrap/components/pagination'; +import { ModalModule } from 'ng2-bootstrap/components/modal'; +import { FileUploadModule } from 'ng2-file-upload/ng2-file-upload'; + +import { AUTH_HTTP_PROVIDERS } from './auth'; +import { RestExtractor, RestService } from './rest'; +import { SearchComponent, SearchService } from './search'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + HttpModule, + RouterModule, + + DropdownModule, + FileUploadModule, + ModalModule, + PaginationModule, + ProgressbarModule + ], + + declarations: [ + BytesPipe, + SearchComponent + ], + + exports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + HttpModule, + RouterModule, + + DropdownModule, + FileUploadModule, + ModalModule, + PaginationModule, + ProgressbarModule, + BytesPipe, + + SearchComponent + ], + + providers: [ + AUTH_HTTP_PROVIDERS, + RestExtractor, + RestService, + SearchService + ] +}) +export class SharedModule { } diff --git a/client/src/app/videos/index.ts b/client/src/app/videos/index.ts index a9088a907..ca386a51d 100644 --- a/client/src/app/videos/index.ts +++ b/client/src/app/videos/index.ts @@ -2,5 +2,6 @@ export * from './shared'; export * from './video-add'; export * from './video-list'; export * from './video-watch'; +export * from './videos-routing.module'; export * from './videos.component'; -export * from './videos.routes'; +export * from './videos.module'; diff --git a/client/src/app/videos/shared/video.service.ts b/client/src/app/videos/shared/video.service.ts index b1f688095..f173ef06b 100644 --- a/client/src/app/videos/shared/video.service.ts +++ b/client/src/app/videos/shared/video.service.ts @@ -4,7 +4,8 @@ import { Observable } from 'rxjs/Observable'; import { Search } from '../../shared'; import { SortField } from './sort-field.type'; -import { AuthHttp, AuthService, RestExtractor, RestPagination, RestService, ResultList } from '../../shared'; +import { AuthService } from '../../core'; +import { AuthHttp, RestExtractor, RestPagination, RestService, ResultList } from '../../shared'; import { Video } from './video.model'; @Injectable() diff --git a/client/src/app/videos/video-add/video-add.component.ts b/client/src/app/videos/video-add/video-add.component.ts index b7bf1534f..6eab54f7e 100644 --- a/client/src/app/videos/video-add/video-add.component.ts +++ b/client/src/app/videos/video-add/video-add.component.ts @@ -4,7 +4,8 @@ import { Router } from '@angular/router'; import { FileUploader } from 'ng2-file-upload/ng2-file-upload'; -import { AuthService, FormReactive, VIDEO_NAME, VIDEO_DESCRIPTION, VIDEO_TAGS } from '../../shared'; +import { AuthService } from '../../core'; +import { FormReactive, VIDEO_NAME, VIDEO_DESCRIPTION, VIDEO_TAGS } from '../../shared'; @Component({ selector: 'my-videos-add', diff --git a/client/src/app/videos/video-list/video-list.component.ts b/client/src/app/videos/video-list/video-list.component.ts index 6b086e938..a8b92480b 100644 --- a/client/src/app/videos/video-list/video-list.component.ts +++ b/client/src/app/videos/video-list/video-list.component.ts @@ -7,7 +7,8 @@ import { Video, VideoService } from '../shared'; -import { AuthService, AuthUser, RestPagination, Search, SearchField } from '../../shared'; +import { AuthService } from '../../core'; +import { AuthUser, RestPagination, Search, SearchField } from '../../shared'; import { SearchService } from '../../shared'; @Component({ diff --git a/client/src/app/videos/videos-routing.module.ts b/client/src/app/videos/videos-routing.module.ts new file mode 100644 index 000000000..766d29d22 --- /dev/null +++ b/client/src/app/videos/videos-routing.module.ts @@ -0,0 +1,44 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { VideoAddComponent } from './video-add'; +import { VideoListComponent } from './video-list'; +import { VideosComponent } from './videos.component'; +import { VideoWatchComponent } from './video-watch'; + +const videosRoutes: Routes = [ + { + path: 'videos', + component: VideosComponent, + children: [ + { + path: 'list', + component: VideoListComponent, + data: { + meta: { + titleSuffix: ' - Videos list' + } + } + }, + { + path: 'add', + component: VideoAddComponent, + data: { + meta: { + titleSuffix: ' - Add a video' + } + } + }, + { + path: 'watch/:id', + component: VideoWatchComponent + } + ] + } +]; + +@NgModule({ + imports: [ RouterModule.forChild(videosRoutes) ], + exports: [ RouterModule ] +}) +export class VideosRoutingModule {} diff --git a/client/src/app/videos/videos.module.ts b/client/src/app/videos/videos.module.ts new file mode 100644 index 000000000..fb2f453b0 --- /dev/null +++ b/client/src/app/videos/videos.module.ts @@ -0,0 +1,42 @@ +import { NgModule } from '@angular/core'; + +import { VideosRoutingModule } from './videos-routing.module'; +import { VideosComponent } from './videos.component'; +import { VideoAddComponent } from './video-add'; +import { VideoListComponent, VideoMiniatureComponent, VideoSortComponent } from './video-list'; +import { VideoWatchComponent, VideoMagnetComponent, VideoShareComponent, WebTorrentService } from './video-watch'; +import { LoaderComponent, VideoService } from './shared'; +import { SharedModule } from '../shared'; + +@NgModule({ + imports: [ + VideosRoutingModule, + SharedModule + ], + + declarations: [ + VideosComponent, + + VideoAddComponent, + + VideoListComponent, + VideoMiniatureComponent, + VideoSortComponent, + + VideoWatchComponent, + VideoMagnetComponent, + VideoShareComponent, + + LoaderComponent + ], + + exports: [ + VideosComponent + ], + + providers: [ + VideoService, + WebTorrentService + ] +}) +export class VideosModule { } diff --git a/client/src/app/videos/videos.routes.ts b/client/src/app/videos/videos.routes.ts deleted file mode 100644 index ab68fbe0c..000000000 --- a/client/src/app/videos/videos.routes.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Routes } from '@angular/router'; - -import { VideoAddComponent } from './video-add'; -import { VideoListComponent } from './video-list'; -import { VideosComponent } from './videos.component'; -import { VideoWatchComponent } from './video-watch'; - -export const VideosRoutes: Routes = [ - { - path: 'videos', - component: VideosComponent, - children: [ - { - path: 'list', - component: VideoListComponent, - data: { - meta: { - titleSuffix: ' - Videos list' - } - } - }, - { - path: 'add', - component: VideoAddComponent, - data: { - meta: { - titleSuffix: ' - Add a video' - } - } - }, - { - path: 'watch/:id', - component: VideoWatchComponent - } - ] - } -];