163 changed files with 1599 additions and 3476 deletions
			
			
		| @ -0,0 +1,39 @@ | |||
| import json | |||
| import multiprocessing | |||
| import os | |||
| 
 | |||
| workers_per_core_str = os.getenv("WORKERS_PER_CORE", "1") | |||
| web_concurrency_str = os.getenv("WEB_CONCURRENCY", None) | |||
| host = os.getenv("HOST", "unix:/tmp/gunicorn.sock") | |||
| port = os.getenv("PORT", "80") | |||
| use_loglevel = os.getenv("LOG_LEVEL", "info") | |||
| use_bind = host if "unix" in host else f"{host}:{port}" | |||
| 
 | |||
| cores = multiprocessing.cpu_count() | |||
| workers_per_core = float(workers_per_core_str) | |||
| default_web_concurrency = workers_per_core * cores | |||
| if web_concurrency_str: | |||
|     web_concurrency = int(web_concurrency_str) | |||
|     assert web_concurrency > 0 | |||
| else: | |||
|     web_concurrency = max(int(default_web_concurrency), 2) | |||
| 
 | |||
| # Gunicorn config variables | |||
| loglevel = use_loglevel | |||
| workers = web_concurrency | |||
| bind = use_bind | |||
| keepalive = 120 | |||
| errorlog = "-" | |||
| 
 | |||
| # For debugging and testing | |||
| log_data = { | |||
|     "loglevel": loglevel, | |||
|     "workers": workers, | |||
|     "bind": bind, | |||
|     "worker-tmp-dir": "/dev/shm", | |||
|     # Additional, non-gunicorn variables | |||
|     "workers_per_core": workers_per_core, | |||
|     "host": host, | |||
|     "port": port, | |||
| } | |||
| print(json.dumps(log_data)) | |||
| @ -0,0 +1,32 @@ | |||
| import os | |||
| from os.path import isdir | |||
| DEFAULT_MODULE_LOCATIONS = [("app.main", "/app/app/main.py"), ("main", "/app/main.py")] | |||
| DEFAULT_GUNICORN_CONF = [(None, "/app/gunicorn_config.py"), (None, "/app/startup/gunicorn_config.py")] | |||
| 
 | |||
| 
 | |||
| def get_location(pot): | |||
|     for i in pot: | |||
|         if not isdir(i[1]): | |||
|             continue | |||
|     # Last record will be "defauilt" | |||
|     return i[0] if i[0] else i[1] | |||
| 
 | |||
| 
 | |||
| if __name__ == "__main__": | |||
|     MODULE_NAME = os.getenv("MODULE_LOCATION", get_location(DEFAULT_MODULE_LOCATIONS)) | |||
|     VARIABLE_NAME = os.getenv("VARIABLE_NAME", "app") | |||
|     APP_MODULE = os.getenv("APP_MODULE", f"{MODULE_NAME}:{VARIABLE_NAME}") | |||
|     GUNICORN_CONF = os.getenv("GUNICORN_CONF", get_location(DEFAULT_GUNICORN_CONF)) | |||
|     OPTIONS = [ | |||
|         "-k", | |||
|         "uvicorn.workers.UvicornWorker", | |||
|         "-c", | |||
|         f"{GUNICORN_CONF} {APP_MODULE}" | |||
|     ] | |||
| 
 | |||
|     # Set envs | |||
|     os.putenv("VARIABLE_NAME", VARIABLE_NAME) | |||
|     os.putenv("APP_MODULE", APP_MODULE) | |||
|     os.putenv("GUNICORN_CONF", GUNICORN_CONF) | |||
| 
 | |||
|     os.system(f"gunicorn -k uvicorn.workers.UvicornWorker -c {GUNICORN_CONF} {APP_MODULE}") | |||
| @ -0,0 +1,31 @@ | |||
| import { NgModule } from '@angular/core'; | |||
| import { CommonModule } from '@angular/common'; | |||
| 
 | |||
| import { LayoutComponent } from './layout/layout.component'; | |||
| import {MatSidenavModule} from "@angular/material/sidenav"; | |||
| import {MatToolbarModule} from "@angular/material/toolbar"; | |||
| import {MatListModule} from "@angular/material/list"; | |||
| import {MatIconModule} from "@angular/material/icon"; | |||
| import {MatButtonModule} from "@angular/material/button"; | |||
| import {FlexLayoutModule} from "@angular/flex-layout"; | |||
| import {RouterModule} from "@angular/router"; | |||
| 
 | |||
| 
 | |||
| 
 | |||
| @NgModule({ | |||
|   declarations: [LayoutComponent], | |||
|   imports: [ | |||
|     CommonModule, | |||
|     MatSidenavModule, | |||
|     MatToolbarModule, | |||
|     MatListModule, | |||
|     MatIconModule, | |||
|     MatButtonModule, | |||
|     FlexLayoutModule, | |||
|     RouterModule | |||
|   ], | |||
|   exports: [ | |||
| 
 | |||
|   ] | |||
| }) | |||
| export class LayoutModule { } | |||
| @ -0,0 +1,56 @@ | |||
| <div style="height: 100vh;"> | |||
| 
 | |||
| 
 | |||
|   <mat-toolbar color="primary"> | |||
|     <mat-toolbar-row> | |||
|       <button mat-icon-button (click)="sidenav.toggle()" fxShow="true" fxHide.gt-sm> | |||
|         <mat-icon>menu</mat-icon> | |||
|       </button> | |||
|       <span>{{config.applicationName}}</span> | |||
|       <span class="example-spacer"></span> | |||
|       <div fxShow="true" fxHide.lt-md="true"> | |||
| 
 | |||
|         <!-- The following menu items will be hidden on both SM and XS screen sizes --> | |||
| 
 | |||
|         <a *ngFor="let item of menu" [routerLink]="item.link" mat-button> | |||
|           <mat-icon>{{item.icon}}</mat-icon> | |||
|           {{item.text}} | |||
|         </a> | |||
| 
 | |||
|       </div> | |||
|     </mat-toolbar-row> | |||
| 
 | |||
|   </mat-toolbar> | |||
| 
 | |||
|   <mat-sidenav-container fxFlexFill> | |||
|     <mat-sidenav #sidenav> | |||
|       <mat-nav-list> | |||
|         <a href="#" mat-list-item> | |||
|           <mat-icon>notifications</mat-icon> | |||
|           Notifications | |||
|         </a> | |||
|         <a href="#" mat-list-item> | |||
|           <mat-icon>message</mat-icon> | |||
|           Messages</a> | |||
|         <a href="#" mat-list-item> | |||
|           <mat-icon>account_box</mat-icon> | |||
|           My Account | |||
|         </a> | |||
|         <a href="#" mat-list-item> | |||
|           <mat-icon>lock</mat-icon> | |||
|           My Account | |||
|         </a> | |||
|         <a (click)="sidenav.toggle()" mat-list-item> | |||
|           <mat-icon>close</mat-icon> Close | |||
|         </a> | |||
|       </mat-nav-list> | |||
|     </mat-sidenav> | |||
|     <mat-sidenav-content fxFlexFill> | |||
|       <router-outlet></router-outlet> | |||
|     </mat-sidenav-content> | |||
|   </mat-sidenav-container> | |||
| 
 | |||
| 
 | |||
| 
 | |||
| 
 | |||
| </div> | |||
| @ -0,0 +1,19 @@ | |||
| .sidenav-container { | |||
|   height: 100%; | |||
| } | |||
| 
 | |||
| .sidenav { | |||
|   width: 200px; | |||
| } | |||
| 
 | |||
| .sidenav .mat-toolbar { | |||
|   background: inherit; | |||
| } | |||
| 
 | |||
| .mat-toolbar.mat-primary { | |||
|   position: sticky; | |||
|   top: 0; | |||
|   z-index: 1; | |||
| } | |||
| 
 | |||
| 
 | |||
| @ -0,0 +1,25 @@ | |||
| import { async, ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| 
 | |||
| import { LayoutComponent } from './layout.component'; | |||
| 
 | |||
| describe('LayoutComponent', () => { | |||
|   let component: LayoutComponent; | |||
|   let fixture: ComponentFixture<LayoutComponent>; | |||
| 
 | |||
|   beforeEach(async(() => { | |||
|     TestBed.configureTestingModule({ | |||
|       declarations: [ LayoutComponent ] | |||
|     }) | |||
|     .compileComponents(); | |||
|   })); | |||
| 
 | |||
|   beforeEach(() => { | |||
|     fixture = TestBed.createComponent(LayoutComponent); | |||
|     component = fixture.componentInstance; | |||
|     fixture.detectChanges(); | |||
|   }); | |||
| 
 | |||
|   it('should create', () => { | |||
|     expect(component).toBeTruthy(); | |||
|   }); | |||
| }); | |||
| @ -0,0 +1,29 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import {Observable} from "rxjs"; | |||
| import {BreakpointObserver, Breakpoints} from "@angular/cdk/layout"; | |||
| import {map, shareReplay} from "rxjs/operators"; | |||
| import {ConfigService} from "../../services/config.service"; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'app-layout', | |||
|   templateUrl: './layout.component.html', | |||
|   styleUrls: ['./layout.component.scss'] | |||
| }) | |||
| export class LayoutComponent implements OnInit { | |||
| 
 | |||
|   isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset) | |||
|     .pipe( | |||
|       map(result => result.matches), | |||
|       shareReplay() | |||
|     ); | |||
| 
 | |||
|   menu: Array<{link: Array<string>, icon: string, text: string}> = [ | |||
|     { link: ["/page/dashboard"], icon: "home", text: "Dashboard"} | |||
|   ]; | |||
| 
 | |||
|   constructor(private breakpointObserver: BreakpointObserver, public config: ConfigService) {} | |||
|   ngOnInit(): void { | |||
|     console.log("Layout") | |||
|   } | |||
| 
 | |||
| } | |||
| @ -1,5 +0,0 @@ | |||
| <div class="mdl-layout mdl-js-layout"> | |||
|   <main class="mdl-layout__content"> | |||
|     <router-outlet></router-outlet> | |||
|   </main> | |||
| </div> | |||
| @ -1,11 +0,0 @@ | |||
| @import '~theme/helpers'; | |||
| 
 | |||
| app-blank-layout .mdl-layout .mdl-layout__content { | |||
|   padding: 16px; | |||
|   display: flex; | |||
| } | |||
| 
 | |||
| // FIXME: responsibility leak | |||
| .not-found .mdl-layout__content { | |||
|   background-image: url('#{$image-path}/404.svg'); | |||
| } | |||
| @ -1,18 +0,0 @@ | |||
| import { Component, HostBinding } from '@angular/core'; | |||
| import { Router } from '@angular/router'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'app-blank-layout', | |||
|   styleUrls: ['./blank-layout.component.scss'], | |||
|   templateUrl: './blank-layout.component.html', | |||
| }) | |||
| export class BlankLayoutComponent { | |||
|   // FIXME: responsibility leak
 | |||
|   @HostBinding('class.not-found') private get notFound() { | |||
|     return this.router.url === '/pages/404'; | |||
|   } | |||
| 
 | |||
|   constructor( | |||
|     private router: Router, | |||
|   ) { } | |||
| } | |||
| @ -1 +0,0 @@ | |||
| export { BlankLayoutComponent } from './blank-layout.component'; | |||
| @ -1,89 +0,0 @@ | |||
| <div class="mdl-layout mdl-js-layout mdl-layout--fixed-drawer mdl-layout--fixed-header has-drawer"> | |||
|   <div class="mdl-layout__header"> | |||
|     <base-page-top> | |||
| 
 | |||
