Client: lazy load admin area
authorChocobozzz <florian.bigard@gmail.com>
Mon, 23 Jan 2017 21:32:43 +0000 (22:32 +0100)
committerChocobozzz <florian.bigard@gmail.com>
Mon, 23 Jan 2017 21:32:43 +0000 (22:32 +0100)
104 files changed:
client/config/webpack.common.js
client/package.json
client/src/app/+admin/admin-routing.module.ts [new file with mode: 0644]
client/src/app/+admin/admin.component.ts [new file with mode: 0644]
client/src/app/+admin/admin.module.ts [new file with mode: 0644]
client/src/app/+admin/friends/friend-add/friend-add.component.html [new file with mode: 0644]
client/src/app/+admin/friends/friend-add/friend-add.component.scss [new file with mode: 0644]
client/src/app/+admin/friends/friend-add/friend-add.component.ts [new file with mode: 0644]
client/src/app/+admin/friends/friend-add/index.ts [new file with mode: 0644]
client/src/app/+admin/friends/friend-list/friend-list.component.html [new file with mode: 0644]
client/src/app/+admin/friends/friend-list/friend-list.component.scss [new file with mode: 0644]
client/src/app/+admin/friends/friend-list/friend-list.component.ts [new file with mode: 0644]
client/src/app/+admin/friends/friend-list/index.ts [new file with mode: 0644]
client/src/app/+admin/friends/friends.component.ts [new file with mode: 0644]
client/src/app/+admin/friends/friends.routes.ts [new file with mode: 0644]
client/src/app/+admin/friends/index.ts [new file with mode: 0644]
client/src/app/+admin/friends/shared/friend.model.ts [new file with mode: 0644]
client/src/app/+admin/friends/shared/friend.service.ts [new file with mode: 0644]
client/src/app/+admin/friends/shared/index.ts [new file with mode: 0644]
client/src/app/+admin/index.ts [new file with mode: 0644]
client/src/app/+admin/requests/index.ts [new file with mode: 0644]
client/src/app/+admin/requests/request-stats/index.ts [new file with mode: 0644]
client/src/app/+admin/requests/request-stats/request-stats.component.html [new file with mode: 0644]
client/src/app/+admin/requests/request-stats/request-stats.component.scss [new file with mode: 0644]
client/src/app/+admin/requests/request-stats/request-stats.component.ts [new file with mode: 0644]
client/src/app/+admin/requests/requests.component.ts [new file with mode: 0644]
client/src/app/+admin/requests/requests.routes.ts [new file with mode: 0644]
client/src/app/+admin/requests/shared/index.ts [new file with mode: 0644]
client/src/app/+admin/requests/shared/request-stats.model.ts [new file with mode: 0644]
client/src/app/+admin/requests/shared/request.service.ts [new file with mode: 0644]
client/src/app/+admin/users/index.ts [new file with mode: 0644]
client/src/app/+admin/users/shared/index.ts [new file with mode: 0644]
client/src/app/+admin/users/shared/user.service.ts [new file with mode: 0644]
client/src/app/+admin/users/user-add/index.ts [new file with mode: 0644]
client/src/app/+admin/users/user-add/user-add.component.html [new file with mode: 0644]
client/src/app/+admin/users/user-add/user-add.component.ts [new file with mode: 0644]
client/src/app/+admin/users/user-list/index.ts [new file with mode: 0644]
client/src/app/+admin/users/user-list/user-list.component.html [new file with mode: 0644]
client/src/app/+admin/users/user-list/user-list.component.scss [new file with mode: 0644]
client/src/app/+admin/users/user-list/user-list.component.ts [new file with mode: 0644]
client/src/app/+admin/users/users.component.ts [new file with mode: 0644]
client/src/app/+admin/users/users.routes.ts [new file with mode: 0644]
client/src/app/+admin/video-abuses/index.ts [new file with mode: 0644]
client/src/app/+admin/video-abuses/video-abuse-list/index.ts [new file with mode: 0644]
client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html [new file with mode: 0644]
client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss [new file with mode: 0644]
client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts [new file with mode: 0644]
client/src/app/+admin/video-abuses/video-abuses.component.ts [new file with mode: 0644]
client/src/app/+admin/video-abuses/video-abuses.routes.ts [new file with mode: 0644]
client/src/app/admin/admin-routing.module.ts [deleted file]
client/src/app/admin/admin.component.ts [deleted file]
client/src/app/admin/admin.module.ts [deleted file]
client/src/app/admin/friends/friend-add/friend-add.component.html [deleted file]
client/src/app/admin/friends/friend-add/friend-add.component.scss [deleted file]
client/src/app/admin/friends/friend-add/friend-add.component.ts [deleted file]
client/src/app/admin/friends/friend-add/index.ts [deleted file]
client/src/app/admin/friends/friend-list/friend-list.component.html [deleted file]
client/src/app/admin/friends/friend-list/friend-list.component.scss [deleted file]
client/src/app/admin/friends/friend-list/friend-list.component.ts [deleted file]
client/src/app/admin/friends/friend-list/index.ts [deleted file]
client/src/app/admin/friends/friends.component.ts [deleted file]
client/src/app/admin/friends/friends.routes.ts [deleted file]
client/src/app/admin/friends/index.ts [deleted file]
client/src/app/admin/friends/shared/friend.model.ts [deleted file]
client/src/app/admin/friends/shared/friend.service.ts [deleted file]
client/src/app/admin/friends/shared/index.ts [deleted file]
client/src/app/admin/index.ts [deleted file]
client/src/app/admin/menu-admin.component.html [deleted file]
client/src/app/admin/menu-admin.component.ts [deleted file]
client/src/app/admin/requests/index.ts [deleted file]
client/src/app/admin/requests/request-stats/index.ts [deleted file]
client/src/app/admin/requests/request-stats/request-stats.component.html [deleted file]
client/src/app/admin/requests/request-stats/request-stats.component.scss [deleted file]
client/src/app/admin/requests/request-stats/request-stats.component.ts [deleted file]
client/src/app/admin/requests/requests.component.ts [deleted file]
client/src/app/admin/requests/requests.routes.ts [deleted file]
client/src/app/admin/requests/shared/index.ts [deleted file]
client/src/app/admin/requests/shared/request-stats.model.ts [deleted file]
client/src/app/admin/requests/shared/request.service.ts [deleted file]
client/src/app/admin/users/index.ts [deleted file]
client/src/app/admin/users/shared/index.ts [deleted file]
client/src/app/admin/users/shared/user.service.ts [deleted file]
client/src/app/admin/users/user-add/index.ts [deleted file]
client/src/app/admin/users/user-add/user-add.component.html [deleted file]
client/src/app/admin/users/user-add/user-add.component.ts [deleted file]
client/src/app/admin/users/user-list/index.ts [deleted file]
client/src/app/admin/users/user-list/user-list.component.html [deleted file]
client/src/app/admin/users/user-list/user-list.component.scss [deleted file]
client/src/app/admin/users/user-list/user-list.component.ts [deleted file]
client/src/app/admin/users/users.component.ts [deleted file]
client/src/app/admin/users/users.routes.ts [deleted file]
client/src/app/admin/video-abuses/index.ts [deleted file]
client/src/app/admin/video-abuses/video-abuse-list/index.ts [deleted file]
client/src/app/admin/video-abuses/video-abuse-list/video-abuse-list.component.html [deleted file]
client/src/app/admin/video-abuses/video-abuse-list/video-abuse-list.component.scss [deleted file]
client/src/app/admin/video-abuses/video-abuse-list/video-abuse-list.component.ts [deleted file]
client/src/app/admin/video-abuses/video-abuses.component.ts [deleted file]
client/src/app/admin/video-abuses/video-abuses.routes.ts [deleted file]
client/src/app/app-routing.module.ts
client/src/app/app.module.ts
client/src/app/core/core.module.ts
client/src/app/core/menu/index.ts
client/src/app/core/menu/menu-admin.component.html [new file with mode: 0644]
client/src/app/core/menu/menu-admin.component.ts [new file with mode: 0644]

index 223f55c2e39aab1894b8dca120b4c62c7ef48f1e..08b8a4b09cedf69aa44a3804ba5f984b1e4400c3 100644 (file)
@@ -5,6 +5,7 @@ const helpers = require('./helpers')
  */
 
 const AssetsPlugin = require('assets-webpack-plugin')
+const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
 const NormalModuleReplacementPlugin = require('webpack/lib/NormalModuleReplacementPlugin')
 const ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin')
 const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin')
@@ -313,6 +314,30 @@ module.exports = function (options) {
         disabled: !AOT,
         tsConfig: helpers.root('tsconfig.webpack.json'),
         resourceOverride: helpers.root('config/resource-override.js')
+      }),
+
+      new BundleAnalyzerPlugin({
+        // Can be `server`, `static` or `disabled`.
+        // In `server` mode analyzer will start HTTP server to show bundle report.
+        // In `static` mode single HTML file with bundle report will be generated.
+        // In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`.
+        analyzerMode: 'static',
+        // Path to bundle report file that will be generated in `static` mode.
+        // Relative to bundles output directory.
+        reportFilename: 'report.html',
+        // Automatically open report in default browser
+        openAnalyzer: false,
+        // If `true`, Webpack Stats JSON file will be generated in bundles output directory
+        generateStatsFile: true,
+        // Name of Webpack Stats JSON file that will be generated if `generateStatsFile` is `true`.
+        // Relative to bundles output directory.
+        statsFilename: 'stats.json',
+        // Options for `stats.toJson()` method.
+        // For example you can exclude sources of your modules from stats file with `source: false` option.
+        // See more options here: https://github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
+        statsOptions: null,
+        // Log level. Can be 'info', 'warn', 'error' or 'silent'.
+        logLevel: 'info'
       })
     ],
 
index ea458f1d4ea43b1b4d835f6031be7943fdb90f54..f4835590ee78ed8786767eb64721bab7742c9983 100644 (file)
@@ -88,6 +88,7 @@
     "add-asset-html-webpack-plugin": "^1.0.2",
     "codelyzer": "2.0.0-beta.4",
     "standard": "^8.0.0",
