Client: split in angular modules
authorChocobozzz <florian.bigard@gmail.com>
Sun, 20 Nov 2016 16:18:15 +0000 (17:18 +0100)
committerChocobozzz <florian.bigard@gmail.com>
Mon, 21 Nov 2016 21:09:45 +0000 (22:09 +0100)
36 files changed:
client/package.json
client/src/app/account/account-routing.module.ts [new file with mode: 0644]
client/src/app/account/account.module.ts [new file with mode: 0644]
client/src/app/account/account.routes.ts [deleted file]
client/src/app/account/account.service.ts
client/src/app/account/index.ts
client/src/app/admin/admin-routing.module.ts [new file with mode: 0644]
client/src/app/admin/admin.module.ts [new file with mode: 0644]
client/src/app/admin/admin.routes.ts [deleted file]
client/src/app/admin/index.ts
client/src/app/app-routing.module.ts [new file with mode: 0644]
client/src/app/app.module.ts
client/src/app/app.routes.ts [deleted file]
client/src/app/core/auth/auth.service.ts [new file with mode: 0644]
client/src/app/core/auth/index.ts [new file with mode: 0644]
client/src/app/core/core.module.ts [new file with mode: 0644]
client/src/app/core/index.ts [new file with mode: 0644]
client/src/app/core/module-import-guard.ts [new file with mode: 0644]
client/src/app/login/index.ts
client/src/app/login/login-routing.module.ts [new file with mode: 0644]
client/src/app/login/login.component.ts
client/src/app/login/login.module.ts [new file with mode: 0644]
client/src/app/login/login.routes.ts [deleted file]
client/src/app/menu.component.ts
client/src/app/shared/auth/auth-http.service.ts
client/src/app/shared/auth/auth.service.ts [deleted file]
client/src/app/shared/auth/index.ts
client/src/app/shared/index.ts
client/src/app/shared/shared.module.ts [new file with mode: 0644]
client/src/app/videos/index.ts
client/src/app/videos/shared/video.service.ts
client/src/app/videos/video-add/video-add.component.ts
client/src/app/videos/video-list/video-list.component.ts
client/src/app/videos/videos-routing.module.ts [new file with mode: 0644]
client/src/app/videos/videos.module.ts [new file with mode: 0644]
client/src/app/videos/videos.routes.ts [deleted file]