|       <span class="mdl-layout__title">{{title}}</span> | |||
|       <nav class="mdl-navigation"> | |||
|         <base-menu-item *ngFor="let item of menu" [data]="item"></base-menu-item> | |||
| 
 | |||
|         <div class="mdl-layout-spacer"></div> | |||
|       </nav> | |||
| 
 | |||
|       <div class="mdl-layout-spacer"></div> | |||
| 
 | |||
| 
 | |||
|       <!-- | |||
|       <div class="mdl-textfield mdl-js-textfield mdl-textfield--expandable search"> | |||
|         <label class="mdl-button mdl-js-button mdl-button--icon" for="search"> | |||
|           <i class="material-icons">search</i> | |||
|         </label> | |||
| 
 | |||
|         <div class="mdl-textfield__expandable-holder"> | |||
|           <input class="mdl-textfield__input" type="text" id="search"/> | |||
|           <label class="mdl-textfield__label" for="search">Enter your query...</label> | |||
|         </div> | |||
|       </div>--> | |||
| 
 | |||
| 
 | |||
| 
 | |||
|       <div class="avatar-dropdown" id="icon" *ngIf="auth.user"> | |||
|         <span>Logged in as {{auth.user?.username}}</span> | |||
|         <!--<img src="assets/images/Icon_header.png">--> | |||
|       </div> | |||
|       <ul | |||
|         class="mdl-menu mdl-list mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect mdl-shadow--2dp account-dropdown" | |||
|         for="icon"> | |||
|         <li class="mdl-list__item mdl-list__item--two-line"> | |||
|             <span class="mdl-list__item-primary-content"> | |||
|               <span class="material-icons mdl-list__item-avatar"></span> | |||
|               <span>{{ auth.user?.username }}</span> | |||
|               <span class="mdl-list__item-sub-title">{{ auth.user?.email }}</span> | |||
|           </span> | |||
|         </li> | |||
|         <li class="list__item--border-top"></li> | |||
|         <li class="mdl-menu__item mdl-list__item"> | |||
|             <span class="mdl-list__item-primary-content" (click)="router.navigate(['/user/edit'])"> | |||
|               <i class="material-icons mdl-list__item-icon">account_circle</i> | |||
|               My account | |||
|             </span> | |||
|         </li> | |||
|         <!--<li class="mdl-menu__item mdl-list__item"> | |||
|             <span class="mdl-list__item-primary-content"> | |||
|               <i class="material-icons mdl-list__item-icon">check_box</i> | |||
|               My tasks | |||
|             </span> | |||
|           <span class="mdl-list__item-secondary-content"> | |||
|               <span class="label background-color--primary">3 new</span> | |||
|             </span> | |||
|         </li> | |||
|         <li class="mdl-menu__item mdl-list__item"> | |||
|             <span class="mdl-list__item-primary-content"> | |||
|               <i class="material-icons mdl-list__item-icon">perm_contact_calendar</i> | |||
|               My events | |||
|             </span> | |||
|         </li>--> | |||
|         <!--<li class="list__item--border-top"></li> | |||
|         <li class="mdl-menu__item mdl-list__item"> | |||
|             <span class="mdl-list__item-primary-content"> | |||
|               <i class="material-icons mdl-list__item-icon">settings</i> | |||
|               Settings | |||
|             </span> | |||
|         </li>--> | |||
|         <li class="mdl-menu__item mdl-list__item"> | |||
|             <span class="mdl-list__item-primary-content" (click)="logout()"> | |||
|               <i class="material-icons mdl-list__item-icon text-color--secondary">exit_to_app</i> | |||
|               Log out | |||
|             </span> | |||
|         </li> | |||
|       </ul> | |||
| 
 | |||
|     </base-page-top> | |||
|   </div> | |||
| 
 | |||
|   <!--<div class="mdl-layout__drawer"> | |||
|     <app-sidebar></app-sidebar> | |||
|   </div>--> | |||
|   <main class="mdl-layout__content"> | |||
|     <router-outlet></router-outlet> | |||
|   </main> | |||
| </div> | |||
| @ -1,28 +0,0 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import { Router } from '@angular/router'; | |||
| 
 | |||
| import { AuthService } from '@services/*'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'app-common-layout', | |||
|   templateUrl: './common-layout.component.html', | |||
| }) | |||
| export class CommonLayoutComponent implements OnInit { | |||
|   public title = 'Wireguard Manager'; | |||
|   public menu = [ | |||
|     { name: 'Dashboard', link: '/app/dashboard', icon: 'dashboard' }, | |||
|   ]; | |||
| 
 | |||
| 
 | |||
|   constructor(public auth: AuthService, | |||
|               public router: Router) {} | |||
| 
 | |||
|   public ngOnInit() { | |||
| 
 | |||
|   } | |||
| 
 | |||
|   public logout() { | |||
|     this.auth.logout() | |||
|       .subscribe(res => this.router.navigate(['/user/login'])); | |||
|   } | |||
| } | |||
| @ -1 +0,0 @@ | |||
| export { CommonLayoutComponent } from './common-layout.component'; | |||
| @ -1 +0,0 @@ | |||
| export { LayoutsModule } from './layouts.module'; | |||
| @ -1,32 +0,0 @@ | |||
| import { CommonModule } from '@angular/common'; | |||
| import { NgModule } from '@angular/core'; | |||
| import { RouterModule } from '@angular/router'; | |||
| 
 | |||
| import { BlankLayoutCardComponent } from 'app/components/blank-layout-card'; | |||
| import { MessageMenuComponent } from 'app/components/message-menu'; | |||
| import { NotificationMenuComponent } from 'app/components/notification-menu'; | |||
| import { SidebarComponent } from 'app/components/sidebar'; | |||
| import { ThemeModule } from 'theme'; | |||
| import { BlankLayoutComponent } from './blank-layout'; | |||
| import { CommonLayoutComponent } from './common-layout'; | |||
| 
 | |||
| @NgModule({ | |||
|   imports: [ | |||
|     CommonModule, | |||
|     ThemeModule, | |||
|     RouterModule, | |||
|   ], | |||
|   declarations: [ | |||
|     CommonLayoutComponent, | |||
|     BlankLayoutComponent, | |||
|     BlankLayoutCardComponent, | |||
|     SidebarComponent, | |||
|     MessageMenuComponent, | |||
|     NotificationMenuComponent, | |||
|   ], | |||
|   exports: [ | |||
|     CommonLayoutComponent, | |||
|     BlankLayoutComponent, | |||
|   ], | |||
| }) | |||
| export class LayoutsModule { } | |||
| @ -1,13 +1,12 @@ | |||
| import { Component, HostBinding } from '@angular/core'; | |||
| 
 | |||
| import { UpgradableComponent } from 'theme/components/upgradable'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'app-components', | |||
|   templateUrl: './components.component.html', | |||
|   styleUrls: ['./components.component.scss'], | |||
| }) | |||
| export class ComponentsComponent extends UpgradableComponent { | |||
| export class ComponentsComponent { | |||
|   @HostBinding('class.mdl-grid') private readonly mdlGrid = true; | |||
|   @HostBinding('class.ui-components') private readonly uiComponents = true; | |||
| 
 | |||
| @ -0,0 +1,104 @@ | |||
| <mat-card class="dashboard-card"> | |||
|   <mat-card-content class="dashboard-card-content"> | |||
| 
 | |||
|     <form [formGroup]="serverForm" (ngSubmit)="serverForm.valid && add(serverForm.value)" class="add-server-form"> | |||
| 
 | |||
|       <p>Essentials</p> | |||
|       <table class="add-server-full-width" cellspacing="0"><tr> | |||
|         <td> | |||
|           <mat-form-field class="add-server-full-width"> | |||
|           <mat-label>Interface</mat-label> | |||
|           <input formControlName="interface" matInput placeholder="wg0"> | |||
|           </mat-form-field> | |||
|         </td> | |||
|         <td> | |||
|           <mat-form-field class="add-server-full-width"> | |||
|           <mat-label>Address Scope</mat-label> | |||
|           <input formControlName="address" matInput placeholder="10.0.200.1/24"> | |||
|           </mat-form-field> | |||
|         </td> | |||
|       </tr></table> | |||
| 
 | |||
|       <table class="add-server-full-width" cellspacing="0"><tr> | |||
|         <td> | |||
|           <mat-form-field class="add-server-full-width"> | |||
|             <mat-label>Endpoint</mat-label> | |||
|             <input formControlName="endpoint" matInput placeholder="my-address.com"> | |||
|           </mat-form-field> | |||
|         </td> | |||
|         <td> | |||
|           <mat-form-field class="add-server-full-width"> | |||
|             <mat-label>Port</mat-label> | |||
|             <input formControlName="listen_port" matInput placeholder="51820"> | |||
|           </mat-form-field> | |||
|         </td> | |||
|       </tr></table> | |||
| 
 | |||
|       <p>Keys</p> | |||
|       <p> | |||
|         <mat-form-field class="add-server-full-width"> | |||
|           <mat-label>Private-Key</mat-label> | |||
|           <input formControlName="private_key" matInput> | |||
|         </mat-form-field> | |||
|       </p> | |||
| 
 | |||
|       <p> | |||
|         <mat-form-field class="add-server-full-width"> | |||
|           <mat-label>Public-Key</mat-label> | |||
|           <input formControlName="public_key" matInput> | |||
|         </mat-form-field> | |||
|       </p> | |||
| 
 | |||
|       <p> | |||
|         <mat-form-field class="add-server-full-width"> | |||
|           <mat-label>Shared-Key</mat-label> | |||
|           <input formControlName="shared_key" matInput> | |||
|         </mat-form-field> | |||
|       </p> | |||
| 
 | |||
|       <div class="add-server-button-group"> | |||
|         <button type="button" [hidden]="!isEdit" (click)="getKeyPair()" mat-raised-button color="primary" disabled> | |||
|           <i class="material-icons">vpn_key</i> | |||
|           Generate KeyPair | |||
|         </button> | |||
|         <button type="button" [hidden]="!isEdit" (click)="getPSK()" mat-raised-button color="primary"> | |||
|           <i class="material-icons">share</i> | |||
|           Generate PSK | |||
|         </button> | |||
|       </div> | |||
| 
 | |||
|       <p>Scripts</p> | |||
|       <p> | |||
|         <mat-form-field class="add-server-full-width"> | |||
|           <mat-label>Post-Up</mat-label> | |||
|           <input formControlName="post_up" matInput> | |||
|         </mat-form-field> | |||
|       </p> | |||
| 
 | |||
|       <p> | |||
|         <mat-form-field class="add-server-full-width"> | |||
|           <mat-label>Post-Down</mat-label> | |||
|           <input formControlName="post_down" matInput> | |||
|         </mat-form-field> | |||
|       </p> | |||
| 
 | |||
| 
 | |||
|       <div class="add-server-button-group"> | |||
|         <button mat-raised-button color="primary" [disabled]="!serverForm.valid" type="submit"> | |||
|           <ng-container *ngIf="!isEdit">Add Server</ng-container> | |||
|           <ng-container *ngIf="isEdit">Edit Server</ng-container> | |||
|         </button> | |||
| 
 | |||
|         <button mat-raised-button color="warn" (click)="isEdit = false; serverForm.reset()"> | |||
|           Reset | |||
|         </button> | |||
| 
 | |||
|       </div> | |||
|     </form> | |||
| 
 | |||
| 
 | |||
| 
 | |||
| 
 | |||
| 
 | |||
|   </mat-card-content> | |||
| </mat-card> | |||
| @ -0,0 +1,18 @@ | |||
| .add-server-form { | |||
|   min-width: 150px; | |||
|   //max-width: 500px; | |||
|   width: 100%; | |||
| } | |||
| 
 | |||
| .add-server-full-width { | |||
|   width: 100%; | |||
| } | |||
| 
 | |||
| td { | |||
|   padding-right: 8px; | |||
| } | |||
| 
 | |||
| .add-server-button-group{ | |||
|   margin-right: 8px; | |||
| } | |||
| 
 | |||
| @ -0,0 +1,85 @@ | |||
| import {Component, Input, OnInit, ViewEncapsulation} from '@angular/core'; | |||
| import {FormControl, FormGroup, Validators} from "@angular/forms"; | |||
| import {IPValidator} from "../../../validators/ip-address.validator"; | |||
| import {NumberValidator} from "../../../validators/number.validator"; | |||
| import {Server} from "../../../interfaces/server"; | |||
| import {ServerService} from "../../../services/server.service"; | |||
| import {DataService} from "../../../services/data.service"; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'app-add-server', | |||
|   templateUrl: './add-server.component.html', | |||
|   encapsulation: ViewEncapsulation.None, | |||
|   styleUrls: ['./add-server.component.scss', '../dashboard2.component.css'] | |||
| }) | |||
| export class AddServerComponent implements OnInit { | |||
| 
 | |||
|   @Input() servers: Array<Server>; | |||
| 
 | |||
|   serverForm = new FormGroup({ | |||
|     address: new FormControl('', [IPValidator.isIPAddress]), | |||
|     interface: new FormControl('', [Validators.required, Validators.minLength(3)]), | |||
|     listen_port: new FormControl('', [Validators.required, NumberValidator.stringIsNumber]), | |||
|     endpoint: new FormControl('', Validators.required), | |||
|     private_key: new FormControl('', [Validators.minLength(44), Validators.maxLength(44)]), | |||
|     public_key: new FormControl('', [Validators.minLength(44), Validators.maxLength(44)]), | |||
|     shared_key: new FormControl('', [Validators.minLength(44), Validators.maxLength(44)]), | |||
|     post_up: new FormControl(''), | |||
|     post_down: new FormControl(''), | |||
| 
 | |||
|     // Unused on backend
 | |||
|     is_running: new FormControl(false), | |||
|     peers: new FormControl([]), | |||
|   }); | |||
|   isEdit: boolean = false; | |||
|   editServer: Server = null; | |||
| 
 | |||
|   constructor(private serverAPI: ServerService, private comm: DataService) { } | |||
| 
 | |||
|   ngOnInit(): void { | |||
| 
 | |||
|     this.comm.on("server-edit").subscribe( (data: Server) => { | |||
|       this.isEdit = true; | |||
|       this.serverForm.setValue(data); | |||
|       this.editServer = data; | |||
|     }) | |||
| 
 | |||
|   } | |||
| 
 | |||
|   add(form: Server) { | |||
| 
 | |||
|     if(this.isEdit){ | |||
|       const idx = this.servers.indexOf(this.editServer); | |||
|       this.serverAPI.editServer(this.editServer, form).subscribe((server: Server) => { | |||
|         this.servers[idx] = server; | |||
|       }); | |||
| 
 | |||
|     } else { | |||
| 
 | |||
|       this.serverAPI.addServer(form).subscribe((server: Server) => { | |||
|         this.servers.push(server); | |||
|       }); | |||
|     } | |||
| 
 | |||
|     this.isEdit = false; | |||
|     this.serverForm.reset(); | |||
|     this.editServer = null; | |||
|   } | |||
| 
 | |||
|   getKeyPair() { | |||
|     this.serverAPI.getKeyPair().subscribe((kp: any) => { | |||
|       this.serverForm.patchValue({ | |||
|         private_key: kp.private_key, | |||
|         public_key: kp.public_key | |||
|       }) | |||
|     }); | |||
|   } | |||
| 
 | |||
|   getPSK() { | |||
|     this.serverAPI.getPSK().subscribe((psk: any) => { | |||
|       this.serverForm.patchValue({ | |||
|         shared_key: psk.psk | |||
|       }) | |||
|     }); | |||
|   } | |||
| } | |||
| @ -0,0 +1,18 @@ | |||
| .grid-container { | |||
|   margin: 20px; | |||
| } | |||
| 
 | |||