+    "webpack-bundle-analyzer": "^2.2.1",
     "webpack-dll-bundles-plugin": "^1.0.0-beta.5"
   }
 }
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..d3adf3f
--- /dev/null
@@ -0,0 +1,32 @@
+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';
+import { VideoAbusesRoutes } from './video-abuses';
+
+const adminRoutes: Routes = [
+  {
+    path: '',
+    component: AdminComponent,
+    children: [
+      {
+        path: '',
+        redirectTo: 'users',
+        pathMatch: 'full'
+      },
+      ...FriendsRoutes,
+      ...RequestsRoutes,
+      ...UsersRoutes,
+      ...VideoAbusesRoutes
+    ]
+  }
+];
+
+@NgModule({
+  imports: [ RouterModule.forChild(adminRoutes) ],
+  exports: [ RouterModule ]
+})
+export class AdminRoutingModule {}
diff --git a/client/src/app/+admin/admin.component.ts b/client/src/app/+admin/admin.component.ts
new file mode 100644 (file)
index 0000000..64a7400
--- /dev/null
@@ -0,0 +1,8 @@
+import { Component } from '@angular/core';
+
+@Component({
+  template: '<router-outlet></router-outlet>'
+})
+
+export class AdminComponent {
+}
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..db1ce2d
--- /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 { VideoAbusesComponent, VideoAbuseListComponent } from './video-abuses';
+import { SharedModule } from '../shared';
+
+@NgModule({
+  imports: [
+    AdminRoutingModule,
+    SharedModule
+  ],
+
+  declarations: [
+    AdminComponent,
+
+    FriendsComponent,
+    FriendAddComponent,
+    FriendListComponent,
+
+    RequestsComponent,
+    RequestStatsComponent,
+
+    UsersComponent,
+    UserAddComponent,
+    UserListComponent,
+
+    VideoAbusesComponent,
+    VideoAbuseListComponent
+  ],
+
+  exports: [
+    AdminComponent
+  ],
+
+  providers: [
+    FriendService,
+    RequestService,
+    UserService
+  ]
+})
+export class AdminModule { }
diff --git a/client/src/app/+admin/friends/friend-add/friend-add.component.html b/client/src/app/+admin/friends/friend-add/friend-add.component.html
new file mode 100644 (file)
index 0000000..eebe033
--- /dev/null
@@ -0,0 +1,30 @@
+<h3>Make friends</h3>
+
+<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
+
+<form (ngSubmit)="makeFriends()" [formGroup]="form">
+  <div class="form-group"  *ngFor="let host of hosts; let id = index; trackBy:customTrackBy">
+    <label for="username">Host</label>
+
+    <div class="input-group">
+      <input
+        type="text" class="form-control" placeholder="domain.tld"
+        [id]="'host-' + id" [formControlName]="'host-' + id"
+      />
+      <span class="input-group-btn">
+        <button *ngIf="displayAddField(id)" (click)="addField()" class="btn btn-default" type="button">+</button>
+        <button *ngIf="displayRemoveField(id)" (click)="removeField(id)" class="btn btn-default" type="button">-</button>
+      </span>
+    </div>
+
+    <div [hidden]="form.controls['host-' + id].valid || form.controls['host-' + id].pristine" class="alert alert-warning">
+      It should be a valid host.
+    </div>
+  </div>
+
+  <div *ngIf="canMakeFriends() === false"  class="alert alert-warning">
+    It seems that you are not on a HTTPS pod. Your webserver need to have TLS activated in order to make friends.
+  </div>
+
+  <input type="submit" value="Make friends" class="btn btn-default" [disabled]="!isFormValid()">
+</form>
diff --git a/client/src/app/+admin/friends/friend-add/friend-add.component.scss b/client/src/app/+admin/friends/friend-add/friend-add.component.scss
new file mode 100644 (file)
index 0000000..5fde516
--- /dev/null
@@ -0,0 +1,7 @@
+table {
+  margin-bottom: 40px;
+}
+
+.input-group-btn button {
+  width: 35px;
+}
diff --git a/client/src/app/+admin/friends/friend-add/friend-add.component.ts b/client/src/app/+admin/friends/friend-add/friend-add.component.ts
new file mode 100644 (file)
index 0000000..0142520
--- /dev/null
@@ -0,0 +1,107 @@
+import { Component, OnInit } from '@angular/core';
+import { FormControl, FormGroup } from '@angular/forms';
+import { Router } from '@angular/router';
+
+import { validateHost } from '../../../shared';
+import { FriendService } from '../shared';
+
+@Component({
+  selector: 'my-friend-add',
+  templateUrl: './friend-add.component.html',
+  styleUrls: [ './friend-add.component.scss' ]
+})
+export class FriendAddComponent implements OnInit {
+  form: FormGroup;
+  hosts = [ ];
+  error: string = null;
+
+  constructor(private router: Router, private friendService: FriendService) {}
+
+  ngOnInit() {
+    this.form = new FormGroup({});
+    this.addField();
+  }
+
+  addField() {
+    this.form.addControl(`host-${this.hosts.length}`, new FormControl('', [ validateHost ]));
+    this.hosts.push('');
+  }
+
+  canMakeFriends() {
+    return window.location.protocol === 'https:';
+  }
+
+  customTrackBy(index: number, obj: any): any {
+    return index;
+  }
+
+  displayAddField(index: number) {
+    return index === (this.hosts.length - 1);
+  }
+
+  displayRemoveField(index: number) {
+    return (index !== 0 || this.hosts.length > 1) && index !== (this.hosts.length - 1);
+  }
+
+  isFormValid() {
+    // Do not check the last input
+    for (let i = 0; i < this.hosts.length - 1; i++) {
+      if (!this.form.controls[`host-${i}`].valid) return false;
+    }
+
+    const lastIndex = this.hosts.length - 1;
+    // If the last input (which is not the first) is empty, it's ok
+    if (this.hosts[lastIndex] === '' && lastIndex !== 0) {
+      return true;
+    } else {
+      return this.form.controls[`host-${lastIndex}`].valid;
+    }
+  }
+
+  removeField(index: number) {
+    // Remove the last control
+    this.form.removeControl(`host-${this.hosts.length - 1}`);
+    this.hosts.splice(index, 1);
+  }
+
+  makeFriends() {
+    this.error = '';
+
+    const notEmptyHosts = this.getNotEmptyHosts();
+    if (notEmptyHosts.length === 0) {
+      this.error = 'You need to specify at least 1 host.';
+      return;
+    }
+
+    if (!this.isHostsUnique(notEmptyHosts)) {
+      this.error = 'Hosts need to be unique.';
+      return;
+    }
+
+    const confirmMessage = 'Are you sure to make friends with:\n - ' + notEmptyHosts.join('\n - ');
+    if (!confirm(confirmMessage)) return;
+
+    this.friendService.makeFriends(notEmptyHosts).subscribe(
+      status => {
+        alert('Make friends request sent!');
+        this.router.navigate([ '/admin/friends/list' ]);
+      },
+      error => alert(error.text)
+    );
+  }
+
+  private getNotEmptyHosts() {
+    const notEmptyHosts = [];
+
+    Object.keys(this.form.value).forEach((hostKey) => {
+      const host = this.form.value[hostKey];
+      if (host !== '') notEmptyHosts.push(host);
+    });
+
+    return notEmptyHosts;
+  }
+
+  private isHostsUnique(hosts: string[]) {
+    return hosts.every(host => hosts.indexOf(host) === hosts.lastIndexOf(host));
+  }
+}
diff --git a/client/src/app/+admin/friends/friend-add/index.ts b/client/src/app/+admin/friends/friend-add/index.ts
new file mode 100644 (file)
index 0000000..a101b3b
--- /dev/null
@@ -0,0 +1 @@
+export * from './friend-add.component';
diff --git a/client/src/app/+admin/friends/friend-list/friend-list.component.html b/client/src/app/+admin/friends/friend-list/friend-list.component.html
new file mode 100644 (file)
index 0000000..06258f8
--- /dev/null
@@ -0,0 +1,29 @@
+<h3>Friends list</h3>
+
+<table class="table table-hover">
+  <thead>
+    <tr>
+      <th class="table-column-id">ID</th>
+      <th>Host</th>
+      <th>Score</th>
+      <th>Created Date</th>
+    </tr>
+  </thead>
+
+  <tbody>
+    <tr *ngFor="let friend of friends">
+      <td>{{ friend.id }}</td>
+      <td>{{ friend.host }}</td>
+      <td>{{ friend.score }}</td>
+      <td>{{ friend.createdAt | date: 'medium' }}</td>
+    </tr>
+  </tbody>
+</table>
+
+<a *ngIf="friends && friends.length !== 0" class="add-user btn btn-danger pull-left" (click)="quitFriends()">
+  Quit friends
+</a>
+
+<a *ngIf="friends?.length === 0" class="add-user btn btn-success pull-right" [routerLink]="['/admin/friends/add']">
+  Make friends
+</a>
diff --git a/client/src/app/+admin/friends/friend-list/friend-list.component.scss b/client/src/app/+admin/friends/friend-list/friend-list.component.scss
new file mode 100644 (file)
index 0000000..cb597e1
--- /dev/null
@@ -0,0 +1,3 @@
+table {
+  margin-bottom: 40px;
+}
diff --git a/client/src/app/+admin/friends/friend-list/friend-list.component.ts b/client/src/app/+admin/friends/friend-list/friend-list.component.ts
new file mode 100644 (file)
index 0000000..bec1016
--- /dev/null
@@ -0,0 +1,38 @@
+import { Component, OnInit } from '@angular/core';
+
+import { Friend, FriendService } from '../shared';
+
+@Component({
+  selector: 'my-friend-list',
+  templateUrl: './friend-list.component.html',
+  styleUrls: [ './friend-list.component.scss' ]
+})
+export class FriendListComponent implements OnInit {
+  friends: Friend[];
+
+  constructor(private friendService: FriendService) {  }
+
+  ngOnInit() {
+    this.getFriends();
+  }
+
+  quitFriends() {
+    if (!confirm('Are you sure?')) return;
+
+    this.friendService.quitFriends().subscribe(
+      status => {
+        alert('Quit friends!');
+        this.getFriends();
+      },
+      error => alert(error.text)
+    );
+  }
+
+  private getFriends() {
+    this.friendService.getFriends().subscribe(
+      res => this.friends = res.friends,
+
+      err => alert(err.text)
+    );
+  }
+}
diff --git a/client/src/app/+admin/friends/friend-list/index.ts b/client/src/app/+admin/friends/friend-list/index.ts
new file mode 100644 (file)
index 0000000..354c978
--- /dev/null
@@ -0,0 +1 @@
+export * from './friend-list.component';
diff --git a/client/src/app/+admin/friends/friends.component.ts b/client/src/app/+admin/friends/friends.component.ts
new file mode 100644 (file)
index 0000000..bc3f541
--- /dev/null
@@ -0,0 +1,8 @@
+import { Component } from '@angular/core';
+
+@Component({
+    template: '<router-outlet></router-outlet>'
+})
+
+export class FriendsComponent {
+}
diff --git a/client/src/app/+admin/friends/friends.routes.ts b/client/src/app/+admin/friends/friends.routes.ts
new file mode 100644 (file)
index 0000000..a9a0653
--- /dev/null
@@ -0,0 +1,37 @@
+import { Routes } from '@angular/router';
+
+import { FriendsComponent } from './friends.component';
+import { FriendAddComponent } from './friend-add';
+import { FriendListComponent } from './friend-list';
+
+export const FriendsRoutes: Routes = [
+  {
+      path: 'friends',
+      component: FriendsComponent,
+      children: [
+        {
+          path: '',
+          redirectTo: 'list',
+          pathMatch: 'full'
+        },
+        {
+          path: 'list',
+          component: FriendListComponent,
+          data: {
+            meta: {
+              titleSuffix: ' - Friends list'
+            }
+          }
+        },
+        {
+          path: 'add',
+          component: FriendAddComponent,
+          data: {
+            meta: {
+              titleSuffix: ' - Add friends'
+            }
+          }
+        }
+      ]
+    }
+];
diff --git a/client/src/app/+admin/friends/index.ts b/client/src/app/+admin/friends/index.ts
new file mode 100644 (file)
index 0000000..dd4df25
--- /dev/null
@@ -0,0 +1,5 @@
+export * from './friend-add';
+export * from './friend-list';
+export * from './shared';
+export * from './friends.component';
+export * from './friends.routes';
diff --git a/client/src/app/+admin/friends/shared/friend.model.ts b/client/src/app/+admin/friends/shared/friend.model.ts
new file mode 100644 (file)
index 0000000..462cc82
--- /dev/null
@@ -0,0 +1,6 @@
+export interface Friend {
+  id: string;
+  host: string;
+  score: number;
+  createdAt: Date;
+}
diff --git a/client/src/app/+admin/friends/shared/friend.service.ts b/client/src/app/+admin/friends/shared/friend.service.ts
new file mode 100644 (file)
index 0000000..e974593
--- /dev/null
@@ -0,0 +1,47 @@
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/operator/catch';
+import 'rxjs/add/operator/map';
+
+import { Friend } from './friend.model';
+import { AuthHttp, RestExtractor, ResultList } from '../../../shared';
+
+@Injectable()
+export class FriendService {
+  private static BASE_FRIEND_URL: string = '/api/v1/pods/';
+
+  constructor (
+    private authHttp: AuthHttp,
+    private restExtractor: RestExtractor
+  ) {}
+
+  getFriends() {
+    return this.authHttp.get(FriendService.BASE_FRIEND_URL)
+                        .map(this.restExtractor.extractDataList)
+                        .map(this.extractFriends)
+                        .catch((res) => this.restExtractor.handleError(res));
+  }
+
+  makeFriends(notEmptyHosts) {
+    const body = {
+      hosts: notEmptyHosts
+    };
+
+    return this.authHttp.post(FriendService.BASE_FRIEND_URL + 'makefriends', body)
+                        .map(this.restExtractor.extractDataBool)
+                        .catch((res) => this.restExtractor.handleError(res));
+  }
+
+  quitFriends() {
+    return this.authHttp.get(FriendService.BASE_FRIEND_URL + 'quitfriends')
+                        .map(res => res.status)
+                        .catch((res) => this.restExtractor.handleError(res));
+  }
+
+  private extractFriends(result: ResultList) {
+    const friends: Friend[] = result.data;
+    const totalFriends = result.total;
+
+    return { friends, totalFriends };
+  }
+}
diff --git a/client/src/app/+admin/friends/shared/index.ts b/client/src/app/+admin/friends/shared/index.ts
new file mode 100644 (file)
index 0000000..0d67163
--- /dev/null
@@ -0,0 +1,2 @@
+export * from './friend.model';
+export * from './friend.service';
diff --git a/client/src/app/+admin/index.ts b/client/src/app/+admin/index.ts
new file mode 100644 (file)
index 0000000..2f47a3c
--- /dev/null
@@ -0,0 +1,6 @@
+export * from './friends';
+export * from './requests';
+export * from './users';
+export * from './admin-routing.module';
+export * from './admin.module';
+export * from './admin.component';
diff --git a/client/src/app/+admin/requests/index.ts b/client/src/app/+admin/requests/index.ts
new file mode 100644 (file)
index 0000000..236a9ee
--- /dev/null
@@ -0,0 +1,4 @@
+export * from './request-stats';
+export * from './shared';
+export * from './requests.component';
+export * from './requests.routes';
diff --git a/client/src/app/+admin/requests/request-stats/index.ts b/client/src/app/+admin/requests/request-stats/index.ts
new file mode 100644 (file)
index 0000000..be3a66f
--- /dev/null
@@ -0,0 +1 @@
+export * from './request-stats.component';
diff --git a/client/src/app/+admin/requests/request-stats/request-stats.component.html b/client/src/app/+admin/requests/request-stats/request-stats.component.html
new file mode 100644 (file)
index 0000000..9dbed17
--- /dev/null
@@ -0,0 +1,33 @@
+<h3>Requests stats</h3>
+
+<div *ngIf="stats !== null">
+  <div class="requests-general">
+    <div>
+      <span class="label-description">Remaining requests:</span>
+      {{ stats.totalRequests }}
+    </div>
+
+    <div>
+      <span class="label-description">Interval seconds between requests:</span>
+      {{ stats.secondsInterval }}
+    </div>
+
+    <div>
+      <span class="label-description">Remaining time before the scheduled request:</span>
+      {{ stats.remainingSeconds }}
+    </div>
+  </div>
+
+  <div class="requests-limit">
+    <div>
+      <span class="label-description">Maximum number of different pods for a scheduled request:</span>
+      {{ stats.requestsLimitPods }}
+    </div>
+
+    <div>
+      <span class="label-description">Maximum number of requests per pod for a scheduled request:</span>
+      {{ stats.requestsLimitPerPod }}
+    </div>
+  </div>
+
+</div>
diff --git a/client/src/app/+admin/requests/request-stats/request-stats.component.scss b/client/src/app/+admin/requests/request-stats/request-stats.component.scss
new file mode 100644 (file)
index 0000000..9c68fba
--- /dev/null
@@ -0,0 +1,19 @@
+.label-description {
+  display: inline-block;
+  font-weight: bold;
+  color: black;
+}
+
+.requests-general {
+  .label-description {
+    width: 320px;
+  }
+}
+
+.requests-limit {
+  margin-top: 20px;
+
+  .label-description {
+    width: 430px;
+  }
+}
diff --git a/client/src/app/+admin/requests/request-stats/request-stats.component.ts b/client/src/app/+admin/requests/request-stats/request-stats.component.ts
new file mode 100644 (file)
index 0000000..23b8367
--- /dev/null
@@ -0,0 +1,48 @@
+import { setInterval } from 'timers'
+import { Component, OnInit, OnDestroy } from '@angular/core';
+
+import { RequestService, RequestStats } from '../shared';
+
+@Component({
+       selector: 'my-request-stats',
+       templateUrl: './request-stats.component.html',
+  styleUrls: [ './request-stats.component.scss' ]
+})
+export class RequestStatsComponent implements OnInit, OnDestroy {
+  stats: RequestStats = null;
+
+  private interval: NodeJS.Timer = null;
+
+  constructor(private requestService: RequestService) {  }
+
+  ngOnInit() {
+    this.getStats();
+    this.runInterval();
+  }
+
+  ngOnDestroy() {
+    if (this.stats !== null && this.stats.secondsInterval !== null) {
+      clearInterval(this.interval);
+    }
+  }
+
+  getStats() {
+    this.requestService.getStats().subscribe(
+      stats => this.stats = stats,
+
+      err => alert(err.text)
+    );
+  }
+
+  private runInterval() {
+    this.interval = setInterval(() => {
+      this.stats.remainingMilliSeconds -= 1000;
+
+      if (this.stats.remainingMilliSeconds <= 0) {
+        setTimeout(() => this.getStats(), this.stats.remainingMilliSeconds + 100);
+      }
+    }, 1000);
+  }
+
+
+}
diff --git a/client/src/app/+admin/requests/requests.component.ts b/client/src/app/+admin/requests/requests.component.ts
new file mode 100644 (file)
index 0000000..471112b
--- /dev/null
@@ -0,0 +1,8 @@
+import { Component } from '@angular/core';
+
+@Component({
+    template: '<router-outlet></router-outlet>'
+})
+
+export class RequestsComponent {
+}
diff --git a/client/src/app/+admin/requests/requests.routes.ts b/client/src/app/+admin/requests/requests.routes.ts
new file mode 100644 (file)
index 0000000..70fbf41
--- /dev/null
@@ -0,0 +1,27 @@
+import { Routes } from '@angular/router';
+
+import { RequestsComponent } from './requests.component';
+import { RequestStatsComponent } from './request-stats';
+
+export const RequestsRoutes: Routes = [
+  {
+      path: 'requests',
+      component: RequestsComponent,
+      children: [
+        {
+          path: '',
+          redirectTo: 'stats',
+          pathMatch: 'full'
+        },
+        {
+          path: 'stats',
+          component: RequestStatsComponent,
+          data: {
+            meta: {
+              titleSuffix: ' - Request stats'
+            }
+          }
+        }
+      ]
+    }
+];
diff --git a/client/src/app/+admin/requests/shared/index.ts b/client/src/app/+admin/requests/shared/index.ts
new file mode 100644 (file)
index 0000000..32ab576
--- /dev/null
@@ -0,0 +1,2 @@
+export * from './request-stats.model';
+export * from './request.service';
diff --git a/client/src/app/+admin/requests/shared/request-stats.model.ts b/client/src/app/+admin/requests/shared/request-stats.model.ts
new file mode 100644 (file)
index 0000000..f658c46
--- /dev/null
@@ -0,0 +1,35 @@
+export interface Request {
+  request: any;
+  to: any;
+}
+
+export class RequestStats {
+  requestsLimitPods: number;
+  requestsLimitPerPod: number;
+  milliSecondsInterval: number;
+  remainingMilliSeconds: number;
+  totalRequests: number;
+
+  constructor(hash: {
+    requestsLimitPods: number,
+    requestsLimitPerPod: number,
+    milliSecondsInterval: number,
+    remainingMilliSeconds: number,
+    totalRequests: number;
+  }) {
+    this.requestsLimitPods = hash.requestsLimitPods;
+    this.requestsLimitPerPod = hash.requestsLimitPerPod;
+    this.milliSecondsInterval = hash.milliSecondsInterval;
+    this.remainingMilliSeconds = hash.remainingMilliSeconds;
+    this.totalRequests = hash.totalRequests;
+  }
+
+  get remainingSeconds() {
+    return Math.floor(this.remainingMilliSeconds / 1000);
+  }
+
+  get secondsInterval() {
+    return Math.floor(this.milliSecondsInterval / 1000);
+  }
+
+}
diff --git a/client/src/app/+admin/requests/shared/request.service.ts b/client/src/app/+admin/requests/shared/request.service.ts
new file mode 100644 (file)
index 0000000..55b28bc
--- /dev/null
@@ -0,0 +1,24 @@
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/operator/catch';
+import 'rxjs/add/operator/map';
+
+import { RequestStats } from './request-stats.model';
+import { AuthHttp, RestExtractor } from '../../../shared';
+
+@Injectable()
+export class RequestService {
+  private static BASE_REQUEST_URL: string = '/api/v1/requests/';
+
+  constructor (
+    private authHttp: AuthHttp,
+    private restExtractor: RestExtractor
+  ) {}
+
+  getStats(): Observable<RequestStats> {
+    return this.authHttp.get(RequestService.BASE_REQUEST_URL + 'stats')
+                        .map(this.restExtractor.extractDataGet)
+                        .map((data) => new RequestStats(data))
+                        .catch((res) => this.restExtractor.handleError(res));
+  }
+}
diff --git a/client/src/app/+admin/users/index.ts b/client/src/app/+admin/users/index.ts
new file mode 100644 (file)
index 0000000..e98a81f
--- /dev/null
@@ -0,0 +1,5 @@
+export * from './shared';
+export * from './user-add';
+export * from './user-list';
+export * from './users.component';
+export * from './users.routes';
diff --git a/client/src/app/+admin/users/shared/index.ts b/client/src/app/+admin/users/shared/index.ts
new file mode 100644 (file)
index 0000000..e17ee5c
--- /dev/null
@@ -0,0 +1 @@
+export * from './user.service';
diff --git a/client/src/app/+admin/users/shared/user.service.ts b/client/src/app/+admin/users/shared/user.service.ts
new file mode 100644 (file)
index 0000000..d9005b2
--- /dev/null
@@ -0,0 +1,49 @@
+import { Injectable } from '@angular/core';
+import 'rxjs/add/operator/catch';
+import 'rxjs/add/operator/map';
+
+import { AuthHttp, RestExtractor, ResultList, User } from '../../../shared';
+
+@Injectable()
+export class UserService {
+  // TODO: merge this constant with account
+  private static BASE_USERS_URL = '/api/v1/users/';
+
+  constructor(
+    private authHttp: AuthHttp,
+    private restExtractor: RestExtractor
+  ) {}
+
+  addUser(username: string, password: string) {
+    const body = {
+      username,
+      password
+    };
+
+    return this.authHttp.post(UserService.BASE_USERS_URL, body)
+                        .map(this.restExtractor.extractDataBool)
+                        .catch(this.restExtractor.handleError);
+  }
+
+  getUsers() {
+    return this.authHttp.get(UserService.BASE_USERS_URL)
+                 .map(this.restExtractor.extractDataList)
+                 .map(this.extractUsers)
+                 .catch((res) => this.restExtractor.handleError(res));
+  }
+
+  removeUser(user: User) {
+    return this.authHttp.delete(UserService.BASE_USERS_URL + user.id);
+  }
+
+  private extractUsers(result: ResultList) {
+    const usersJson = result.data;
+    const totalUsers = result.total;
+    const users = [];
+    for (const userJson of usersJson) {
+      users.push(new User(userJson));
+    }
+
+    return { users, totalUsers };
+  }
+}
diff --git a/client/src/app/+admin/users/user-add/index.ts b/client/src/app/+admin/users/user-add/index.ts
new file mode 100644 (file)
index 0000000..66d5ca0
--- /dev/null
@@ -0,0 +1 @@
+export * from './user-add.component';
diff --git a/client/src/app/+admin/users/user-add/user-add.component.html b/client/src/app/+admin/users/user-add/user-add.component.html
new file mode 100644 (file)
index 0000000..9b76c7c
--- /dev/null
@@ -0,0 +1,29 @@
+<h3>Add user</h3>
+
+<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
+
+<form role="form" (ngSubmit)="addUser()" [formGroup]="form">
+  <div class="form-group">
+    <label for="username">Username</label>
+    <input
+      type="text" class="form-control" id="username" placeholder="Username"
+      formControlName="username"
+    >
+    <div *ngIf="formErrors.username" class="alert alert-danger">
+      {{ formErrors.username }}
+    </div>
+  </div>
+
+  <div class="form-group">
+    <label for="password">Password</label>
+    <input
+      type="password" class="form-control" id="password" placeholder="Password"
+      formControlName="password"
+    >
+    <div *ngIf="formErrors.password" class="alert alert-danger">
+      {{ formErrors.password }}
+    </div>
+  </div>
+
+  <input type="submit" value="Add user" class="btn btn-default" [disabled]="!form.valid">
+</form>
diff --git a/client/src/app/+admin/users/user-add/user-add.component.ts b/client/src/app/+admin/users/user-add/user-add.component.ts
new file mode 100644 (file)
index 0000000..ab96fb0
--- /dev/null
@@ -0,0 +1,57 @@
+import { Component, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup } from '@angular/forms';
+import { Router } from '@angular/router';
+
+import { UserService } from '../shared';
+import { FormReactive, USER_USERNAME, USER_PASSWORD } from '../../../shared';
+
+@Component({
+  selector: 'my-user-add',
+  templateUrl: './user-add.component.html'
+})
+export class UserAddComponent extends FormReactive implements OnInit {
+  error: string = null;
+
+  form: FormGroup;
+  formErrors = {
+    'username': '',
+    'password': ''
+  };
+  validationMessages = {
+    'username': USER_USERNAME.MESSAGES,
+    'password': USER_PASSWORD.MESSAGES,
+  };
+
+  constructor(
+    private formBuilder: FormBuilder,
+    private router: Router,
+    private userService: UserService
+  ) {
+    super();
+  }
+
+  buildForm() {
+    this.form = this.formBuilder.group({
+      username: [ '', USER_USERNAME.VALIDATORS ],
+      password: [ '', USER_PASSWORD.VALIDATORS ],
+    });
+
+    this.form.valueChanges.subscribe(data => this.onValueChanged(data));
+  }
+
+  ngOnInit() {
+    this.buildForm();
+  }
+
+  addUser() {
+    this.error = null;
+
+    const { username, password } = this.form.value;
+
+    this.userService.addUser(username, password).subscribe(
+      ok => this.router.navigate([ '/admin/users/list' ]),
+
+      err => this.error = err.text
+    );
+  }
+}
diff --git a/client/src/app/+admin/users/user-list/index.ts b/client/src/app/+admin/users/user-list/index.ts
new file mode 100644 (file)
index 0000000..51fbefa
--- /dev/null
@@ -0,0 +1 @@
+export * from './user-list.component';
diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html
new file mode 100644 (file)
index 0000000..36193d1
--- /dev/null
@@ -0,0 +1,28 @@
+<h3>Users list</h3>
+
+<table class="table table-hover">
+  <thead>
+    <tr>
+      <th class="table-column-id">ID</th>
+      <th>Username</th>
+      <th>Created Date</th>
+      <th class="text-right">Remove</th>
+    </tr>
+  </thead>
+
+  <tbody>
+    <tr *ngFor="let user of users">
+      <td>{{ user.id }}</td>
+      <td>{{ user.username }}</td>
+      <td>{{ user.createdAt | date: 'medium' }}</td>
+      <td class="text-right">
+        <span class="glyphicon glyphicon-remove" *ngIf="!user.isAdmin()" (click)="removeUser(user)"></span>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+<a class="add-user btn btn-success pull-right" [routerLink]="['/admin/users/add']">
+  <span class="glyphicon glyphicon-plus"></span>
+  Add user
+</a>
diff --git a/client/src/app/+admin/users/user-list/user-list.component.scss b/client/src/app/+admin/users/user-list/user-list.component.scss
new file mode 100644 (file)
index 0000000..e9f61e9
--- /dev/null
@@ -0,0 +1,7 @@
+.glyphicon-remove {
+  cursor: pointer;
+}
+
+.add-user {
+  margin-top: 10px;
+}
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts
new file mode 100644 (file)
index 0000000..03f4e5c
--- /dev/null
@@ -0,0 +1,42 @@
+import { Component, OnInit } from '@angular/core';
+
+import { User } from '../../../shared';
+import { UserService } from '../shared';
+
+@Component({
+  selector: 'my-user-list',
+  templateUrl: './user-list.component.html',
+  styleUrls: [ './user-list.component.scss' ]
+})
+export class UserListComponent implements OnInit {
+  totalUsers: number;
+  users: User[];
+
+  constructor(private userService: UserService) {}
+
+  ngOnInit() {
+    this.getUsers();
+  }
+
+  getUsers() {
+    this.userService.getUsers().subscribe(
+      ({ users, totalUsers }) => {
+        this.users = users;
+        this.totalUsers = totalUsers;
+      },
+
+      err => alert(err.text)
+    );
+  }
+
+
+  removeUser(user: User) {
+    if (confirm('Are you sure?')) {
+      this.userService.removeUser(user).subscribe(
+        () => this.getUsers(),
+
+        err => alert(err.text)
+      );
+    }
+  }
+}
diff --git a/client/src/app/+admin/users/users.component.ts b/client/src/app/+admin/users/users.component.ts
new file mode 100644 (file)
index 0000000..37e3b15
--- /dev/null
@@ -0,0 +1,8 @@
+import { Component } from '@angular/core';
+
+@Component({
+    template: '<router-outlet></router-outlet>'
+})
+
+export class UsersComponent {
+}
diff --git a/client/src/app/+admin/users/users.routes.ts b/client/src/app/+admin/users/users.routes.ts
new file mode 100644 (file)
index 0000000..92e3e43
--- /dev/null
@@ -0,0 +1,37 @@
+import { Routes } from '@angular/router';
+
+import { UsersComponent } from './users.component';
+import { UserAddComponent } from './user-add';
+import { UserListComponent } from './user-list';
+
+export const UsersRoutes: Routes = [
+  {
+      path: 'users',
+      component: UsersComponent,
+      children: [
+        {
+          path: '',
+          redirectTo: 'list',
+          pathMatch: 'full'
+        },
+        {
+          path: 'list',
+          component: UserListComponent,
+          data: {
+            meta: {
+              titleSuffix: ' - Users list'
+            }
+          }
+        },
+        {
+          path: 'add',
+          component: UserAddComponent,
+          data: {
+            meta: {
+              titleSuffix: ' - Add a user'
+            }
+          }
+        }
+      ]
+    }
+];
diff --git a/client/src/app/+admin/video-abuses/index.ts b/client/src/app/+admin/video-abuses/index.ts
new file mode 100644 (file)
index 0000000..7f5e65f
--- /dev/null
@@ -0,0 +1,3 @@
+export * from './video-abuse-list';
+export * from './video-abuses.component';
+export * from './video-abuses.routes';
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/index.ts b/client/src/app/+admin/video-abuses/video-abuse-list/index.ts
new file mode 100644 (file)
index 0000000..3f2ed17
--- /dev/null
@@ -0,0 +1 @@
+export * from './video-abuse-list.component';
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html
new file mode 100644 (file)
index 0000000..4604357
--- /dev/null
@@ -0,0 +1,27 @@
+<h3>Video abuses list</h3>
+
+<table class="table table-hover">
+  <thead>
+    <tr>
+      <th class="cell-id">ID</th>
+      <th class="cell-reason">Reason</th>
+      <th>Reporter pod host</th>
+      <th>Reporter username</th>
+      <th>Video</th>
+      <th>Created at</th>
+    </tr>
+  </thead>
+
+  <tbody>
+    <tr *ngFor="let videoAbuse of videoAbuses">
+      <td>{{ videoAbuse.id }}</td>
+      <td>{{ videoAbuse.reason }}</td>
+      <td>{{ videoAbuse.reporterPodHost }}</td>
+      <td>{{ videoAbuse.reporterUsername }}</td>
+      <td>
+        <a [routerLink]="buildVideoLink(videoAbuse)" title="Go to video">{{ videoAbuse.videoId }}</a>
+      </td>
+      <td>{{ videoAbuse.createdAt | date: 'medium' }}</td>
+    </tr>
+  </tbody>
+</table>
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss
new file mode 100644 (file)
index 0000000..a094f74
--- /dev/null
@@ -0,0 +1,7 @@
+.cell-id {
+  width: 40px;
+}
+
+.cell-reason {
+  width: 200px;
+}
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts
new file mode 100644 (file)
index 0000000..de58bba
--- /dev/null
@@ -0,0 +1,31 @@
+import { setInterval } from 'timers'
+import { Component, OnInit } from '@angular/core';
+
+import { VideoAbuseService, VideoAbuse} from '../../../shared';
+
+@Component({
+       selector: 'my-video-abuse-list',
+       templateUrl: './video-abuse-list.component.html',
+  styleUrls: [ './video-abuse-list.component.scss' ]
+})
+export class VideoAbuseListComponent implements OnInit {
+  videoAbuses: VideoAbuse[];
+
+  constructor(private videoAbuseService: VideoAbuseService) {  }
+
+  ngOnInit() {
+    this.getVideoAbuses();
+  }
+
+  buildVideoLink(videoAbuse: VideoAbuse) {
+    return `/videos/${videoAbuse.videoId}`;
+  }
+
+  private getVideoAbuses() {
+    this.videoAbuseService.getVideoAbuses().subscribe(
+      res => this.videoAbuses = res.videoAbuses,
+
+      err => alert(err.text)
+    );
+  }
+}
diff --git a/client/src/app/+admin/video-abuses/video-abuses.component.ts b/client/src/app/+admin/video-abuses/video-abuses.component.ts
new file mode 100644 (file)
index 0000000..001f27e
--- /dev/null
@@ -0,0 +1,8 @@
+import { Component } from '@angular/core';
+
+@Component({
+  template: '<router-outlet></router-outlet>'
+})
+
+export class VideoAbusesComponent {
+}
diff --git a/client/src/app/+admin/video-abuses/video-abuses.routes.ts b/client/src/app/+admin/video-abuses/video-abuses.routes.ts
new file mode 100644 (file)
index 0000000..26a7618
--- /dev/null
@@ -0,0 +1,28 @@
+import { Routes } from '@angular/router';
+
+import { VideoAbusesComponent } from './video-abuses.component';
+import { VideoAbuseListComponent } from './video-abuse-list';
+
+export const VideoAbusesRoutes: Routes = [
+  {
+      path: 'video-abuses',
+      component: VideoAbusesComponent
+      ,
+      children: [
+        {
+          path: '',
+          redirectTo: 'list',
+          pathMatch: 'full'
+        },
+        {
+          path: 'list',
+          component: VideoAbuseListComponent,
+          data: {
+            meta: {
+              titleSuffix: ' - Video abuses list'
+            }
+          }
+        }
+      ]
+    }
+];
diff --git a/client/src/app/admin/admin-routing.module.ts b/client/src/app/admin/admin-routing.module.ts
deleted file mode 100644 (file)
index cabc6df..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-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';
-import { VideoAbusesRoutes } from './video-abuses';
-
-const adminRoutes: Routes = [
-  {
-    path: 'admin',
-    component: AdminComponent,
-    children: [
-      {
-        path: '',
-        redirectTo: 'users',
-        pathMatch: 'full'
-      },
-      ...FriendsRoutes,
-      ...RequestsRoutes,
-      ...UsersRoutes,
-      ...VideoAbusesRoutes
-    ]
-  }
-];
-
-@NgModule({
-  imports: [ RouterModule.forChild(adminRoutes) ],
-  exports: [ RouterModule ]
-})
-export class AdminRoutingModule {}
diff --git a/client/src/app/admin/admin.component.ts b/client/src/app/admin/admin.component.ts
deleted file mode 100644 (file)
index 64a7400..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
-  template: '<router-outlet></router-outlet>'
-})
-
-export class AdminComponent {
-}
diff --git a/client/src/app/admin/admin.module.ts b/client/src/app/admin/admin.module.ts
deleted file mode 100644 (file)
index d3ada8c..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-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 { VideoAbusesComponent, VideoAbuseListComponent } from './video-abuses';
-import { MenuAdminComponent } from './menu-admin.component';
-import { SharedModule } from '../shared';
-
-@NgModule({
-  imports: [
-    AdminRoutingModule,
-    SharedModule
-  ],
-
-  declarations: [
-    AdminComponent,
-
-    FriendsComponent,
-    FriendAddComponent,
-    FriendListComponent,
-
-    RequestsComponent,
-    RequestStatsComponent,
-
-    UsersComponent,
-    UserAddComponent,
-    UserListComponent,
-
-    VideoAbusesComponent,
-    VideoAbuseListComponent,
-
-    MenuAdminComponent
-  ],
-
-  exports: [
-    AdminComponent,
-    MenuAdminComponent
-  ],
-
-  providers: [
-    FriendService,
-    RequestService,
-    UserService
-  ]
-})
-export class AdminModule { }
diff --git a/client/src/app/admin/friends/friend-add/friend-add.component.html b/client/src/app/admin/friends/friend-add/friend-add.component.html
deleted file mode 100644 (file)
index eebe033..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<h3>Make friends</h3>
-
-<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
-
-<form (ngSubmit)="makeFriends()" [formGroup]="form">
-  <div class="form-group"  *ngFor="let host of hosts; let id = index; trackBy:customTrackBy">
-    <label for="username">Host</label>
-
-    <div class="input-group">
-      <input
-        type="text" class="form-control" placeholder="domain.tld"
-        [id]="'host-' + id" [formControlName]="'host-' + id"
-      />
-      <span class="input-group-btn">
-        <button *ngIf="displayAddField(id)" (click)="addField()" class="btn btn-default" type="button">+</button>
-        <button *ngIf="displayRemoveField(id)" (click)="removeField(id)" class="btn btn-default" type="button">-</button>
-      </span>
-    </div>
-
-    <div [hidden]="form.controls['host-' + id].valid || form.controls['host-' + id].pristine" class="alert alert-warning">
-      It should be a valid host.
-    </div>
-  </div>
-
-  <div *ngIf="canMakeFriends() === false"  class="alert alert-warning">
-    It seems that you are not on a HTTPS pod. Your webserver need to have TLS activated in order to make friends.
-  </div>
-
-  <input type="submit" value="Make friends" class="btn btn-default" [disabled]="!isFormValid()">
-</form>
diff --git a/client/src/app/admin/friends/friend-add/friend-add.component.scss b/client/src/app/admin/friends/friend-add/friend-add.component.scss
deleted file mode 100644 (file)
index 5fde516..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-table {
-  margin-bottom: 40px;
-}
-
-.input-group-btn button {
-  width: 35px;
-}
diff --git a/client/src/app/admin/friends/friend-add/friend-add.component.ts b/client/src/app/admin/friends/friend-add/friend-add.component.ts
deleted file mode 100644 (file)
index 0142520..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { FormControl, FormGroup } from '@angular/forms';
-import { Router } from '@angular/router';
-
-import { validateHost } from '../../../shared';
-import { FriendService } from '../shared';
-
-@Component({
-  selector: 'my-friend-add',
-  templateUrl: './friend-add.component.html',
-  styleUrls: [ './friend-add.component.scss' ]
-})
-export class FriendAddComponent implements OnInit {
-  form: FormGroup;
-  hosts = [ ];
-  error: string = null;
-
-  constructor(private router: Router, private friendService: FriendService) {}
-
-  ngOnInit() {
-    this.form = new FormGroup({});
-    this.addField();
-  }
-
-  addField() {
-    this.form.addControl(`host-${this.hosts.length}`, new FormControl('', [ validateHost ]));
-    this.hosts.push('');
-  }
-
-  canMakeFriends() {
-    return window.location.protocol === 'https:';
-  }
-
-  customTrackBy(index: number, obj: any): any {
-    return index;
-  }
-
-  displayAddField(index: number) {
-    return index === (this.hosts.length - 1);
-  }
-
-  displayRemoveField(index: number) {
-    return (index !== 0 || this.hosts.length > 1) && index !== (this.hosts.length - 1);
-  }
-
-  isFormValid() {
-    // Do not check the last input
-    for (let i = 0; i < this.hosts.length - 1; i++) {
-      if (!this.form.controls[`host-${i}`].valid) return false;
-    }
-
-    const lastIndex = this.hosts.length - 1;
-    // If the last input (which is not the first) is empty, it's ok
-    if (this.hosts[lastIndex] === '' && lastIndex !== 0) {
-      return true;
-    } else {
-      return this.form.controls[`host-${lastIndex}`].valid;
-    }
-  }
-
-  removeField(index: number) {
-    // Remove the last control
-    this.form.removeControl(`host-${this.hosts.length - 1}`);
-    this.hosts.splice(index, 1);
-  }
-
-  makeFriends() {
-    this.error = '';
-
-    const notEmptyHosts = this.getNotEmptyHosts();
-    if (notEmptyHosts.length === 0) {
-      this.error = 'You need to specify at least 1 host.';
-      return;
-    }
-
-    if (!this.isHostsUnique(notEmptyHosts)) {
-      this.error = 'Hosts need to be unique.';
-      return;
-    }
-
-    const confirmMessage = 'Are you sure to make friends with:\n - ' + notEmptyHosts.join('\n - ');
-    if (!confirm(confirmMessage)) return;
-
-    this.friendService.makeFriends(notEmptyHosts).subscribe(
-      status => {
-        alert('Make friends request sent!');
-        this.router.navigate([ '/admin/friends/list' ]);
-      },
-      error => alert(error.text)
-    );
-  }
-
-  private getNotEmptyHosts() {
-    const notEmptyHosts = [];
-
-    Object.keys(this.form.value).forEach((hostKey) => {
-      const host = this.form.value[hostKey];
-      if (host !== '') notEmptyHosts.push(host);
-    });
-
-    return notEmptyHosts;
-  }
-
-  private isHostsUnique(hosts: string[]) {
-    return hosts.every(host => hosts.indexOf(host) === hosts.lastIndexOf(host));
-  }
-}
diff --git a/client/src/app/admin/friends/friend-add/index.ts b/client/src/app/admin/friends/friend-add/index.ts
deleted file mode 100644 (file)
index a101b3b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-export * from './friend-add.component';
diff --git a/client/src/app/admin/friends/friend-list/friend-list.component.html b/client/src/app/admin/friends/friend-list/friend-list.component.html
deleted file mode 100644 (file)
index 06258f8..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<h3>Friends list</h3>
-
-<table class="table table-hover">
-  <thead>
-    <tr>
-      <th class="table-column-id">ID</th>
-      <th>Host</th>
-      <th>Score</th>
-      <th>Created Date</th>
-    </tr>
-  </thead>
-
-  <tbody>
-    <tr *ngFor="let friend of friends">
-      <td>{{ friend.id }}</td>
-      <td>{{ friend.host }}</td>
-      <td>{{ friend.score }}</td>
-      <td>{{ friend.createdAt | date: 'medium' }}</td>
-    </tr>
-  </tbody>
-</table>
-
-<a *ngIf="friends && friends.length !== 0" class="add-user btn btn-danger pull-left" (click)="quitFriends()">
-  Quit friends
-</a>
-
-<a *ngIf="friends?.length === 0" class="add-user btn btn-success pull-right" [routerLink]="['/admin/friends/add']">
-  Make friends
-</a>
diff --git a/client/src/app/admin/friends/friend-list/friend-list.component.scss b/client/src/app/admin/friends/friend-list/friend-list.component.scss
deleted file mode 100644 (file)
index cb597e1..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-table {
-  margin-bottom: 40px;
-}
diff --git a/client/src/app/admin/friends/friend-list/friend-list.component.ts b/client/src/app/admin/friends/friend-list/friend-list.component.ts
deleted file mode 100644 (file)
index bec1016..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-import { Friend, FriendService } from '../shared';
-
-@Component({
-  selector: 'my-friend-list',
-  templateUrl: './friend-list.component.html',
-  styleUrls: [ './friend-list.component.scss' ]
-})
-export class FriendListComponent implements OnInit {
-  friends: Friend[];
-
-  constructor(private friendService: FriendService) {  }
-
-  ngOnInit() {
-    this.getFriends();
-  }
-
-  quitFriends() {
-    if (!confirm('Are you sure?')) return;
-
-    this.friendService.quitFriends().subscribe(
-      status => {
-        alert('Quit friends!');
-        this.getFriends();
-      },
-      error => alert(error.text)
-    );
-  }
-
-  private getFriends() {
-    this.friendService.getFriends().subscribe(
-      res => this.friends = res.friends,
-
-      err => alert(err.text)
-    );
-  }
-}
diff --git a/client/src/app/admin/friends/friend-list/index.ts b/client/src/app/admin/friends/friend-list/index.ts
deleted file mode 100644 (file)
index 354c978..0000000
+++ /dev/null
@@ -1 +0,0 @@
-export * from './friend-list.component';
diff --git a/client/src/app/admin/friends/friends.component.ts b/client/src/app/admin/friends/friends.component.ts
deleted file mode 100644 (file)
index bc3f541..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
-    template: '<router-outlet></router-outlet>'
-})
-
-export class FriendsComponent {
-}
diff --git a/client/src/app/admin/friends/friends.routes.ts b/client/src/app/admin/friends/friends.routes.ts
deleted file mode 100644 (file)
index a9a0653..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Routes } from '@angular/router';
-
-import { FriendsComponent } from './friends.component';
-import { FriendAddComponent } from './friend-add';
-import { FriendListComponent } from './friend-list';
-
-export const FriendsRoutes: Routes = [
-  {
-      path: 'friends',
-      component: FriendsComponent,
-      children: [
-        {
-          path: '',
-          redirectTo: 'list',
-          pathMatch: 'full'
-        },
-        {
-          path: 'list',
-          component: FriendListComponent,
-          data: {
-            meta: {
-              titleSuffix: ' - Friends list'
-            }
-          }
-        },
-        {
-          path: 'add',
-          component: FriendAddComponent,
-          data: {
-            meta: {
-              titleSuffix: ' - Add friends'
-            }
-          }
-        }
-      ]
-    }
-];
diff --git a/client/src/app/admin/friends/index.ts b/client/src/app/admin/friends/index.ts
deleted file mode 100644 (file)
index dd4df25..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-export * from './friend-add';
-export * from './friend-list';
-export * from './shared';
-export * from './friends.component';
-export * from './friends.routes';
diff --git a/client/src/app/admin/friends/shared/friend.model.ts b/client/src/app/admin/friends/shared/friend.model.ts
deleted file mode 100644 (file)
index 462cc82..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-export interface Friend {
-  id: string;
-  host: string;
-  score: number;
-  createdAt: Date;
-}
diff --git a/client/src/app/admin/friends/shared/friend.service.ts b/client/src/app/admin/friends/shared/friend.service.ts
deleted file mode 100644 (file)
index e974593..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-import { Injectable } from '@angular/core';
-import { Observable } from 'rxjs/Observable';
-import 'rxjs/add/operator/catch';
-import 'rxjs/add/operator/map';
-
-import { Friend } from './friend.model';
-import { AuthHttp, RestExtractor, ResultList } from '../../../shared';
-
-@Injectable()
-export class FriendService {
-  private static BASE_FRIEND_URL: string = '/api/v1/pods/';
-
-  constructor (
-    private authHttp: AuthHttp,
-    private restExtractor: RestExtractor
-  ) {}
-
-  getFriends() {
-    return this.authHttp.get(FriendService.BASE_FRIEND_URL)
-                        .map(this.restExtractor.extractDataList)
-                        .map(this.extractFriends)
-                        .catch((res) => this.restExtractor.handleError(res));
-  }
-
-  makeFriends(notEmptyHosts) {
-    const body = {
-      hosts: notEmptyHosts
-    };
-
-    return this.authHttp.post(FriendService.BASE_FRIEND_URL + 'makefriends', body)
-                        .map(this.restExtractor.extractDataBool)
-                        .catch((res) => this.restExtractor.handleError(res));
-  }
-
-  quitFriends() {
-    return this.authHttp.get(FriendService.BASE_FRIEND_URL + 'quitfriends')
-                        .map(res => res.status)
-                        .catch((res) => this.restExtractor.handleError(res));
-  }
-
-  private extractFriends(result: ResultList) {
-    const friends: Friend[] = result.data;
-    const totalFriends = result.total;
-
-    return { friends, totalFriends };
-  }
-}
diff --git a/client/src/app/admin/friends/shared/index.ts b/client/src/app/admin/friends/shared/index.ts
deleted file mode 100644 (file)
index 0d67163..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './friend.model';
-export * from './friend.service';
diff --git a/client/src/app/admin/index.ts b/client/src/app/admin/index.ts
deleted file mode 100644 (file)
index b75ff9b..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-export * from './friends';
-export * from './requests';
-export * from './users';
-export * from './admin-routing.module';
-export * from './admin.module';
-export * from './admin.component';
-export * from './menu-admin.component';
diff --git a/client/src/app/admin/menu-admin.component.html b/client/src/app/admin/menu-admin.component.html
deleted file mode 100644 (file)
index ad7a7a1..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<menu class="col-md-2 col-sm-3 col-xs-3">
-
-  <div class="panel-block">
-    <div id="panel-users" class="panel-button">
-      <span class="hidden-xs glyphicon glyphicon-user"></span>
-      <a [routerLink]="['/admin/users/list']">List users</a>
-    </div>
-
-    <div id="panel-friends" class="panel-button">
-      <span class="hidden-xs glyphicon glyphicon-cloud"></span>
-      <a [routerLink]="['/admin/friends/list']">List friends</a>
-    </div>
-
-    <div id="panel-request-stats" class="panel-button">
-      <span class="hidden-xs glyphicon glyphicon-stats"></span>
-      <a [routerLink]="['/admin/requests/stats']">Request stats</a>
-    </div>
-
-    <div id="panel-video-abuses" class="panel-button">
-      <span class="hidden-xs glyphicon glyphicon-alert"></span>
-      <a [routerLink]="['/admin/video-abuses/list']">Video abuses</a>
-    </div>
-  </div>
-
-  <div class="panel-block">
-    <div id="panel-quit-administration" class="panel-button">
-      <span class="hidden-xs glyphicon glyphicon-cog"></span>
-      <a [routerLink]="['/videos/list']">Quit admin.</a>
-    </div>
-  </div>
-</menu>
diff --git a/client/src/app/admin/menu-admin.component.ts b/client/src/app/admin/menu-admin.component.ts
deleted file mode 100644 (file)
index 59ffccf..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
-  selector: 'my-menu-admin',
-  templateUrl: './menu-admin.component.html'
-})
-export class MenuAdminComponent { }
diff --git a/client/src/app/admin/requests/index.ts b/client/src/app/admin/requests/index.ts
deleted file mode 100644 (file)
index 236a9ee..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './request-stats';
-export * from './shared';
-export * from './requests.component';
-export * from './requests.routes';
diff --git a/client/src/app/admin/requests/request-stats/index.ts b/client/src/app/admin/requests/request-stats/index.ts
deleted file mode 100644 (file)
index be3a66f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-export * from './request-stats.component';
diff --git a/client/src/app/admin/requests/request-stats/request-stats.component.html b/client/src/app/admin/requests/request-stats/request-stats.component.html
deleted file mode 100644 (file)
index 9dbed17..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<h3>Requests stats</h3>
-
-<div *ngIf="stats !== null">
-  <div class="requests-general">
-    <div>
-      <span class="label-description">Remaining requests:</span>
-      {{ stats.totalRequests }}
-    </div>
-
-    <div>
-      <span class="label-description">Interval seconds between requests:</span>
-      {{ stats.secondsInterval }}
-    </div>
-
-    <div>
-      <span class="label-description">Remaining time before the scheduled request:</span>
-      {{ stats.remainingSeconds }}
-    </div>
-  </div>
-
-  <div class="requests-limit">
-    <div>
-      <span class="label-description">Maximum number of different pods for a scheduled request:</span>
-      {{ stats.requestsLimitPods }}
-    </div>
-
-    <div>
-      <span class="label-description">Maximum number of requests per pod for a scheduled request:</span>
-      {{ stats.requestsLimitPerPod }}
-    </div>
-  </div>
-
-</div>
diff --git a/client/src/app/admin/requests/request-stats/request-stats.component.scss b/client/src/app/admin/requests/request-stats/request-stats.component.scss
deleted file mode 100644 (file)
index 9c68fba..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-.label-description {
-  display: inline-block;
-  font-weight: bold;
-  color: black;
-}
-
-.requests-general {
-  .label-description {
-    width: 320px;
-  }
-}
-
-.requests-limit {
-  margin-top: 20px;
-
-  .label-description {
-    width: 430px;
-  }
-}
diff --git a/client/src/app/admin/requests/request-stats/request-stats.component.ts b/client/src/app/admin/requests/request-stats/request-stats.component.ts
deleted file mode 100644 (file)
index 23b8367..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-import { setInterval } from 'timers'
-import { Component, OnInit, OnDestroy } from '@angular/core';
-
-import { RequestService, RequestStats } from '../shared';
-
-@Component({
-       selector: 'my-request-stats',
-       templateUrl: './request-stats.component.html',
-  styleUrls: [ './request-stats.component.scss' ]
-})
-export class RequestStatsComponent implements OnInit, OnDestroy {
-  stats: RequestStats = null;
-
-  private interval: NodeJS.Timer = null;
-
-  constructor(private requestService: RequestService) {  }
-
-  ngOnInit() {
-    this.getStats();
-    this.runInterval();
-  }
-
-  ngOnDestroy() {
-    if (this.stats !== null && this.stats.secondsInterval !== null) {
-      clearInterval(this.interval);
-    }
-  }
-
-  getStats() {
-    this.requestService.getStats().subscribe(
-      stats => this.stats = stats,
-
-      err => alert(err.text)
-    );
-  }
-
-  private runInterval() {
-    this.interval = setInterval(() => {
-      this.stats.remainingMilliSeconds -= 1000;
-
-      if (this.stats.remainingMilliSeconds <= 0) {
-        setTimeout(() => this.getStats(), this.stats.remainingMilliSeconds + 100);
-      }
-    }, 1000);
-  }
-
-
-}
diff --git a/client/src/app/admin/requests/requests.component.ts b/client/src/app/admin/requests/requests.component.ts
deleted file mode 100644 (file)
index 471112b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
-    template: '<router-outlet></router-outlet>'
-})
-
-export class RequestsComponent {
-}
diff --git a/client/src/app/admin/requests/requests.routes.ts b/client/src/app/admin/requests/requests.routes.ts
deleted file mode 100644 (file)
index 70fbf41..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Routes } from '@angular/router';
-
-import { RequestsComponent } from './requests.component';
-import { RequestStatsComponent } from './request-stats';
-
-export const RequestsRoutes: Routes = [
-  {
-      path: 'requests',
-      component: RequestsComponent,
-      children: [
-        {
-          path: '',
-          redirectTo: 'stats',
-          pathMatch: 'full'
-        },
-        {
-          path: 'stats',
-          component: RequestStatsComponent,
-          data: {
-            meta: {
-              titleSuffix: ' - Request stats'
-            }
-          }
-        }
-      ]
-    }
-];
diff --git a/client/src/app/admin/requests/shared/index.ts b/client/src/app/admin/requests/shared/index.ts
deleted file mode 100644 (file)
index 32ab576..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './request-stats.model';
-export * from './request.service';
diff --git a/client/src/app/admin/requests/shared/request-stats.model.ts b/client/src/app/admin/requests/shared/request-stats.model.ts
deleted file mode 100644 (file)
index f658c46..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-export interface Request {
-  request: any;
-  to: any;
-}
-
-export class RequestStats {
-  requestsLimitPods: number;
-  requestsLimitPerPod: number;
-  milliSecondsInterval: number;
-  remainingMilliSeconds: number;
-  totalRequests: number;
-
-  constructor(hash: {
-    requestsLimitPods: number,
-    requestsLimitPerPod: number,
-    milliSecondsInterval: number,
-    remainingMilliSeconds: number,
-    totalRequests: number;
-  }) {
-    this.requestsLimitPods = hash.requestsLimitPods;
-    this.requestsLimitPerPod = hash.requestsLimitPerPod;
-    this.milliSecondsInterval = hash.milliSecondsInterval;
-    this.remainingMilliSeconds = hash.remainingMilliSeconds;
-    this.totalRequests = hash.totalRequests;
-  }
-
-  get remainingSeconds() {
-    return Math.floor(this.remainingMilliSeconds / 1000);
-  }
-
-  get secondsInterval() {
-    return Math.floor(this.milliSecondsInterval / 1000);
-  }
-
-}
diff --git a/client/src/app/admin/requests/shared/request.service.ts b/client/src/app/admin/requests/shared/request.service.ts
deleted file mode 100644 (file)
index 55b28bc..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Injectable } from '@angular/core';
-import { Observable } from 'rxjs/Observable';
-import 'rxjs/add/operator/catch';
-import 'rxjs/add/operator/map';
-
-import { RequestStats } from './request-stats.model';
-import { AuthHttp, RestExtractor } from '../../../shared';
-
-@Injectable()
-export class RequestService {
-  private static BASE_REQUEST_URL: string = '/api/v1/requests/';
-
-  constructor (
-    private authHttp: AuthHttp,
-    private restExtractor: RestExtractor
-  ) {}
-
-  getStats(): Observable<RequestStats> {
-    return this.authHttp.get(RequestService.BASE_REQUEST_URL + 'stats')
-                        .map(this.restExtractor.extractDataGet)
-                        .map((data) => new RequestStats(data))
-                        .catch((res) => this.restExtractor.handleError(res));
-  }
-}
diff --git a/client/src/app/admin/users/index.ts b/client/src/app/admin/users/index.ts
deleted file mode 100644 (file)
index e98a81f..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-export * from './shared';
-export * from './user-add';
-export * from './user-list';
-export * from './users.component';
-export * from './users.routes';
diff --git a/client/src/app/admin/users/shared/index.ts b/client/src/app/admin/users/shared/index.ts
deleted file mode 100644 (file)
index e17ee5c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-export * from './user.service';
diff --git a/client/src/app/admin/users/shared/user.service.ts b/client/src/app/admin/users/shared/user.service.ts
deleted file mode 100644 (file)
index d9005b2..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-import { Injectable } from '@angular/core';
-import 'rxjs/add/operator/catch';
-import 'rxjs/add/operator/map';
-
-import { AuthHttp, RestExtractor, ResultList, User } from '../../../shared';
-
-@Injectable()
-export class UserService {
-  // TODO: merge this constant with account
-  private static BASE_USERS_URL = '/api/v1/users/';
-
-  constructor(
-    private authHttp: AuthHttp,
-    private restExtractor: RestExtractor
-  ) {}
-
-  addUser(username: string, password: string) {
-    const body = {
-      username,
-      password
-    };
-
-    return this.authHttp.post(UserService.BASE_USERS_URL, body)
-                        .map(this.restExtractor.extractDataBool)
-                        .catch(this.restExtractor.handleError);
-  }
-
-  getUsers() {
-    return this.authHttp.get(UserService.BASE_USERS_URL)
-                 .map(this.restExtractor.extractDataList)
-                 .map(this.extractUsers)
-                 .catch((res) => this.restExtractor.handleError(res));
-  }
-
-  removeUser(user: User) {
-    return this.authHttp.delete(UserService.BASE_USERS_URL + user.id);
-  }
-
-  private extractUsers(result: ResultList) {
-    const usersJson = result.data;
-    const totalUsers = result.total;
-    const users = [];
-    for (const userJson of usersJson) {
-      users.push(new User(userJson));
-    }
-
-    return { users, totalUsers };
-  }
-}
diff --git a/client/src/app/admin/users/user-add/index.ts b/client/src/app/admin/users/user-add/index.ts
deleted file mode 100644 (file)
index 66d5ca0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-export * from './user-add.component';
diff --git a/client/src/app/admin/users/user-add/user-add.component.html b/client/src/app/admin/users/user-add/user-add.component.html
deleted file mode 100644 (file)
index 9b76c7c..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<h3>Add user</h3>
-
-<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
-
-<form role="form" (ngSubmit)="addUser()" [formGroup]="form">
-  <div class="form-group">
-    <label for="username">Username</label>
-    <input
-      type="text" class="form-control" id="username" placeholder="Username"
-      formControlName="username"
-    >
-    <div *ngIf="formErrors.username" class="alert alert-danger">
-      {{ formErrors.username }}
-    </div>
-  </div>
-
-  <div class="form-group">
-    <label for="password">Password</label>
-    <input
-      type="password" class="form-control" id="password" placeholder="Password"
-      formControlName="password"
-    >
-    <div *ngIf="formErrors.password" class="alert alert-danger">
-      {{ formErrors.password }}
-    </div>
-  </div>
-
-  <input type="submit" value="Add user" class="btn btn-default" [disabled]="!form.valid">
-</form>
diff --git a/client/src/app/admin/users/user-add/user-add.component.ts b/client/src/app/admin/users/user-add/user-add.component.ts
deleted file mode 100644 (file)
index ab96fb0..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { FormBuilder, FormGroup } from '@angular/forms';
-import { Router } from '@angular/router';
-
-import { UserService } from '../shared';
-import { FormReactive, USER_USERNAME, USER_PASSWORD } from '../../../shared';
-
-@Component({
-  selector: 'my-user-add',
-  templateUrl: './user-add.component.html'
-})
-export class UserAddComponent extends FormReactive implements OnInit {
-  error: string = null;
-
-  form: FormGroup;
-  formErrors = {
-    'username': '',
-    'password': ''
-  };
-  validationMessages = {
-    'username': USER_USERNAME.MESSAGES,
-    'password': USER_PASSWORD.MESSAGES,
-  };
-
-  constructor(
-    private formBuilder: FormBuilder,
-    private router: Router,
-    private userService: UserService
-  ) {
-    super();
-  }
-
-  buildForm() {
-    this.form = this.formBuilder.group({
-      username: [ '', USER_USERNAME.VALIDATORS ],
-      password: [ '', USER_PASSWORD.VALIDATORS ],
-    });
-
-    this.form.valueChanges.subscribe(data => this.onValueChanged(data));
-  }
-
-  ngOnInit() {
-    this.buildForm();
-  }
-
-  addUser() {
-    this.error = null;
-
-    const { username, password } = this.form.value;
-
-    this.userService.addUser(username, password).subscribe(
-      ok => this.router.navigate([ '/admin/users/list' ]),
-
-      err => this.error = err.text
-    );
-  }
-}
diff --git a/client/src/app/admin/users/user-list/index.ts b/client/src/app/admin/users/user-list/index.ts
deleted file mode 100644 (file)
index 51fbefa..0000000
+++ /dev/null
@@ -1 +0,0 @@
-export * from './user-list.component';
diff --git a/client/src/app/admin/users/user-list/user-list.component.html b/client/src/app/admin/users/user-list/user-list.component.html
deleted file mode 100644 (file)
index 36193d1..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<h3>Users list</h3>
-
-<table class="table table-hover">
-  <thead>
-    <tr>
-      <th class="table-column-id">ID</th>
-      <th>Username</th>
-      <th>Created Date</th>
-      <th class="text-right">Remove</th>
-    </tr>
-  </thead>
-
-  <tbody>
-    <tr *ngFor="let user of users">
-      <td>{{ user.id }}</td>
-      <td>{{ user.username }}</td>
-      <td>{{ user.createdAt | date: 'medium' }}</td>
-      <td class="text-right">
-        <span class="glyphicon glyphicon-remove" *ngIf="!user.isAdmin()" (click)="removeUser(user)"></span>
-      </td>
-    </tr>
-  </tbody>
-</table>
-
-<a class="add-user btn btn-success pull-right" [routerLink]="['/admin/users/add']">
-  <span class="glyphicon glyphicon-plus"></span>
-  Add user
-</a>
diff --git a/client/src/app/admin/users/user-list/user-list.component.scss b/client/src/app/admin/users/user-list/user-list.component.scss
deleted file mode 100644 (file)
index e9f61e9..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-.glyphicon-remove {
-  cursor: pointer;
-}
-
-.add-user {
-  margin-top: 10px;
-}
diff --git a/client/src/app/admin/users/user-list/user-list.component.ts b/client/src/app/admin/users/user-list/user-list.component.ts
deleted file mode 100644 (file)
index 03f4e5c..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-import { User } from '../../../shared';
-import { UserService } from '../shared';
-
-@Component({
-  selector: 'my-user-list',
-  templateUrl: './user-list.component.html',
-  styleUrls: [ './user-list.component.scss' ]
-})
-export class UserListComponent implements OnInit {
-  totalUsers: number;
-  users: User[];
-
-  constructor(private userService: UserService) {}
-
-  ngOnInit() {
-    this.getUsers();
-  }
-
-  getUsers() {
-    this.userService.getUsers().subscribe(
-      ({ users, totalUsers }) => {
-        this.users = users;
-        this.totalUsers = totalUsers;
-      },
-
-      err => alert(err.text)
-    );
-  }
-
-
-  removeUser(user: User) {
-    if (confirm('Are you sure?')) {
-      this.userService.removeUser(user).subscribe(
-        () => this.getUsers(),
-
-        err => alert(err.text)
-      );
-    }
-  }
-}
diff --git a/client/src/app/admin/users/users.component.ts b/client/src/app/admin/users/users.component.ts
deleted file mode 100644 (file)
index 37e3b15..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
-    template: '<router-outlet></router-outlet>'
-})
-
-export class UsersComponent {
-}
diff --git a/client/src/app/admin/users/users.routes.ts b/client/src/app/admin/users/users.routes.ts
deleted file mode 100644 (file)
index 92e3e43..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Routes } from '@angular/router';
-
-import { UsersComponent } from './users.component';
-import { UserAddComponent } from './user-add';
-import { UserListComponent } from './user-list';
-
-export const UsersRoutes: Routes = [
-  {
-      path: 'users',
-      component: UsersComponent,
-      children: [
-        {
-          path: '',
-          redirectTo: 'list',
-          pathMatch: 'full'
-        },
-        {
-          path: 'list',
-          component: UserListComponent,
-          data: {
-            meta: {
-              titleSuffix: ' - Users list'
-            }
-          }
-        },
-        {
-          path: 'add',
-          component: UserAddComponent,
-          data: {
-            meta: {
-              titleSuffix: ' - Add a user'
-            }
-          }
-        }
-      ]
-    }
-];
diff --git a/client/src/app/admin/video-abuses/index.ts b/client/src/app/admin/video-abuses/index.ts
deleted file mode 100644 (file)
index 7f5e65f..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './video-abuse-list';
-export * from './video-abuses.component';
-export * from './video-abuses.routes';
diff --git a/client/src/app/admin/video-abuses/video-abuse-list/index.ts b/client/src/app/admin/video-abuses/video-abuse-list/index.ts
deleted file mode 100644 (file)
index 3f2ed17..0000000
+++ /dev/null
@@ -1 +0,0 @@
-export * from './video-abuse-list.component';
diff --git a/client/src/app/admin/video-abuses/video-abuse-list/video-abuse-list.component.html b/client/src/app/admin/video-abuses/video-abuse-list/video-abuse-list.component.html
deleted file mode 100644 (file)
index 4604357..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<h3>Video abuses list</h3>
-
-<table class="table table-hover">
-  <thead>
-    <tr>
-      <th class="cell-id">ID</th>
-      <th class="cell-reason">Reason</th>
-      <th>Reporter pod host</th>
-      <th>Reporter username</th>
-      <th>Video</th>
-      <th>Created at</th>
-    </tr>
-  </thead>
-
-  <tbody>
-    <tr *ngFor="let videoAbuse of videoAbuses">
-      <td>{{ videoAbuse.id }}</td>
-      <td>{{ videoAbuse.reason }}</td>
-      <td>{{ videoAbuse.reporterPodHost }}</td>
-      <td>{{ videoAbuse.reporterUsername }}</td>
-      <td>
-        <a [routerLink]="buildVideoLink(videoAbuse)" title="Go to video">{{ videoAbuse.videoId }}</a>
-      </td>
-      <td>{{ videoAbuse.createdAt | date: 'medium' }}</td>
-    </tr>
-  </tbody>
-</table>
diff --git a/client/src/app/admin/video-abuses/video-abuse-list/video-abuse-list.component.scss b/client/src/app/admin/video-abuses/video-abuse-list/video-abuse-list.component.scss
deleted file mode 100644 (file)
index a094f74..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-.cell-id {
-  width: 40px;
-}
-
-.cell-reason {
-  width: 200px;
-}
diff --git a/client/src/app/admin/video-abuses/video-abuse-list/video-abuse-list.component.ts b/client/src/app/admin/video-abuses/video-abuse-list/video-abuse-list.component.ts
deleted file mode 100644 (file)
index de58bba..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-import { setInterval } from 'timers'
-import { Component, OnInit } from '@angular/core';
-
-import { VideoAbuseService, VideoAbuse} from '../../../shared';
-
-@Component({
-       selector: 'my-video-abuse-list',
-       templateUrl: './video-abuse-list.component.html',
-  styleUrls: [ './video-abuse-list.component.scss' ]
-})
-export class VideoAbuseListComponent implements OnInit {
-  videoAbuses: VideoAbuse[];
-
-  constructor(private videoAbuseService: VideoAbuseService) {  }
-
-  ngOnInit() {
-    this.getVideoAbuses();
-  }
-
-  buildVideoLink(videoAbuse: VideoAbuse) {
-    return `/videos/${videoAbuse.videoId}`;
-  }
-
-  private getVideoAbuses() {
-    this.videoAbuseService.getVideoAbuses().subscribe(
-      res => this.videoAbuses = res.videoAbuses,
-
-      err => alert(err.text)
-    );
-  }
-}
diff --git a/client/src/app/admin/video-abuses/video-abuses.component.ts b/client/src/app/admin/video-abuses/video-abuses.component.ts
deleted file mode 100644 (file)
index 001f27e..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
-  template: '<router-outlet></router-outlet>'
-})
-
-export class VideoAbusesComponent {
-}
diff --git a/client/src/app/admin/video-abuses/video-abuses.routes.ts b/client/src/app/admin/video-abuses/video-abuses.routes.ts
deleted file mode 100644 (file)
index 26a7618..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Routes } from '@angular/router';
-
-import { VideoAbusesComponent } from './video-abuses.component';
-import { VideoAbuseListComponent } from './video-abuse-list';
-
-export const VideoAbusesRoutes: Routes = [
-  {
-      path: 'video-abuses',
-      component: VideoAbusesComponent
-      ,
-      children: [
-        {
-          path: '',
-          redirectTo: 'list',
-          pathMatch: 'full'
-        },
-        {
-          path: 'list',
-          component: VideoAbuseListComponent,
-          data: {
-            meta: {
-              titleSuffix: ' - Video abuses list'
-            }
-          }
-        }
-      ]
-    }
-];
index 900a4c5b6d064b4cc048f65c73b8f297d117697e..f9465dc9c4ed39bccf7fd56b13d2f8990e2076d8 100644 (file)
@@ -6,6 +6,10 @@ const routes: Routes = [
     path: '',
     redirectTo: '/videos/list',
     pathMatch: 'full'
+  },
+  {
+    path: 'admin',
+    loadChildren: './+admin#AdminModule'
   }
 ];
 