index bce442c3812a7bf2d13dad787243334bb042e73c..c767787a7b048384450fb23361be2421c559799d 100644 (file)
@@ -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 (file)
index 0000000..8c1033c
--- /dev/null
@@ -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 (file)
index 0000000..53f6ba5
--- /dev/null
@@ -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 (file)
index c382a6d..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-import { AccountComponent } from './account.component';
-
-export const AccountRoutes = [
-  {
-    path: 'account',
-    component: AccountComponent,
-    data: {
-      meta: {
-        titleSuffix: ' - My account'
-      }
-    }
-  }
-];
index 355bcef742ebd9fffca1cefb27bd440e3551133f..0635c253331f33b02ebd3b873e2753061e021fb3 100644 (file)
@@ -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 {
index 823d9fe5fd95faee4b514602d4483d22459f221a..be03713cb7fb37320606717b608d40fe3d0abb29 100644 (file)
@@ -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 (file)
index 0000000..6bff250
--- /dev/null
@@ -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 (file)
index 0000000..63d99a3
--- /dev/null
@@ -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 (file)
index edb8ba4..0000000
+++ /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
-    ]
-  }
-];
index 493caed1576635d2a7937ad85d4afc349985706a..b75ff9b51c0b54512ae5d2e8cc06651749186ebc 100644 (file)
@@ -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 (file)
index 0000000..900a4c5
--- /dev/null
@@ -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 {}
+
index 3f57897fbd9edec8c3a0a0709600d787b6e63d8b..555f412e7c44f41a6ce994994a22f73dbe54050e 100644 (file)
@@ -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 (file)
index 03e2bce..0000000
+++ /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 (file)
index 0000000..1f0e322
--- /dev/null
@@ -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<AuthStatus>;
+
+  private clientId: string;
+  private clientSecret: string;
+  private loginChanged: Subject<AuthStatus>;
+  private user: AuthUser = null;
+
+  constructor(
+    private http: Http,
+    private restExtractor: RestExtractor,
+    private router: Router
+   ) {
+    this.loginChanged = new Subject<AuthStatus>();
+    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 (file)
index 0000000..cf52c9c
--- /dev/null
@@ -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 (file)
index 0000000..be29b88
--- /dev/null
@@ -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 (file)
index 0000000..fa951b2
--- /dev/null
@@ -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 (file)
index 0000000..445640c
--- /dev/null
@@ -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.`);
+  }
+}
index 64d3f2a3c8ee7ec7bc0cb109e91864eacbc0c7a1..5639915f7c5f7f6c8c6ab777a7c14a6826ccbbbe 100644 (file)
@@ -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 (file)
index 0000000..da68519
--- /dev/null
@@ -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 {}
index c4ff7050bf1f422301180e31d1e645421844ac91..fd4a314ccf18734561ab2d059d91ecb1d3284b43 100644 (file)
@@ -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 (file)
index 0000000..31a723b
--- /dev/null
@@ -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 (file)
index 2f63af5..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-import { LoginComponent } from './login.component';
-
-export const LoginRoutes = [
-  {
-    path: 'login',
-    component: LoginComponent,
-    data: {
-      meta: {
-        titleSuffix: ' - Login'
-      }
-    }
-  }
-];
index 6cfc854dde757801c4fe165a0305c56e4edc391b..d1a1d51e7ddc76195e558751f0ddaf6aedd3fbad 100644 (file)
@@ -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',
index 2392898cae43521a56bcb348af5ea84d5c09ec09..6027265705eac94a7b5f27df78eb5e29d15fe664 100644 (file)
@@ -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 (file)
index b7e0a44..0000000
+++ /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<AuthStatus>;
-
-  private clientId: string;
-  private clientSecret: string;
-  private loginChanged: Subject<AuthStatus>;
-  private user: AuthUser = null;
-
-  constructor(
-    private http: Http,
-    private restExtractor: RestExtractor,
-    private router: Router
-   ) {
-    this.loginChanged = new Subject<AuthStatus>();
-    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);
-  }
-
-}
index ebd9e14cd48e20895b2361dddccab49d40213154..ce0bd8adfc240834d4bb11ed894f96083dce1930 100644 (file)
@@ -1,4 +1,3 @@
 export * from './auth-http.service';
 export * from './auth-status.model';
-export * from './auth.service';
 export * from './auth-user.model';
index af34b4b64da57cad4e724a699e887ef7c7257d1b..52a647b83c1bf5bca8f1928f70dd1146005ff19e 100644 (file)
@@ -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 (file)
index 0000000..1419223
--- /dev/null
@@ -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 { }
index a9088a907a1bd4c9a78c5a4b1af97135334d1f60..ca386a51df51e63f80adbcd2ee233f1859466c57 100644 (file)
@@ -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';
index b1f6880954d95a8ce309bd53260b140dde231f69..f173ef06be5fdf01a61fdc0118e09eda43ccc6d4 100644 (file)
@@ -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()
index b7bf1534f9ee13b5c6c446d1b3e423e60f10537d..6eab54f7e3388b9be0ab4f082402e60007c0d49f 100644 (file)
@@ -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',
index 6b086e93880b34cee8c2fba58f4878406b215d7a..a8b92480b537d13542d5925c5583e7867b94d4d6 100644 (file)
@@ -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 (file)
index 0000000..766d29d
--- /dev/null
@@ -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 (file)
index 0000000..fb2f453
--- /dev/null
@@ -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 (file)
index ab68fbe..0000000
+++ /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
-      }
-    ]
-  }
-];