| .dashboard-card { | |||
|   position: absolute; | |||
|   top: 15px; | |||
|   left: 15px; | |||
|   right: 15px; | |||
|   bottom: 15px; | |||
| } | |||
| 
 | |||
| .more-button { | |||
|   position: absolute; | |||
|   top: 5px; | |||
|   right: 10px; | |||
| } | |||
| 
 | |||
| @ -0,0 +1,11 @@ | |||
| <div flex fxFill fxLayout="row" fxLayoutAlign="space-between" > | |||
|   <div fxFlex="65"> | |||
|       <app-server [(server)]="servers[idx]" [(servers)]="servers" *ngFor="let server of servers; let idx = index"></app-server> | |||
|   </div> | |||
| 
 | |||
|   <div fxFlex="34"> | |||
|     <app-add-server [(servers)]="servers"></app-add-server> | |||
|   </div> | |||
| 
 | |||
| 
 | |||
| </div> | |||
| @ -0,0 +1,40 @@ | |||
| import { LayoutModule } from '@angular/cdk/layout'; | |||
| import { NoopAnimationsModule } from '@angular/platform-browser/animations'; | |||
| import { async, ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { MatButtonModule } from '@angular/material/button'; | |||
| import { MatCardModule } from '@angular/material/card'; | |||
| import { MatGridListModule } from '@angular/material/grid-list'; | |||
| import { MatIconModule } from '@angular/material/icon'; | |||
| import { MatMenuModule } from '@angular/material/menu'; | |||
| 
 | |||
| import { Dashboard2Component } from './dashboard2.component'; | |||
| 
 | |||
| describe('Dashboard2Component', () => { | |||
|   let component: Dashboard2Component; | |||
|   let fixture: ComponentFixture<Dashboard2Component>; | |||
| 
 | |||
|   beforeEach(async(() => { | |||
|     TestBed.configureTestingModule({ | |||
|       declarations: [Dashboard2Component], | |||
|       imports: [ | |||
|         NoopAnimationsModule, | |||
|         LayoutModule, | |||
|         MatButtonModule, | |||
|         MatCardModule, | |||
|         MatGridListModule, | |||
|         MatIconModule, | |||
|         MatMenuModule, | |||
|       ] | |||
|     }).compileComponents(); | |||
|   })); | |||
| 
 | |||
|   beforeEach(() => { | |||
|     fixture = TestBed.createComponent(Dashboard2Component); | |||
|     component = fixture.componentInstance; | |||
|     fixture.detectChanges(); | |||
|   }); | |||
| 
 | |||
|   it('should compile', () => { | |||
|     expect(component).toBeTruthy(); | |||
|   }); | |||
| }); | |||
| @ -0,0 +1,44 @@ | |||
| import {Component, OnInit} from '@angular/core'; | |||
| import { map } from 'rxjs/operators'; | |||
| import { Breakpoints, BreakpointObserver } from '@angular/cdk/layout'; | |||
| import {Server} from "../../interfaces/server"; | |||
| import {ServerService} from "../../services/server.service"; | |||
| import {Peer} from "../../interfaces/peer"; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'dashboard2', | |||
|   templateUrl: './dashboard2.component.html', | |||
|   styleUrls: ['./dashboard2.component.css'] | |||
| }) | |||
| export class Dashboard2Component implements OnInit | |||
| { | |||
|   servers: Array<Server> = []; | |||
| 
 | |||
|   constructor(private breakpointObserver: BreakpointObserver, private serverAPI: ServerService) { | |||
| 
 | |||
|   } | |||
| 
 | |||
| 
 | |||
|   ngOnInit(): void { | |||
|     this.serverAPI.getServers() | |||
|       .subscribe( (servers: Array<Server>) => { | |||
|         this.servers.push(...servers); | |||
|         servers.forEach((server) => { | |||
| 
 | |||
|           this.serverAPI.serverStats(server).subscribe((stats: Peer[]) => { | |||
|             stats.forEach( item => { | |||
|               const peer = server.peers.find(x => x.public_key == item.public_key); | |||
|               peer._stats = item | |||
|             }); | |||
| 
 | |||
| 
 | |||
|           }); | |||
| 
 | |||
| 
 | |||
|         }); | |||
| 
 | |||
| 
 | |||
|       }) | |||
|   } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,49 @@ | |||
| import { NgModule } from '@angular/core'; | |||
| import { CommonModule } from '@angular/common'; | |||
| import {Dashboard2Component} from "./dashboard2.component"; | |||
| import {MatGridListModule} from "@angular/material/grid-list"; | |||
| import {MatCardModule} from "@angular/material/card"; | |||
| import {MatMenuModule} from "@angular/material/menu"; | |||
| import {MatIconModule} from "@angular/material/icon"; | |||
| import {MatButtonModule} from "@angular/material/button"; | |||
| import {ServerComponent} from "./server/server.component"; | |||
| import {MatExpansionModule} from "@angular/material/expansion"; | |||
| import {AddServerComponent} from "./add-server/add-server.component"; | |||
| import {MatFormFieldModule} from "@angular/material/form-field"; | |||
| import {MatInputModule} from "@angular/material/input"; | |||
| import {FormsModule, ReactiveFormsModule} from "@angular/forms"; | |||
| import {ComponentsModule} from "../components"; | |||
| import {FlexModule} from "@angular/flex-layout"; | |||
| import {MatTableModule} from "@angular/material/table"; | |||
| import {PeerComponent} from "./peer/peer.component"; | |||
| import {QRCodeModule} from "angularx-qrcode"; | |||
| 
 | |||
| 
 | |||
| 
 | |||
| @NgModule({ | |||
|   declarations: [ | |||
|     Dashboard2Component, | |||
|     ServerComponent, | |||
|     AddServerComponent, | |||
|     PeerComponent | |||
|   ], | |||
|   imports: [ | |||
|     CommonModule, | |||
|     MatGridListModule, | |||
|     MatCardModule, | |||
|     MatMenuModule, | |||
|     MatIconModule, | |||
|     MatButtonModule, | |||
|     MatExpansionModule, | |||
|     MatFormFieldModule, | |||
|     MatInputModule, | |||
|     ReactiveFormsModule, | |||
|     ComponentsModule, | |||
|     FlexModule, | |||
|     MatTableModule, | |||
|     FormsModule, | |||
|     QRCodeModule, | |||
| 
 | |||
|   ] | |||
| }) | |||
| export class Dashboard2Module { } | |||
| @ -0,0 +1,76 @@ | |||
| <div flex fxLayout="row" fxLayoutAlign="space-between"> | |||
|   <div fxFlex="50"> | |||
| 
 | |||
| 
 | |||
|     <form #peerForm="ngForm" class="peer-edit-form" (ngSubmit)="peerForm.valid && edit()" > | |||
| 
 | |||
|       <b>Essentials</b> | |||
|       <table class="full-width" cellspacing="0"><tr> | |||
|         <td> | |||
|           <mat-form-field class="full-width"> | |||
|             <mat-label>Name</mat-label> | |||
|             <input [disabled]="!peer._edit" name="name" matInput [(ngModel)]="peer.name"> | |||
|           </mat-form-field> | |||
|         </td> | |||
|         <td> | |||
|           <mat-form-field class="full-width"> | |||
|             <mat-label>Address</mat-label> | |||
|             <input [disabled]="!peer._edit" name="address" matInput [(ngModel)]="peer.address"> | |||
|           </mat-form-field> | |||
|         </td> | |||
|       </tr></table> | |||
| 
 | |||
|       <p> | |||
|         <mat-form-field class="full-width"> | |||
|           <mat-label>DNS</mat-label> | |||
|           <input [disabled]="!peer._edit" name="dns" [(ngModel)]="peer.dns" matInput> | |||
|         </mat-form-field> | |||
|       </p> | |||
| 
 | |||
| 
 | |||
|       <p> | |||
|         <mat-form-field class="full-width"> | |||
|           <mat-label>Allowed IPs</mat-label> | |||
|           <input [disabled]="!peer._edit" name="allowed_ips" [(ngModel)]="peer.allowed_ips" matInput> | |||
|         </mat-form-field> | |||
|       </p> | |||
| 
 | |||
| 
 | |||
|       <p>Keys</p> | |||
|       <p> | |||
|         <mat-form-field class="full-width"> | |||
|           <mat-label>Private-Key</mat-label> | |||
|           <input [disabled]="!peer._edit" name="private_key" [(ngModel)]="peer.private_key" matInput> | |||
|         </mat-form-field> | |||
|       </p> | |||
| 
 | |||
|       <p> | |||
|         <mat-form-field class="full-width"> | |||
|           <mat-label>Public-Key</mat-label> | |||
|           <input [disabled]="!peer._edit" name="public_key" [(ngModel)]="peer.public_key" matInput> | |||
|         </mat-form-field> | |||
|       </p> | |||
| 
 | |||
|       <button | |||
|         [hidden]="!peer._edit" | |||
|         [disabled]="!peerForm.valid" | |||
|         type="submit" | |||
|         mat-raised-button color="primary"> | |||
|         Submit changes | |||
|       </button> | |||
| 
 | |||
|     </form> | |||
| 
 | |||
| 
 | |||
| 
 | |||
|   </div> | |||
| 
 | |||
|   <div fxFlex="33"> | |||
|     <textarea readonly class="mdl-textfield--full-width" style="min-height: 250px; height: 100%; background-color: #202020; color: #00bcd4;">{{config}}</textarea> | |||
| 
 | |||
|   </div> | |||
|   <div fxFlex="33"> | |||
|     <qrcode [qrdata]="config" [width]="256" [errorCorrectionLevel]="'M'"></qrcode> | |||
|   </div> | |||
| 
 | |||
| </div> | |||
| @ -0,0 +1,17 @@ | |||
| .peer-edit-form { | |||
|   min-width: 150px; | |||
|   //max-width: 500px; | |||
|   width: 100%; | |||
|   text-align: left; | |||
| } | |||
| 
 | |||
| .full-width { | |||
|   width: 100%; | |||
| } | |||
| 
 | |||
| 
 | |||
| td { | |||
|   padding-left: 0 !important; | |||
|   padding-right: 8px; | |||
|   text-align: left !important; | |||
| } | |||
| @ -0,0 +1,84 @@ | |||
| import {Component, EventEmitter, Input, OnInit, ViewEncapsulation} from '@angular/core'; | |||
| import {ServerService} from "../../../services/server.service"; | |||
| import {Peer} from "../../../interfaces/peer"; | |||
| import {Server} from "../../../interfaces/server"; | |||
| import {FormControl, FormGroup} from "@angular/forms"; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'app-peer', | |||
|   templateUrl: './peer.component.html', | |||
|   encapsulation: ViewEncapsulation.None, | |||
|   styleUrls: ['./peer.component.scss'], | |||
| }) | |||
| export class PeerComponent implements OnInit { | |||
| 
 | |||
|   @Input("peer") peer: Peer; | |||
|   @Input("server") server: Server; | |||
|   @Input("selectedPeer") selectedPeer: Peer; | |||
|   @Input("onEvent") editPeerEmitter: EventEmitter<any> = new EventEmitter<any>(); | |||
| 
 | |||
| 
 | |||
| 
 | |||
|   config: string = "Loading..."; | |||
| 
 | |||
| 
 | |||
|   constructor(public serverAPI: ServerService) { } | |||
| 
 | |||
|   ngOnInit(): void { | |||
| 
 | |||
|     this.editPeerEmitter.subscribe( (msg) => { | |||
|       if(msg.peer !== this.peer){ | |||
|         return; | |||
|       } | |||
|       if(msg.type === "edit"){ | |||
|         this.edit(); | |||
| 
 | |||
|       }else if(msg.type == "delete"){ | |||
|         this.delete(); | |||
|       }else if(msg.type == "open"){ | |||
|         this.fetchConfig(); | |||
|       } | |||
|     }) | |||
| 
 | |||
|   } | |||
| 
 | |||
|   edit(){ | |||
|     if(this.peer._edit) { | |||
| 
 | |||
|       // Submit the edit (True -> False)
 | |||
|       const idx = this.server.peers.indexOf(this.peer); | |||
|       this.serverAPI.editPeer(this.peer).subscribe((newPeer) => { | |||
|         Object.keys(newPeer).forEach(k => { | |||
|           this.server.peers[idx][k] = newPeer[k]; | |||
|         }); | |||
|       }); | |||
| 
 | |||
|     } else if(!this.peer._edit) { | |||
|       this.peer._expand = true; | |||
| 
 | |||
|       // Open for edit. aka do nothing (False -> True
 | |||
| 
 | |||
|     } | |||
| 
 | |||
|     this.peer._edit = !this.peer._edit; | |||
| 
 | |||
| 
 | |||
|   } | |||
| 
 | |||
|   delete(){ | |||
|     const idx = this.server.peers.indexOf(this.peer); | |||
|     this.serverAPI.deletePeer(this.peer).subscribe((apiServer) => { | |||
|       this.server.peers.splice(idx, 1); | |||
|     }) | |||
|   } | |||
| 
 | |||
|   fetchConfig() { | |||
|     this.serverAPI.peerConfig(this.peer).subscribe((config: any) => { | |||
|       this.config = config.config | |||
|     }) | |||
|   } | |||
| 
 | |||
|   pInt(string: string) { | |||
|     return parseInt(string) | |||
|   } | |||
| } | |||
| @ -0,0 +1,248 @@ | |||
| 
 | |||