index 10526e81380d04b8333c4e18973601117751411d..482a9b5b7d85c223767d8f64f1dbbdbcd9460b6c 100644 (file)
@@ -11,7 +11,6 @@ import { AppComponent } from './app.component';
 import { AppState } from './app.service';
 
 import { AccountModule } from './account';
-import { AdminModule } from './admin';
 import { CoreModule } from './core';
 import { LoginModule } from './login';
 import { SharedModule } from './shared';
@@ -47,7 +46,6 @@ const APP_PROVIDERS = [
     MetaModule.forRoot(metaConfig),
 
     AccountModule,
-    AdminModule,
     CoreModule,
     LoginModule,
     SharedModule,
index 27e6ee1fb765b2d73aa5e392db9f7001a79bcfc9..09a6f92f5542f71e6a3e1be6269a06959d95b0d4 100644 (file)
@@ -4,7 +4,7 @@ import { HttpModule } from '@angular/http';
 import { RouterModule } from '@angular/router';
 
 import { AuthService } from './auth';
-import { MenuComponent } from './menu';
+import { MenuComponent, MenuAdminComponent } from './menu';
 import { throwIfAlreadyLoaded } from './module-import-guard';
 
 @NgModule({
@@ -13,8 +13,14 @@ import { throwIfAlreadyLoaded } from './module-import-guard';
     HttpModule,
     RouterModule
   ],
-  declarations: [ MenuComponent ],
-  exports: [ MenuComponent ],
+  declarations: [
+    MenuComponent,
+    MenuAdminComponent
+  ],
+  exports: [
+    MenuComponent,
+    MenuAdminComponent
+  ],
   providers: [ AuthService ]
 })
 export class CoreModule {
index d07a1144cb2c83fd6fdbe5de91f8efb97515d311..ff40f26e1ac3f9c865951323d788710902488446 100644 (file)
@@ -1 +1,2 @@
 export * from './menu.component';
+export * from './menu-admin.component';
diff --git a/client/src/app/core/menu/menu-admin.component.html b/client/src/app/core/menu/menu-admin.component.html
new file mode 100644 (file)
index 0000000..ad7a7a1
--- /dev/null
@@ -0,0 +1,31 @@
+<menu class="col-md-2 col-sm-3 col-xs-3">
+
+  <div class="panel-block">
+    <div id="panel-users" class="panel-button">
+      <span class="hidden-xs glyphicon glyphicon-user"></span>
+      <a [routerLink]="['/admin/users/list']">List users</a>
+    </div>
+
+    <div id="panel-friends" class="panel-button">
+      <span class="hidden-xs glyphicon glyphicon-cloud"></span>
+      <a [routerLink]="['/admin/friends/list']">List friends</a>
+    </div>
+
+    <div id="panel-request-stats" class="panel-button">
+      <span class="hidden-xs glyphicon glyphicon-stats"></span>
+      <a [routerLink]="['/admin/requests/stats']">Request stats</a>
+    </div>
+
+    <div id="panel-video-abuses" class="panel-button">
+      <span class="hidden-xs glyphicon glyphicon-alert"></span>
+      <a [routerLink]="['/admin/video-abuses/list']">Video abuses</a>
+    </div>
+  </div>
+
+  <div class="panel-block">
+    <div id="panel-quit-administration" class="panel-button">
+      <span class="hidden-xs glyphicon glyphicon-cog"></span>
+      <a [routerLink]="['/videos/list']">Quit admin.</a>
+    </div>
+  </div>
+</menu>
diff --git a/client/src/app/core/menu/menu-admin.component.ts b/client/src/app/core/menu/menu-admin.component.ts
new file mode 100644 (file)
index 0000000..59ffccf
--- /dev/null
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+  selector: 'my-menu-admin',
+  templateUrl: './menu-admin.component.html'
+})
+export class MenuAdminComponent { }