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 { Component, HostBinding } from '@angular/core'; | ||||
| 
 | 
 | ||||
| import { UpgradableComponent } from 'theme/components/upgradable'; |  | ||||
| 
 | 
 | ||||
| @Component({ | @Component({ | ||||
|   selector: 'app-components', |   selector: 'app-components', | ||||
|   templateUrl: './components.component.html', |   templateUrl: './components.component.html', | ||||
|   styleUrls: ['./components.component.scss'], |   styleUrls: ['./components.component.scss'], | ||||
| }) | }) | ||||
| export class ComponentsComponent extends UpgradableComponent { | export class ComponentsComponent { | ||||
|   @HostBinding('class.mdl-grid') private readonly mdlGrid = true; |   @HostBinding('class.mdl-grid') private readonly mdlGrid = true; | ||||
|   @HostBinding('class.ui-components') private readonly uiComponents = 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