| <mat-card class="dashboard-card"> | |||
|   <mat-card-header class="server-card-header"> | |||
|     <mat-card-title> | |||
|       <span>{{server.interface}}</span> | |||
| 
 | |||
|       <!-- This fills the remaining space of the current row --> | |||
|       <span class="fill-remaining-space"></span> | |||
| 
 | |||
|       <i class="material-icons" [ngClass]="{'text-success': server.is_running, 'text-danger': !server.is_running}">check_circle</i> | |||
| 
 | |||
|       <app-modal-confirm | |||
|         [qrCode]="true" | |||
|         [noConfirm]="false" | |||
|         area="true" | |||
|         icon="settings" | |||
|         title="Configuration" | |||
|         [text]="serverConfig" | |||
|         hover="Show config for {{server.interface}}"> | |||
|       </app-modal-confirm> | |||
| 
 | |||
|       <span> | |||
| 
 | |||
|         <app-modal-confirm | |||
|           [noConfirm]="true" | |||
|           (onConfirm)="addPeer()" | |||
|           icon="person_add" | |||
|           hover="Add peer to {{server.interface}}"> | |||
|         </app-modal-confirm> | |||
| 
 | |||
|         <app-modal-confirm | |||
|           *ngIf="!server.is_running" | |||
|           [noConfirm]="true" | |||
|           (onConfirm)="start()" | |||
|           icon="play_arrow" | |||
|           hover="Start {{server.interface}}"> | |||
|         </app-modal-confirm> | |||
| 
 | |||
|         <app-modal-confirm | |||
|           *ngIf="server.is_running" | |||
|           [noConfirm]="false" | |||
|           (onConfirm)="stop()" | |||
|           title="Stop server {{server.interface}}?" | |||
|           text="Are you sure you want to stop this server? This may cause you or your clients to lose connection to the server." | |||
|           icon="stop" | |||
|           hover="Stop {{server.interface}}"> | |||
|         </app-modal-confirm> | |||
| 
 | |||
|         <app-modal-confirm | |||
|           [noConfirm]="false" | |||
|           (onConfirm)="restart()" | |||
|           title="Restart server {{server.interface}}?" | |||
|           text="Are you sure you want to restart this server? This may cause you or your clients to lose connection to the server." | |||
|           icon="autorenew" | |||
|           hover="Restart {{server.interface}}"> | |||
|         </app-modal-confirm> | |||
| 
 | |||
|         <app-modal-confirm | |||
|           [noConfirm]="true" | |||
|           (onConfirm)="edit()" | |||
|           icon="edit" | |||
|           hover="Edit {{server.interface}}"> | |||
|         </app-modal-confirm> | |||
| 
 | |||
|         <app-modal-confirm | |||
|           (onConfirm)="delete()" | |||
|           title="Delete {{server.interface}}" | |||
|           text="Are you sure you want to delete {{server.interface}}" | |||
|           icon="delete" | |||
|           hover="Delete {{server.interface}}"> | |||
|         </app-modal-confirm> | |||
|       </span> | |||
| 
 | |||
| 
 | |||
| 
 | |||
|     </mat-card-title> | |||
|     <mat-card-subtitle>{{server.address}} @ {{server.endpoint}}</mat-card-subtitle> | |||
|   </mat-card-header> | |||
| 
 | |||
|   <mat-card-content class="dashboard-card-content"> | |||
| 
 | |||
| 
 | |||
|     <table class="table"> | |||
|       <thead> | |||
|         <tr> | |||
|           <th>Name</th> | |||
|           <th>Address</th> | |||
|           <th>Public-Key</th> | |||
|           <th>Total tx/rx</th> | |||
|           <th>Handshake</th> | |||
|           <th>Manage</th> | |||
|         </tr> | |||
|       </thead> | |||
| 
 | |||
| 
 | |||
|       <tbody> | |||
|         <ng-container *ngFor="let peer of server.peers; let idx = index;" (click)="selectedPeer = (selectedPeer != peer)? peer : null"> | |||
| 
 | |||
|             <tr (click)="openPeer(peer)"> | |||
|               <td>{{peer.name}}</td> | |||
|               <td>{{peer.address}}</td> | |||
|               <td>{{peer.public_key}}</td> | |||
|               <td>{{peer._stats?.tx || '0'}}/{{peer._stats?.rx || '0'}}</td> | |||
|               <td>{{peer._stats?.handshake || 'N/A'}}</td> | |||
|               <td> | |||
| 
 | |||
|                 <!-- Edit buttons --> | |||
|                 <app-modal-confirm | |||
|                   [noConfirm]="true" | |||
|                   (onConfirm)="this.editPeerEmitter.emit({type: 'edit', peer: peer})" | |||
|                   icon="edit" | |||
|                   hover="Edit {{peer.name}}"> | |||
|                 </app-modal-confirm> | |||
| 
 | |||
|                 <app-modal-confirm | |||
|                   [noConfirm]="false" | |||
|                   (onConfirm)="this.editPeerEmitter.emit({type: 'delete', peer: peer});" | |||
|                   text="Are you sure you want to delete {{peer.name}} ({{peer.public_key}})?" | |||
|                   title="Delete {{peer.name}}" | |||
|                   icon="delete" | |||
|                   hover="Delete {{peer.name}} ({{peer.public_key}})"> | |||
|                 </app-modal-confirm> | |||
| 
 | |||
| 
 | |||
| 
 | |||
|               </td> | |||
|             </tr> | |||
|             <tr [hidden]="peer !== selectedPeer"> | |||
|               <td colspan="6"> | |||
|                   <app-peer [onEvent]="this.editPeerEmitter" [(peer)]="server.peers[idx]" [(server)]="server"></app-peer> | |||
|               </td> | |||
|             </tr> | |||
| 
 | |||
| 
 | |||
|         </ng-container> | |||
|       </tbody> | |||
|     </table> | |||
| 
 | |||
| 
 | |||
| 
 | |||
| 
 | |||
|   </mat-card-content> | |||
|   <mat-card-actions> | |||
|   </mat-card-actions> | |||
| </mat-card> | |||
| 
 | |||
| 
 | |||
| 
 | |||
| <!-- | |||
| <mat-card class="dashboard-card"> | |||
|   <mat-card-content class="dashboard-card-content"> | |||
|     *Server* | |||
|     <ng-container > | |||
| 
 | |||
| 
 | |||
|     </ng-container> | |||
|   </mat-card-content> | |||
| </mat-card> | |||
| 
 | |||
| 
 | |||
| --> | |||
| 
 | |||
| <!-- | |||
| <div class=" mdl-card mdl-shadow--2dp"> | |||
|   <div class="mdl-card__title mdl-card--border"> | |||
|     <h2 class="mdl-card__title-text">{{server.interface}}</h2> | |||
|     <span style="width:20px;"></span> | |||
| 
 | |||
| 
 | |||
| 
 | |||
|   </div> | |||
| 
 | |||
|   <div class="mdl-card__actions"> | |||
| 
 | |||
|     <div class="mdl-grid peer-item-header"> | |||
|       <div class="mdl-cell--2-col mdl-cell--12-col-phone">Name</div> | |||
|       <div class="mdl-cell--2-col mdl-cell--12-col-phone">Address</div> | |||
|       <div class="mdl-cell--3-col mdl-cell--12-col-phone">Public-Key</div> | |||
|       <div class="mdl-cell--2-col mdl-cell--12-col-phone">Total tx/rx</div> | |||
|       <div class="mdl-cell--2-col mdl-cell--12-col-phone">Handshake</div> | |||
|       <div class="mdl-cell--2-col mdl-cell--12-col-phone">Manage</div> | |||
|     </div> | |||
| 
 | |||
| 
 | |||
|     <div style="cursor: pointer;" *ngFor="let peer of server.peers; let idx = index;" > | |||
|       <app-peer [(peer)]="server.peers[idx]" [(server)]="server"></app-peer> | |||
|     </div> | |||
| 
 | |||
|   </div> | |||
| 
 | |||
|   <div class="mdl-card__supporting-text"> | |||
|   </div> | |||
| 
 | |||
| 
 | |||
| 
 | |||
| 
 | |||
|   <div class="mdl-card__menu"> | |||
| 
 | |||
|     <app-modal-confirm | |||
|       [noConfirm]="true" | |||
|       (onConfirm)="addPeer()" | |||
|       icon="person_add" | |||
|       hover="Add peer to {{server.interface}}"> | |||
|     </app-modal-confirm> | |||
| 
 | |||
|     <app-modal-confirm | |||
|       *ngIf="!server.is_running" | |||
|       [noConfirm]="true" | |||
|       (onConfirm)="start()" | |||
|       icon="play_arrow" | |||
|       hover="Start {{server.interface}}"> | |||
|     </app-modal-confirm> | |||
| 
 | |||
|     <app-modal-confirm | |||
|       *ngIf="server.is_running" | |||
|       [noConfirm]="false" | |||
|       (onConfirm)="stop()" | |||
|       title="Stop server {{server.interface}}?" | |||
|       text="Are you sure you want to stop this server? This may cause you or your clients to lose connection to the server." | |||
|       icon="stop" | |||
|       hover="Stop {{server.interface}}"> | |||
|     </app-modal-confirm> | |||
| 
 | |||
|     <app-modal-confirm | |||
|       [noConfirm]="false" | |||
|       (onConfirm)="restart()" | |||
|       title="Restart server {{server.interface}}?" | |||
|       text="Are you sure you want to restart this server? This may cause you or your clients to lose connection to the server." | |||
|       icon="autorenew" | |||
|       hover="Restart {{server.interface}}"> | |||
|     </app-modal-confirm> | |||
| 
 | |||
|     <app-modal-confirm | |||
|       [noConfirm]="true" | |||
|       (onConfirm)="edit()" | |||
|       icon="edit" | |||
|       hover="Edit {{server.interface}}"> | |||
|     </app-modal-confirm> | |||
| 
 | |||
|     <app-modal-confirm | |||
|       (onConfirm)="delete()" | |||
|       title="Delete {{server.interface}}" | |||
|       text="Are you sure you want to delete {{server.interface}}" | |||
|       icon="delete" | |||
|       hover="Delete {{server.interface}}"> | |||
|     </app-modal-confirm> | |||
|   </div> | |||
| </div>--> | |||
| @ -0,0 +1,28 @@ | |||
| 
 | |||
| table { | |||
|   width: 100%; | |||
| } | |||
| 
 | |||
| tr.example-detail-row { | |||
|   height: 0 !important; | |||
| } | |||
| 
 | |||
| tr.example-element-row:not(.example-expanded-row):hover { | |||
|   background: whitesmoke; | |||
| } | |||
| 
 | |||
| tr.example-element-row:not(.example-expanded-row):active { | |||
|   background: #efefef; | |||
| } | |||
| 
 | |||
| .example-element-row td { | |||
|   border-bottom-width: 0; | |||
| } | |||
| 
 | |||
| .example-element-detail { | |||
|   overflow: hidden; | |||
|   display: flex; | |||
| } | |||
| 
 | |||
| 
 | |||
| 
 | |||
| @ -0,0 +1,77 @@ | |||
| import {Component, EventEmitter, Input, OnInit, ViewEncapsulation} from '@angular/core'; | |||
| import {Server} from "../../../interfaces/server"; | |||
| import {ServerService} from "../../../services/server.service"; | |||
| import {DataService} from "../../../services/data.service"; | |||
| import {Peer} from "../../../interfaces/peer"; | |||
| 
 | |||
| 
 | |||
| @Component({ | |||
|   selector: 'app-server', | |||
|   templateUrl: './server.component.html', | |||
|   encapsulation: ViewEncapsulation.None, | |||
|   styleUrls: ['./server.component.scss', '../dashboard2.component.css'], | |||
| }) | |||
| export class ServerComponent implements OnInit { | |||
|   @Input() server: Server; | |||
|   @Input() servers: Array<Server>; | |||
|   public editPeerEmitter: EventEmitter<any> = new EventEmitter<any>(); | |||
| 
 | |||
|   serverConfig: string; | |||
| 
 | |||
|   selectedPeer: Peer | null; | |||
| 
 | |||
|   constructor(private serverAPI: ServerService, private comm: DataService) { } | |||
| 
 | |||
|   ngOnInit(): void { | |||
|     console.log("Server"); | |||
| 
 | |||
|     this.serverAPI.serverConfig(this.server).subscribe((x: any) => this.serverConfig = x.config) | |||
| 
 | |||
|   } | |||
| 
 | |||
|   edit(){ | |||
| 
 | |||
|     this.comm.emit('server-edit', this.server); | |||
|   } | |||
| 
 | |||
|   stop() { | |||
|     this.serverAPI.stopServer(this.server).subscribe((apiServer) => { | |||
|       this.server.is_running = apiServer.is_running | |||
|     }) | |||
|   } | |||
| 
 | |||
|   start() { | |||
|     this.serverAPI.startServer(this.server).subscribe((apiServer) => { | |||
|       this.server.is_running = apiServer.is_running | |||
|     }) | |||
|   } | |||
| 
 | |||
|   addPeer() { | |||
|     this.serverAPI.addPeer(this.server).subscribe((peer) => { | |||
|       this.server.peers.push(peer) | |||
|     }) | |||
|   } | |||
| 
 | |||
|   restart() { | |||
|     this.serverAPI.restartServer(this.server).subscribe((apiServer) => { | |||
|       this.server.is_running = apiServer.is_running | |||
|     }) | |||
|   } | |||
| 
 | |||
| 
 | |||
|   delete() { | |||
|     const index = this.servers.indexOf(this.server); | |||
|     this.serverAPI.deleteServer(this.server).subscribe((apiServer) => { | |||
|       this.servers.splice(index, 1); | |||
|     }) | |||
|   } | |||
| 
 | |||
|   openPeer(peer: Peer) { | |||
|     if(this.selectedPeer == peer){ | |||
|       this.selectedPeer = null; | |||
|       return | |||
|     } | |||
|     this.selectedPeer = peer; | |||
|     this.editPeerEmitter.emit({type: 'open', peer: peer}); | |||
|   } | |||
| } | |||
| @ -0,0 +1 @@ | |||
| *{-webkit-box-sizing:border-box;box-sizing:border-box}body{padding:0;margin:0}#notfound{position:relative;height:100vh}#notfound .notfound{position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.notfound{max-width:520px;width:100%;line-height:1.4;text-align:center}.notfound .notfound-404{position:relative;height:240px}.notfound .notfound-404 h1{font-family:montserrat,sans-serif;position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%);font-size:252px;font-weight:900;margin:0;color:#262626;text-transform:uppercase;letter-spacing:-40px;margin-left:-20px}.notfound .notfound-404 h1>span{text-shadow:-8px 0 0 #fff}.notfound .notfound-404 h3{font-family:cabin,sans-serif;position:relative;font-size:16px;font-weight:700;text-transform:uppercase;color:#262626;margin:0;letter-spacing:3px;padding-left:6px}.notfound h2{font-family:cabin,sans-serif;font-size:20px;font-weight:400;text-transform:uppercase;color:#000;margin-top:0;margin-bottom:25px}@media only screen and (max-width:767px){.notfound .notfound-404{height:200px}.notfound .notfound-404 h1{font-size:200px}}@media only screen and (max-width:480px){.notfound .notfound-404{height:162px}.notfound .notfound-404 h1{font-size:162px;height:150px;line-height:162px}.notfound h2{font-size:16px}} | |||
| @ -0,0 +1,9 @@ | |||
| <div id="notfound"> | |||
|   <div class="notfound"> | |||
|     <div class="notfound-404"> | |||
|       <h3>Oops! Page not found</h3> | |||
|       <h1><span>4</span><span>0</span><span>4</span></h1> | |||
|     </div> | |||
|     <h2>we are sorry, but the page you requested was not found</h2> | |||
|   </div> | |||
| </div> | |||
| @ -0,0 +1,8 @@ | |||
| import { Component, HostBinding } from '@angular/core'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'app-error', | |||
|   styleUrls: ['error.component.css'], | |||
|   templateUrl: './error.component.html', | |||
| }) | |||
| export class ErrorComponent { } | |||
| @ -0,0 +1,32 @@ | |||
| import { NgModule } from '@angular/core'; | |||
| import { Routes, RouterModule } from '@angular/router'; | |||
| import {Dashboard2Component} from "./dashboard2/dashboard2.component"; | |||
| import {LayoutComponent} from "../layout/layout/layout.component"; | |||
| import {ErrorComponent} from "./error"; | |||
| import {LoginComponent} from "./user/login/login.component"; | |||
| import {AuthGuard} from "@services/*"; | |||
| 
 | |||
| 
 | |||
| 
 | |||
| 
 | |||
| const routes: Routes = [ | |||
|   { path: '', component: LayoutComponent, children: | |||
|       [ | |||
|         //{ path: 'dashboard', component: DashboardComponent, pathMatch: 'full', canActivate: [AuthGuard]},
 | |||
|         { path: 'dashboard', component: Dashboard2Component, pathMatch: 'full', canActivate: [AuthGuard]}, | |||
|         { path: '404', component: ErrorComponent, pathMatch: 'full' }, | |||
|       ] | |||
|   }, | |||
|   { path: 'user', component: LayoutComponent, children: | |||
|       [ | |||
|         //{ path: 'dashboard', component: DashboardComponent, pathMatch: 'full', canActivate: [AuthGuard]},
 | |||
|         { path: 'login', component: LoginComponent, pathMatch: 'full'}, | |||
|       ] | |||
|   }, | |||
| ]; | |||
| 
 | |||
| @NgModule({ | |||
|   imports: [RouterModule.forChild(routes)], | |||
|   exports: [RouterModule] | |||
| }) | |||
| export class PageRoutingModule { } | |||
| @ -0,0 +1,24 @@ | |||
| import { NgModule } from '@angular/core'; | |||
| import { CommonModule } from '@angular/common'; | |||
| import {PageRoutingModule} from "./page-routing.module"; | |||
| import {Dashboard2Module} from "./dashboard2/dashboard2.module"; | |||
| import {LoginComponent} from "./user/login/login.component"; | |||
| import {MatCardModule} from "@angular/material/card"; | |||
| import {FormsModule, ReactiveFormsModule} from "@angular/forms"; | |||
| import {MatInputModule} from "@angular/material/input"; | |||
| 
 | |||
| 
 | |||
| @NgModule({ | |||
|   declarations: [LoginComponent], | |||
|   imports: [ | |||
|     CommonModule, | |||
|     PageRoutingModule, | |||
|     FormsModule, | |||
|     Dashboard2Module, | |||
|     MatCardModule, | |||
|     ReactiveFormsModule, | |||
|     MatInputModule, | |||
|   ], | |||
| 
 | |||
| }) | |||
| export class PageModule { } | |||
| @ -0,0 +1,48 @@ | |||
| <div class="container"> | |||
| 
 | |||
| <base-card> | |||
|   <base-card-title> | |||
|     <h2 class="mdl-card__title-text">Edit User</h2> | |||
|   </base-card-title> | |||
|   <base-card-body> | |||
| 
 | |||
|     <form [formGroup]="editForm" (ngSubmit)="editForm.valid && edit()" class="form"> | |||
| 
 | |||
|       <div class="mdl-grid"> | |||
|         <div class="mdl-cell mdl-cell--6-col mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> | |||
|           <input formControlName="full_name" class="mdl-textfield__input" type="text" id="full_name"  value=""/> | |||
|           <label class="mdl-textfield__label" for="full_name">Full Name</label> | |||
|         </div> | |||
| 
 | |||
| 
 | |||
|         <div class="mdl-cell mdl-cell--6-col mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> | |||
|           <input formControlName="username" class="mdl-textfield__input" type="text" id="username" /> | |||
|           <label class="mdl-textfield__label" for="username">Username</label> | |||
|         </div> | |||
| 
 | |||
|         <div class="mdl-cell mdl-cell--6-col mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> | |||
|           <input formControlName="password" class="mdl-textfield__input" type="text" id="password"/> | |||
|           <label class="mdl-textfield__label" for="password">Password</label> | |||
|         </div> | |||
| 
 | |||
|         <div class="mdl-cell mdl-cell--6-col mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> | |||
|           <input formControlName="email" class="mdl-textfield__input" type="text" id="email"/> | |||
|           <label class="mdl-textfield__label" for="email">Email</label> | |||
|         </div> | |||
| 
 | |||
|       </div> | |||
| 
 | |||
| 
 | |||
|       <button [disabled]="!editForm.valid" type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect button--colored-light-blue"> | |||
|         Edit User | |||
|       </button> | |||
| 
 | |||
| 
 | |||
| 
 | |||
|     </form> | |||
| 
 | |||
| 
 | |||
|   </base-card-body> | |||
| </base-card> | |||
| 
 | |||
| </div> | |||
| @ -0,0 +1,51 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import {FormControl, FormGroup, Validators} from "@angular/forms"; | |||
| import {AuthService} from "@services/*"; | |||
| import {Router} from "@angular/router"; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'app-edit', | |||
|   templateUrl: './edit.component.html', | |||
|   styleUrls: ['./edit.component.scss'] | |||
| }) | |||
| export class EditComponent implements OnInit { | |||
| 
 | |||
|   public editForm: FormGroup = new FormGroup({ | |||
|     full_name: new FormControl(''), | |||
|     password: new FormControl('', Validators.required), | |||
|     email: new FormControl('', [ | |||
|       Validators.required, | |||
|       Validators.pattern('^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$'), | |||
|       Validators.maxLength(20), | |||
|     ]), | |||
|     username: new FormControl('', [Validators.required, Validators.maxLength(20)]), | |||
|   }); | |||
|   public user: any; | |||
|   public error: string; | |||
| 
 | |||
|   constructor(private authService: AuthService, | |||
|               private router: Router) { | |||
| 
 | |||
|   } | |||
| 
 | |||
|   public ngOnInit() { | |||
|     this.user = this.authService.user; | |||
| 
 | |||
| 
 | |||
|     this.editForm.setValue({ | |||
|       full_name: this.user.full_name, | |||
|       password: "", | |||
|       email: this.user.email, | |||
|       username: this.user.username | |||
|     }) | |||
|   } | |||
| 
 | |||
|   public edit() { | |||
|     if (this.editForm.valid) { | |||
|       this.authService.edit(this.editForm.getRawValue()) | |||
|         .subscribe(res => this.router.navigate(['/app/dashboard']), | |||
|           error => this.error = error.message); | |||
|     } | |||
|   } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,40 @@ | |||
| 
 | |||
| 
 | |||
| <mat-card> | |||
|   <mat-card-title> | |||
|     Authenticate to Wireguard Management | |||
|   </mat-card-title> | |||
| 
 | |||
|   <mat-card-content> | |||
|     <form [formGroup]="loginForm" (ngSubmit)="loginForm.valid && login()" class="form"> | |||
| 
 | |||
|       <p> | |||
|         <mat-form-field class="full-width"> | |||
|           <mat-label>Username</mat-label> | |||
|           <input type="text" id="username" formControlName="username" matInput> | |||
|         </mat-form-field> | |||
|       </p> | |||
| 
 | |||
|       <p> | |||
|         <mat-form-field class="full-width"> | |||
|           <mat-label>Password</mat-label> | |||
|           <input type="text" id="password" formControlName="password"  matInput> | |||
|         </mat-form-field> | |||
|       </p> | |||
| 
 | |||
| 
 | |||
|       <button [disabled]="!loginForm.valid" type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect button--colored-light-blue"> | |||
|         SIGN IN | |||
|       </button> | |||
| 
 | |||
| 
 | |||
| 
 | |||
| 
 | |||
| 
 | |||
|     </form> | |||
|   </mat-card-content> | |||
| 
 | |||
| 
 | |||
| </mat-card> | |||
| 
 | |||
| 
 | |||
| @ -0,0 +1,25 @@ | |||
| import { async, ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| 
 | |||
| import { LoginComponent } from './login.component'; | |||
| 
 | |||
| describe('LoginComponent', () => { | |||
|   let component: LoginComponent; | |||
|   let fixture: ComponentFixture<LoginComponent>; | |||
| 
 | |||
|   beforeEach(async(() => { | |||
|     TestBed.configureTestingModule({ | |||
|       declarations: [ LoginComponent ] | |||
|     }) | |||
|     .compileComponents(); | |||
|   })); | |||
| 
 | |||
|   beforeEach(() => { | |||
|     fixture = TestBed.createComponent(LoginComponent); | |||
|     component = fixture.componentInstance; | |||
|     fixture.detectChanges(); | |||
|   }); | |||
| 
 | |||
|   it('should create', () => { | |||
|     expect(component).toBeTruthy(); | |||
|   }); | |||
| }); | |||
| @ -0,0 +1,53 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms"; | |||
| import {AuthService} from "@services/*"; | |||
| import {Router} from "@angular/router"; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'app-login', | |||
|   templateUrl: './login.component.html', | |||
|   styleUrls: ['./login.component.scss'] | |||
| }) | |||
| export class LoginComponent implements OnInit { | |||
| 
 | |||
|   public loginForm: FormGroup; | |||
|   public username; | |||
|   public password; | |||
|   public error: string; | |||
| 
 | |||
|   constructor(private authService: AuthService, | |||
|               private fb: FormBuilder, | |||
|               private router: Router) { | |||
| 
 | |||
| 
 | |||
|     this.loginForm = this.fb.group({ | |||
|       password: new FormControl('', Validators.required), | |||
|       username: new FormControl('', [ | |||
|         Validators.required, | |||
|       ]), | |||
|     }); | |||
|     this.username = this.loginForm.get('username'); | |||
|     this.password = this.loginForm.get('password'); | |||
|   } | |||
| 
 | |||
|   public ngOnInit() { | |||
|     this.authService.logout(); | |||
|     this.loginForm.valueChanges.subscribe(() => { | |||
|       this.error = null; | |||
|     }); | |||
|   } | |||
| 
 | |||
|   public login() { | |||
|     this.error = null; | |||
|     if (this.loginForm.valid) { | |||
|       this.authService.login(this.loginForm.getRawValue()) | |||
|         .subscribe(res => this.router.navigate(['/page/dashboard']), | |||
|           error => this.error = error.message); | |||
|     } | |||
|   } | |||
| 
 | |||
|   public onInputChange(event) { | |||
|     event.target.required = true; | |||
|   } | |||
| 
 | |||
| } | |||
| @ -1,2 +0,0 @@ | |||
| 
 | |||
| 
 | |||
| @ -1,20 +0,0 @@ | |||
| <div class="mdl-card mdl-card__blank-layout-card mdl-shadow--2dp"> | |||
|   <div class="mdl-card__supporting-text color--dark-gray"> | |||
|     <div class="mdl-grid"> | |||
|       <div class="mdl-cell mdl-cell--12-col mdl-cell--4-col-phone"> | |||
|         <span class="mdl-card__title-text text-color--smooth-gray">DARKBOARD</span> | |||
|       </div> | |||
|       <div class="mdl-cell mdl-cell--12-col mdl-cell--4-col-phone"> | |||
|         <span class="text--huge color-text--light-blue">404</span> | |||
|       </div> | |||
|       <div class="mdl-cell mdl-cell--12-col mdl-cell--4-col-phone"> | |||
|         <span class="text--sorry text-color--white">Sorry, but there's nothing here</span> | |||
|       </div> | |||
|       <div class="mdl-cell mdl-cell--12-col mdl-cell--4-col-phone"> | |||
|         <a routerLink="/" class="mdl-button mdl-js-button color-text--light-blue pull-right"> | |||
|           Go Back | |||
|         </a> | |||
|       </div> | |||
|     </div> | |||
|   </div> | |||
| </div> | |||
| @ -1,10 +0,0 @@ | |||
| import { Component, HostBinding } from '@angular/core'; | |||
| 
 | |||
| import { BlankLayoutCardComponent } from 'app/components/blank-layout-card'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'app-error', | |||
|   styleUrls: ['../../../components/blank-layout-card/blank-layout-card.component.scss'], | |||
|   templateUrl: './error.component.html', | |||
| }) | |||
| export class ErrorComponent extends BlankLayoutCardComponent { } | |||
| @ -1,27 +0,0 @@ | |||
| <div class="mdl-card mdl-card__blank-layout-card mdl-shadow--2dp"> | |||
|   <div class="mdl-card__supporting-text color--dark-gray"> | |||
|     <div class="mdl-grid"> | |||
|       <div class="mdl-cell mdl-cell--12-col mdl-cell--4-col-phone"> | |||
|         <span class="mdl-card__title-text text-color--smooth-gray">DARKBOARD</span> | |||
|       </div> | |||
|       <div class="mdl-cell mdl-cell--12-col mdl-cell--4-col-phone"> | |||
|         <span class="blank-layout-card-name text-color--white">Forgot password?</span> | |||
|         <span class="blank-layout-card-secondary-text text-color--smoke">Enter your email below to recieve your password</span> | |||
|       </div> | |||
|       <div class="mdl-cell mdl-cell--12-col mdl-cell--4-col-phone"> | |||
|         <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label full-size"> | |||
|           <input class="mdl-textfield__input" type="text" id="e-mail"> | |||
|           <label class="mdl-textfield__label" for="e-mail">Email</label> | |||
|         </div> | |||
|       </div> | |||
|       <div class="mdl-cell mdl-cell--12-col mdl-cell--4-col-phone submit-cell"> | |||
|         <div class="mdl-layout-spacer"></div> | |||
|         <a routerLink="/app/dashboard"> | |||
|           <button class="mdl-button mdl-js-button mdl-button--raised color--light-blue"> | |||
|             SEND PASSWORD | |||
|           </button> | |||
|         </a> | |||
|       </div> | |||
|     </div> | |||
|   </div> | |||
| </div> | |||
| @ -1,10 +0,0 @@ | |||
| import { Component, HostBinding } from '@angular/core'; | |||
| 
 | |||
| import { BlankLayoutCardComponent } from 'app/components/blank-layout-card'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'app-forgot-password', | |||
|   styleUrls: ['../../../components/blank-layout-card/blank-layout-card.component.scss'], | |||
|   templateUrl: './forgot-password.component.html', | |||
| }) | |||
| export class ForgotPasswordComponent extends BlankLayoutCardComponent { } | |||
| @ -1 +0,0 @@ | |||
| export { ForgotPasswordComponent } from './forgot-password.component'; | |||
| @ -1 +0,0 @@ | |||
| export { PagesModule } from './pages.module'; | |||
| @ -1,30 +0,0 @@ | |||
| import { ModuleWithProviders, NgModule } from '@angular/core'; | |||
| import { RouterModule, Routes } from '@angular/router'; | |||
| 
 | |||
| import { LayoutsModule } from 'app/layouts'; | |||
| import { BlankLayoutComponent } from 'app/layouts/blank-layout'; | |||
| import { ErrorComponent } from './error'; | |||
| import { ForgotPasswordComponent } from './forgot-password'; | |||
| import { LoginComponent } from './login'; | |||
| import { SignUpComponent } from './sign-up'; | |||
| 
 | |||
| @NgModule({ | |||
|   imports: [ | |||
|     RouterModule.forChild([ | |||
|       { | |||
|         path: '', | |||
|         component: BlankLayoutComponent, | |||
|         children: [ | |||
|           { path: '404', component: ErrorComponent, pathMatch: 'full' }, | |||
|           { path: 'login', component: LoginComponent, pathMatch: 'full' }, | |||
|           { path: 'sign-up', component: SignUpComponent, pathMatch: 'full' }, | |||
|           { path: 'forgot-password', component: ForgotPasswordComponent, pathMatch: 'full' }, | |||
|           { path: '**', redirectTo: '404' }, | |||
|         ], | |||
|       }, | |||
|     ]), | |||
|     LayoutsModule, | |||
|   ], | |||
|   exports: [RouterModule], | |||
| }) | |||
| export class PagesRoutingModule { } | |||
| @ -1,30 +0,0 @@ | |||
| import { CommonModule } from '@angular/common'; | |||
| import { NgModule } from '@angular/core'; | |||
| import { FormsModule, ReactiveFormsModule } from '@angular/forms'; | |||
| 
 | |||
| import { ThemeModule } from 'theme'; | |||
| 
 | |||
| import { TooltipModule } from '../../../theme/directives/tooltip/tooltip.module'; | |||
| import { ErrorComponent } from './error'; | |||
| import { ForgotPasswordComponent } from './forgot-password'; | |||
| 
 | |||
| import { PagesRoutingModule } from './pages-routing.module'; | |||
| import { SignUpComponent } from './sign-up'; | |||
| 
 | |||
| 
 | |||
| @NgModule({ | |||
|   imports: [ | |||
|     CommonModule, | |||
|     ThemeModule, | |||
|     PagesRoutingModule, | |||
|     FormsModule, | |||
|     ReactiveFormsModule, | |||
|     TooltipModule, | |||
|   ], | |||
|   declarations: [ | |||
|     ErrorComponent, | |||
|     SignUpComponent, | |||
|     ForgotPasswordComponent, | |||
|   ], | |||
| }) | |||
| export class PagesModule { } | |||
| @ -1 +0,0 @@ | |||
| export { SignUpComponent } from './sign-up.component'; | |||
| @ -1,73 +0,0 @@ | |||
| <div class="mdl-card mdl-card__blank-layout-card mdl-shadow--2dp"> | |||
|   <div class="mdl-card__supporting-text color--dark-gray"> | |||
|     <div class="mdl-grid"> | |||
|       <div class="mdl-cell mdl-cell--12-col mdl-cell--4-col-phone"> | |||
|         <span class="mdl-card__title-text text-color--smooth-gray">DARKBOARD</span> | |||
|       </div> | |||
|       <div class="mdl-cell mdl-cell--12-col mdl-cell--4-col-phone"> | |||
|         <span class="blank-layout-card-name text-color--white">Sign up</span> | |||
|       </div> | |||
|       <form class="login-form" [formGroup]="signupForm" (submit)="login()" novalidate autocomplete="off"> | |||
|         <div class="mdl-cell mdl-cell--12-col mdl-cell--4-col-phone"> | |||
|           <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label full-size" | |||
|                [class.is-invalid]="username.invalid && (username.dirty || username.touched)" | |||
|                [class.is-valid]="username.valid && (username.dirty || username.touched)"> | |||
|             <input formControlName="username" | |||
|                    (change)="onInputChange($event)" | |||
|                    class="mdl-textfield__input" type="text" id="username"> | |||
|             <label class="mdl-textfield__label" for="username">Name</label> | |||
| 
 | |||
|             <div *ngIf="username.invalid && (username.dirty || username.touched)"> | |||
|               <span *ngIf="username.errors.required" class="mdl-textfield__error"> | |||
|                 Name is required. <span class="color-text--orange"> Please, write any name.</span> | |||
|               </span> | |||
|             </div> | |||
|           </div> | |||
|           <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label full-size" | |||
|                [class.is-invalid]="password.invalid && (password.dirty || password.touched)" | |||
|                [class.is-valid]="password.valid && (password.dirty || password.touched)" id="forRass"> | |||
|             <input formControlName="password" | |||
|                    (change)="onInputChange($event)" | |||
|                    class="mdl-textfield__input" type="password" id="password"> | |||
|             <label class="mdl-textfield__label" for="password">Password</label> | |||
|             <div *ngIf="password.invalid && (password.dirty || password.touched)"> | |||
|               <span *ngIf="password.errors.required" class="mdl-textfield__error"> | |||
|                 Password is required. <span class="color-text--orange"> Please, write any password.</span> | |||
|               </span> | |||
|             </div> | |||
|           </div> | |||
|           <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label full-size" | |||
|                [class.is-invalid]="email.invalid && (email.dirty || email.touched)" | |||
|                [class.is-valid]="email.valid && (email.dirty || email.touched)"> | |||
|             <input formControlName="email" | |||
|                    pattern="{{emailPattern}}" | |||
|                    (change)="onInputChange($event)" | |||
|                    class="mdl-textfield__input" type="text" id="email"> | |||
|             <label class="mdl-textfield__label" for="email">Email</label> | |||
| 
 | |||
|             <div *ngIf="email.invalid && (email.dirty || email.touched)"> | |||
|               <span *ngIf="email.errors.required" class="mdl-textfield__error"> | |||
|                 Email is required. <span class="color-text--orange"> Please, write any valid email.</span> | |||
|               </span> | |||
|               <span *ngIf="email.errors.pattern" class="mdl-textfield__error"> | |||
|                 Email is invalid. | |||
|               </span> | |||
|             </div> | |||
|           </div> | |||
| 
 | |||
|           <label baseCheckbox color="light-blue" class="checkbox--inline" inline></label> | |||
|           <span class="blank-layout-card-link">I agree all statements in <a href="#" | |||
|                                                                             class="underlined">terms of service</a></span> | |||
|         </div> | |||
|         <div class="mdl-cell mdl-cell--12-col mdl-cell--4-col-phone submit-cell"> | |||
|           <a routerLink="/pages/login" class="blank-layout-card-link">I have already signed up</a> | |||
|           <div class="mdl-layout-spacer"></div> | |||
|           <button class="mdl-button mdl-js-button mdl-button--raised color--light-blue" | |||
|                   type="submit" [disabled]="signupForm.invalid"> | |||
|             SIGN UP | |||
|           </button> | |||
|         </div> | |||
|       </form> | |||
|     </div> | |||
|   </div> | |||
| </div> | |||
| @ -1,61 +0,0 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; | |||
| import { Router } from '@angular/router'; | |||
| 
 | |||
| import { AuthService } from '@services/*'; | |||
| 
 | |||
| import { BlankLayoutCardComponent } from 'app/components/blank-layout-card'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'app-sign-up', | |||
|   styleUrls: ['../../../components/blank-layout-card/blank-layout-card.component.scss'], | |||
|   templateUrl: './sign-up.component.html', | |||
| }) | |||
| export class SignUpComponent extends BlankLayoutCardComponent implements OnInit { | |||
| 
 | |||
|   public signupForm: FormGroup; | |||
|   public email; | |||
|   public password; | |||
|   public username; | |||
|   public emailPattern = '^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$'; | |||
|   public error: string; | |||
| 
 | |||
|   constructor(private authService: AuthService, | |||
|               private fb: FormBuilder, | |||
|               private router: Router) { | |||
|     super(); | |||
| 
 | |||
|     this.signupForm = this.fb.group({ | |||
|       password: new FormControl('', Validators.required), | |||
|       email: new FormControl('', [ | |||
|         Validators.required, | |||
|         Validators.pattern(this.emailPattern), | |||
|         Validators.maxLength(20), | |||
|       ]), | |||
|       username: new FormControl('', [Validators.required, Validators.maxLength(10)]), | |||
|     }); | |||
|     this.email = this.signupForm.get('email'); | |||
|     this.password = this.signupForm.get('password'); | |||
|     this.username = this.signupForm.get('username'); | |||
|   } | |||
| 
 | |||
|   public ngOnInit() { | |||
|     this.authService.logout(); | |||
|     this.signupForm.valueChanges.subscribe(() => { | |||
|       this.error = null; | |||
|     }); | |||
|   } | |||
| 
 | |||
|   public login() { | |||
|     this.error = null; | |||
|     if (this.signupForm.valid) { | |||
|       this.authService.signup(this.signupForm.getRawValue()) | |||
|         .subscribe(res => this.router.navigate(['/app/dashboard']), | |||
|                    error => this.error = error.message); | |||
|     } | |||
|   } | |||
| 
 | |||
|   public onInputChange(event) { | |||
|     event.target.required = true; | |||
|   } | |||
| } | |||
| @ -1,6 +0,0 @@ | |||
| @import '~material-design-lite/src/functions'; | |||
| @import '~material-design-lite/src/variables'; | |||
| @import '~material-design-lite/src/mixins'; | |||
| @import '~material-design-lite/src/color-definitions'; | |||
| @import './scss/variables'; | |||
| @import './scss/mixins'; | |||
| @ -1,14 +0,0 @@ | |||
| import { Component, HostBinding, Input, ViewChild, ViewContainerRef } from '@angular/core'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'base-card base-card-actions', | |||
|   styleUrls: ['./card.component.scss'], | |||
|   template: `<ng-content></ng-content>`, | |||
| }) | |||
| export class CardActionsComponent { | |||
|   @HostBinding('class.mdl-card__actions') private readonly mdlCardActions = true; | |||
| 
 | |||
|   constructor( | |||
|     private viewContainerRef: ViewContainerRef, | |||
|   ) { } | |||
| } | |||
| @ -1,18 +0,0 @@ | |||
| import { Component, HostBinding, Input, ViewChild } from '@angular/core'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'base-card base-card-body', | |||
|   styleUrls: ['./card.component.scss'], | |||
|   template: `<ng-content></ng-content>`, | |||
| }) | |||
| export class CardBodyComponent { | |||
|   @HostBinding('class.mdl-card__supporting-text') private readonly mdlCardSupportingText = true; | |||
| 
 | |||
|   @HostBinding('class.mdl-card--expand') private isExpanded = false; | |||
| 
 | |||
|   @Input() set expanded(value) { | |||
|     if (value || value === '') { | |||
|       this.isExpanded = true; | |||
|     } | |||
|   } | |||
| } | |||
| @ -1,10 +0,0 @@ | |||
| import { Component, HostBinding, Input, ViewChild, ViewContainerRef } from '@angular/core'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'base-card base-card-menu', | |||
|   styleUrls: ['./card.component.scss'], | |||
|   template: `<ng-content></ng-content>`, | |||
| }) | |||
| export class CardMenuComponent { | |||
|   @HostBinding('class.mdl-card__menu') private readonly mdlCardMenu = true; | |||
| } | |||
| @ -1,21 +0,0 @@ | |||
| import { Component, HostBinding, Input, ViewChild } from '@angular/core'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'base-card base-card-title', | |||
|   styleUrls: ['./card.component.scss'], | |||
|   template: `<ng-content></ng-content>`, | |||
| }) | |||
| export class CardTitleComponent { | |||
|   @HostBinding('class.mdl-card__title') private readonly mdlCardTitle = true; | |||
|   @HostBinding('class.mdl-card--border') private readonly mdlCardBorder = true; | |||
| 
 | |||
| 
 | |||
| 
 | |||
|   @HostBinding('class.mdl-card--expand') private isExpanded = false; | |||
| 
 | |||
|   @Input() set expanded(value) { | |||
|     if (value || value === '') { | |||
|       this.isExpanded = true; | |||
|     } | |||
|   } | |||
| } | |||
| @ -1,20 +0,0 @@ | |||
| @import '~theme/helpers'; | |||
| 
 | |||
| .mdl-card__title { | |||
|   background-color: $card-title-background-color; | |||
| } | |||
| 
 | |||
| .mdl-card__supporting-text { | |||
|   line-height: 22px; | |||
|   width: calc(100% - #{$card-horizontal-padding*2}); | |||
|   overflow: visible; | |||
| } | |||
| 
 | |||
| .mdl-card__actions { | |||
|   padding: 8px 16px; | |||
| } | |||
| 
 | |||
| .mdl-card { | |||
|   height: 100%; | |||
|   overflow: visible; | |||
| } | |||
| @ -1,15 +0,0 @@ | |||
| import { Component, HostBinding, Input, ViewChild, ViewContainerRef } from '@angular/core'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'base-card', | |||
|   styleUrls: ['./card.component.scss'], | |||
|   template: `<ng-content></ng-content>`, | |||
| }) | |||
| export class CardComponent { | |||
|   @HostBinding('class.mdl-card') private readonly mdlCard = true; | |||
|   @HostBinding('class.mdl-shadow--2dp') private readonly mdlShadow2DP = true; | |||
| 
 | |||
|   constructor( | |||
|     private viewContainerRef: ViewContainerRef, | |||
|   ) { } | |||
| } | |||
| @ -1,29 +0,0 @@ | |||
| import { CommonModule } from '@angular/common'; | |||
| import { NgModule } from '@angular/core'; | |||
| 
 | |||
| import { CardActionsComponent } from './card-actions.component'; | |||
| import { CardBodyComponent } from './card-body.component'; | |||
| import { CardMenuComponent } from './card-menu.component'; | |||
| import { CardTitleComponent } from './card-title.component'; | |||
| import { CardComponent } from './card.component'; | |||
| 
 | |||
| @NgModule({ | |||
|   imports: [ | |||
|     CommonModule, | |||
|   ], | |||
|   declarations: [ | |||
|     CardComponent, | |||
|     CardTitleComponent, | |||
|     CardMenuComponent, | |||
|     CardBodyComponent, | |||
|     CardActionsComponent, | |||
|   ], | |||
|   exports: [ | |||
|     CardComponent, | |||
|     CardTitleComponent, | |||
|     CardMenuComponent, | |||
|     CardBodyComponent, | |||
|     CardActionsComponent, | |||
|   ], | |||
| }) | |||
| export class CardModule { } | |||
| @ -1,6 +0,0 @@ | |||
| export { CardActionsComponent } from './card-actions.component'; | |||
| export { CardBodyComponent } from './card-body.component'; | |||
| export { CardMenuComponent } from './card-menu.component'; | |||
| export { CardTitleComponent } from './card-title.component'; | |||
| export { CardComponent } from './card.component'; | |||
| export { CardModule } from './card.module'; | |||
| @ -1,23 +0,0 @@ | |||
| import { Component, HostBinding, Input } from '@angular/core'; | |||
| 
 | |||
| import { ToggleComponent } from 'theme/components/toggle/toggle.component'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'label[baseCheckbox]', | |||
|   styleUrls: ['../toggle/toggle.component.scss'], | |||
|   template: ` | |||
|     <input type="checkbox" [id]="innerID" class="mdl-checkbox__input" [checked]="isChecked" (change)="isChecked = !isChecked"> | |||
|     <span class="mdl-checkbox__label"><ng-content></ng-content></span> | |||
|   `,
 | |||
| }) | |||
| export class CheckboxComponent extends ToggleComponent { | |||
|   private isInline = false; | |||
|   @Input() private set inline(value) { | |||
|     if (value || value === '') { | |||
|       this.isInline = true; | |||
|     } | |||
|   } | |||
|   @HostBinding('class') private get className() { | |||
|     return `mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect checkbox--colored-${this.color} ${this.isInline && 'checkbox--inline'}`; | |||
|   } | |||
| } | |||
| @ -1 +0,0 @@ | |||
| export { CheckboxComponent } from './checkbox.component'; | |||
| @ -1,17 +0,0 @@ | |||
| import { Component, HostBinding, Input } from '@angular/core'; | |||
| 
 | |||
| import { ToggleComponent } from 'theme/components/toggle/toggle.component'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'label[baseIconToggle]', | |||
|   styleUrls: ['../toggle/toggle.component.scss'], | |||
|   template: ` | |||
|     <input type="checkbox" [id]="innerID" class="mdl-icon-toggle__input" [checked]="isChecked" (change)="isChecked = !isChecked"> | |||
|     <i class="mdl-icon-toggle__label material-icons"><ng-content></ng-content></i> | |||
|   `,
 | |||
| }) | |||
| export class IconToggleComponent extends ToggleComponent { | |||
|   @HostBinding('class') private get className() { | |||
|     return `mdl-icon-toggle mdl-js-icon-toggle mdl-js-ripple-effect icon-toggle--colored-${this.color}`; | |||
|   } | |||
| } | |||
| @ -1 +0,0 @@ | |||
| export { IconToggleComponent } from './icon-toggle.component'; | |||
| @ -1 +0,0 @@ | |||
| export { PageTopComponent } from './page-top.component'; | |||
| @ -1,62 +0,0 @@ | |||
| @import '~theme/helpers'; | |||
| 
 | |||
| @media screen and (max-width: $layout-screen-size-threshold) { | |||
|   .mdl-layout__header { | |||
|     display: flex !important; | |||
|   } | |||
| } | |||
| 
 | |||
| .account-dropdown { | |||
|   &.mdl-menu { | |||
|     width: 310px; | |||
|   } | |||
| 
 | |||
|   .mdl-list__item { | |||
|     font-size: 1rem; | |||
| 
 | |||
|     &:first-child { | |||
|       font-size: 16px; | |||
|       padding-top: $list-min-padding/2; | |||
|       padding-bottom: $list-min-padding/2; | |||
|       height: $account-dropdown-avatar-size + $list-min-padding; | |||
| 
 | |||
|       .mdl-list__item-primary-content { | |||
|         height: $account-dropdown-avatar-size; | |||
|         line-height: 28px; | |||
| 
 | |||
|         .mdl-list__item-avatar { | |||
|           height: $account-dropdown-avatar-size; | |||
|           width: $account-dropdown-avatar-size; | |||
|           background: url("#{$image-path}/Icon.png"); | |||
|           background-size: cover; | |||
|         } | |||
| 
 | |||
|         .mdl-list__item-sub-title { | |||
|           font-weight: 300; | |||
|         } | |||
|       } | |||
|     } | |||
| 
 | |||
|     &:hover .mdl-list__item-icon { | |||
|       color: $list-icon-hover-color; | |||
|     } | |||
|   } | |||
| 
 | |||
|   .list__item--border-top { | |||
|     margin-top: 8px; | |||
|     padding-top: 8px; | |||
|   } | |||
| } | |||
| 
 | |||
| .settings-dropdown { | |||
|   width: $settings_dropdown_width; | |||
| 
 | |||
|   .mdl-menu__item, | |||
|   a { | |||
|     @include typo-dropdown-menu-li; | |||
|   } | |||
| } | |||
| 
 | |||
| .search { | |||
|   padding: 18px 0 !important; | |||
| } | |||
| @ -1,10 +0,0 @@ | |||
| import { Component, HostBinding } from '@angular/core'; | |||
| 
 | |||
| @Component({ | |||
|   selector: 'base-page-top', | |||
|   styleUrls: ['./page-top.component.scss'], | |||
|   template: `<ng-content></ng-content>`, | |||
| }) | |||
| export class PageTopComponent { | |||
|   @HostBinding('class.mdl-layout__header-row') protected readonly mdlLayoutHeaderRow = true; | |||
| } | |||
| @ -1 +0,0 @@ | |||
| export { PaginationComponent } from './pagination.component'; | |||
| @ -1,13 +0,0 @@ | |||
| <span (click)="onChangePage(-1)"><i class="material-icons">chevron_left</i></span> | |||
| <span> {{ currentPage }} of {{ numPage }} </span> | |||
| <span (click)="onChangePage(1)"><i class="material-icons">chevron_right</i></span> | |||
| <div class="goto"> | |||
|   <span> Go to </span> | |||
|   <form (submit)="goToPage($event)"> | |||
|     <input [(ngModel)]="inputNumPage" name="inputPage" | |||
|            class="mdl-textfield__input" | |||
|            type="number" | |||
|            min="1" | |||
|            [max]="numPage"> | |||
|   </form> | |||
| </div> | |||
| @ -1,36 +0,0 @@ | |||
| @import '~theme/helpers'; | |||
| 
 | |||
| .pagination { | |||
|   font-family: Roboto, Helvetica, sans-serif; | |||
|   color: $color-smooth-gray; | |||
|   font-size: 14px; | |||
|   position: relative; | |||
|   line-height: 16px; | |||
|   user-select: none; | |||
| 
 | |||
|   .material-icons { | |||
|     cursor: pointer; | |||
|     position: relative; | |||
|     top: 0.5rem; | |||
|     margin: 0 0.5rem; | |||
|   } | |||
| 
 | |||
|   .goto { | |||
|     display: inline-block; | |||
|     margin: 0 1rem 0 2rem; | |||
| 
 | |||
|     form { | |||
|       width: 30px; | |||
|       display: inline-block; | |||
| 
 | |||
|       input { | |||
|         text-align: center; | |||
|         font-family: Roboto, Helvetica, sans-serif; | |||
| 
 | |||
|         &:focus { | |||
|           outline: none; | |||
|         } | |||
|       } | |||
|     } | |||
|   } | |||
| } | |||
Some files were not shown because too many files changed in this diff
					Loading…
					
					
				
		Reference in new issue