mirror of
https://github.com/bitwarden/browser.git
synced 2025-02-28 03:21:40 +01:00
setup services, accounts components
This commit is contained in:
parent
28197970bd
commit
a4ff241574
2
jslib
2
jslib
@ -1 +1 @@
|
||||
Subproject commit 66b3dbae177a795d6b5168f7cdbba2f98f309272
|
||||
Subproject commit 476d21e9f07f648784e92e6e7ec4ae37910e2449
|
5
locales/en/messages.json
Normal file
5
locales/en/messages.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"hello": {
|
||||
"message": "hello world"
|
||||
}
|
||||
}
|
5
locales/es/messages.json
Normal file
5
locales/es/messages.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"hello": {
|
||||
"message": "hola mundo"
|
||||
}
|
||||
}
|
30
src/app/accounts/hint.component.html
Normal file
30
src/app/accounts/hint.component.html
Normal file
@ -0,0 +1,30 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/login">{{'cancel' | i18n}}</a>
|
||||
</div>
|
||||
<div class="center">
|
||||
<span class="title">{{'passwordHint' | i18n}}</span>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading">
|
||||
<span [hidden]="form.loading">{{'submit' | i18n}}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{'emailAddress' | i18n}}</label>
|
||||
<input id="email" type="text" name="Email" [(ngModel)]="email" required appAutofocus
|
||||
appInputVerbatim="false">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'enterEmailToGetHint' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
22
src/app/accounts/hint.component.ts
Normal file
22
src/app/accounts/hint.component.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
import { Angulartics2 } from 'angulartics2';
|
||||
|
||||
import { ApiService } from 'jslib/abstractions/api.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
|
||||
import { HintComponent as BaseHintComponent } from 'jslib/angular/components/hint.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hint',
|
||||
templateUrl: 'hint.component.html',
|
||||
})
|
||||
export class HintComponent extends BaseHintComponent {
|
||||
constructor(router: Router, analytics: Angulartics2,
|
||||
toasterService: ToasterService, i18nService: I18nService,
|
||||
apiService: ApiService) {
|
||||
super(router, analytics, toasterService, i18nService, apiService);
|
||||
}
|
||||
}
|
40
src/app/accounts/login.component.html
Normal file
40
src/app/accounts/login.component.html
Normal file
@ -0,0 +1,40 @@
|
||||
<form id="login-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="content">
|
||||
<p class="lead">{{'loginOrCreateNewAccount' | i18n}}</p>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{'emailAddress' | i18n}}</label>
|
||||
<input id="email" type="text" name="Email" [(ngModel)]="email" required
|
||||
[appAutofocus]="email === ''">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
||||
class="monospaced" [(ngModel)]="masterPassword" required [appAutofocus]="email !== ''">
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick
|
||||
title="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
|
||||
<i class="fa fa-lg"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading"><i class="fa fa-sign-in"></i> {{'logIn' | i18n}}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading"></i>
|
||||
</button>
|
||||
<a routerLink="/register" class="btn block">
|
||||
<i class="fa fa-pencil-square-o"></i> {{'createAccount' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<a routerLink="/hint">{{'getMasterPasswordHint' | i18n}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
24
src/app/accounts/login.component.ts
Normal file
24
src/app/accounts/login.component.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
import { Angulartics2 } from 'angulartics2';
|
||||
|
||||
import { AuthService } from 'jslib/abstractions/auth.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { SyncService } from 'jslib/abstractions/sync.service';
|
||||
|
||||
import { LoginComponent as BaseLoginComponent } from 'jslib/angular/components/login.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: 'login.component.html',
|
||||
})
|
||||
export class LoginComponent extends BaseLoginComponent {
|
||||
constructor(authService: AuthService, router: Router,
|
||||
analytics: Angulartics2, toasterService: ToasterService,
|
||||
i18nService: I18nService, private syncService: SyncService) {
|
||||
super(authService, router, analytics, toasterService, i18nService);
|
||||
this.successRoute = '/vault';
|
||||
}
|
||||
}
|
71
src/app/accounts/register.component.html
Normal file
71
src/app/accounts/register.component.html
Normal file
@ -0,0 +1,71 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/home">{{'cancel' | i18n}}</a>
|
||||
</div>
|
||||
<div class="center">
|
||||
<span class="title">{{'createAccount' | i18n}}</span>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading">
|
||||
<span [hidden]="form.loading">{{'submit' | i18n}}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{'emailAddress' | i18n}}</label>
|
||||
<input id="email" type="text" name="Email" [(ngModel)]="email" required
|
||||
[appAutofocus]="email === ''" inputmode="email" appInputVerbatim="false">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
||||
class="monospaced" [(ngModel)]="masterPassword" required [appAutofocus]="email !== ''"
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick
|
||||
title="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
|
||||
<i class="fa fa-lg"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
|
||||
<input id="masterPasswordRetype" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPasswordRetype" class="monospaced" [(ngModel)]="confirmMasterPassword"
|
||||
required appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick
|
||||
title="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
|
||||
<i class="fa fa-lg"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{'masterPassHint' | i18n}}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassHintDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
25
src/app/accounts/register.component.ts
Normal file
25
src/app/accounts/register.component.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
import { Angulartics2 } from 'angulartics2';
|
||||
|
||||
import { ApiService } from 'jslib/abstractions/api.service';
|
||||
import { AuthService } from 'jslib/abstractions/auth.service';
|
||||
import { CryptoService } from 'jslib/abstractions/crypto.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
|
||||
import { RegisterComponent as BaseRegisterComponent } from 'jslib/angular/components/register.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-register',
|
||||
templateUrl: 'register.component.html',
|
||||
})
|
||||
export class RegisterComponent extends BaseRegisterComponent {
|
||||
constructor(authService: AuthService, router: Router,
|
||||
analytics: Angulartics2, toasterService: ToasterService,
|
||||
i18nService: I18nService, cryptoService: CryptoService,
|
||||
apiService: ApiService) {
|
||||
super(authService, router, analytics, toasterService, i18nService, cryptoService, apiService);
|
||||
}
|
||||
}
|
24
src/app/accounts/two-factor-options.component.html
Normal file
24
src/app/accounts/two-factor-options.component.html
Normal file
@ -0,0 +1,24 @@
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/2fa">{{'close' | i18n}}</a>
|
||||
</div>
|
||||
<div class="center">
|
||||
<span class="title">{{'twoStepOptions' | i18n}}</span>
|
||||
</div>
|
||||
<div class="right"></div>
|
||||
</header>
|
||||
<content>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<a href="#" appStopClick *ngFor="let p of providers" class="box-content-row"
|
||||
(click)="choose(p)">
|
||||
<span class="text">{{p.name}}</span>
|
||||
<span class="detail">{{p.description}}</span>
|
||||
</a>
|
||||
<a href="#" appStopClick class="box-content-row" (click)="recover()">
|
||||
<span class="text">{{'recoveryCodeTitle' | i18n}}</span>
|
||||
<span class="detail">{{'recoveryCodeDesc' | i18n}}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
31
src/app/accounts/two-factor-options.component.ts
Normal file
31
src/app/accounts/two-factor-options.component.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
import { Angulartics2 } from 'angulartics2';
|
||||
|
||||
import { AuthService } from 'jslib/abstractions/auth.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
|
||||
import {
|
||||
TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent,
|
||||
} from 'jslib/angular/components/two-factor-options.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-two-factor-options',
|
||||
templateUrl: 'two-factor-options.component.html',
|
||||
})
|
||||
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
|
||||
constructor(authService: AuthService, router: Router,
|
||||
analytics: Angulartics2, toasterService: ToasterService,
|
||||
i18nService: I18nService, platformUtilsService: PlatformUtilsService) {
|
||||
super(authService, router, analytics, toasterService, i18nService, platformUtilsService, window);
|
||||
}
|
||||
|
||||
choose(p: any) {
|
||||
super.choose(p);
|
||||
this.authService.selectedTwoFactorProviderType = p.type;
|
||||
this.router.navigate(['2fa']);
|
||||
}
|
||||
}
|
108
src/app/accounts/two-factor.component.html
Normal file
108
src/app/accounts/two-factor.component.html
Normal file
@ -0,0 +1,108 @@
|
||||
<form id="two-factor-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<header>
|
||||
<div class="left">
|
||||
<a routerLink="/login">{{'back' | i18n}}</a>
|
||||
</div>
|
||||
<div class="center">
|
||||
<span class="title">{{title}}</span>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading"
|
||||
*ngIf="selectedProviderType != null && selectedProviderType !== providerType.Duo &&
|
||||
selectedProviderType !== providerType.OrganizationDuo">
|
||||
<span [hidden]="form.loading">{{'continue' | i18n}}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.Authenticator ||
|
||||
selectedProviderType === providerType.Email">
|
||||
<div class="content text-center">
|
||||
<span *ngIf="selectedProviderType === providerType.Authenticator">
|
||||
{{'enterVerificationCodeApp' | i18n}}
|
||||
</span>
|
||||
<span *ngIf="selectedProviderType === providerType.Email">
|
||||
{{'enterVerificationCodeEmail' | i18n : twoFactorEmail}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="box first">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="code">{{'verificationCode' | i18n}}</label>
|
||||
<input id="code" type="text" name="Code" [(ngModel)]="token" required appAutofocus
|
||||
inputmode="tel" appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.Yubikey">
|
||||
<div class="content text-center">
|
||||
<p>{{'insertYubiKey' | i18n}}</p>
|
||||
<img src="../../images/two-factor/yubikey.jpg" class="img-rounded img-responsive" alt="">
|
||||
</div>
|
||||
<div class="box first">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="code" class="sr-only">{{'verificationCode' | i18n}}</label>
|
||||
<input id="code" type="password" name="Code" [(ngModel)]="token" required appAutofocus
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.U2f">
|
||||
<div class="content text-center">
|
||||
<span *ngIf="!u2fReady"><i class="fa fa-spinner fa-spin"></i></span>
|
||||
<div *ngIf="u2fReady">
|
||||
<p>{{'insertU2f' | i18n}}</p>
|
||||
<img src="../../images/two-factor/u2fkey.jpg" alt="" class="img-rounded img-responsive" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="box first">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.Duo ||
|
||||
selectedProviderType === providerType.OrganizationDuo">
|
||||
<div id="duo-frame" *ngIf="!showNewWindowMessage"><iframe id="duo_iframe"></iframe></div>
|
||||
<div *ngIf="showNewWindowMessage" class="content text-center">{{'twoStepNewWindowMessage' | i18n}}</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="content text-center" *ngIf="selectedProviderType == null">
|
||||
<p>{{'noTwoStepProviders' | i18n}}</p>
|
||||
<p>{{'noTwoStepProviders2' | i18n}}</p>
|
||||
</div>
|
||||
<div class="content no-vpad text-center" *ngIf="selectedProviderType != null">
|
||||
<p>
|
||||
<a href="#" appStopClick (click)="anotherMethod()">{{'useAnotherTwoStepMethod' | i18n}}</a>
|
||||
</p>
|
||||
<p *ngIf="selectedProviderType === providerType.Email">
|
||||
<a href="#" appStopClick (click)="sendEmail(true)" [appApiAction]="emailPromise">
|
||||
{{'sendVerificationCodeEmailAgain' | i18n}}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
||||
<iframe id="u2f_iframe" hidden></iframe>
|
50
src/app/accounts/two-factor.component.ts
Normal file
50
src/app/accounts/two-factor.component.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
import { Angulartics2 } from 'angulartics2';
|
||||
|
||||
import { TwoFactorProviderType } from 'jslib/enums/twoFactorProviderType';
|
||||
|
||||
import { ApiService } from 'jslib/abstractions/api.service';
|
||||
import { AuthService } from 'jslib/abstractions/auth.service';
|
||||
import { EnvironmentService } from 'jslib/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { SyncService } from 'jslib/abstractions/sync.service';
|
||||
|
||||
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
|
||||
|
||||
import { TwoFactorComponent as BaseTwoFactorComponent } from 'jslib/angular/components/two-factor.component';
|
||||
|
||||
const BroadcasterSubscriptionId = 'TwoFactorComponent';
|
||||
|
||||
@Component({
|
||||
selector: 'app-two-factor',
|
||||
templateUrl: 'two-factor.component.html',
|
||||
})
|
||||
export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
showNewWindowMessage = false;
|
||||
|
||||
constructor(authService: AuthService, router: Router,
|
||||
analytics: Angulartics2, toasterService: ToasterService,
|
||||
i18nService: I18nService, apiService: ApiService,
|
||||
platformUtilsService: PlatformUtilsService, syncService: SyncService,
|
||||
environmentService: EnvironmentService, private ngZone: NgZone,
|
||||
private broadcasterService: BroadcasterService, private changeDetectorRef: ChangeDetectorRef) {
|
||||
super(authService, router, analytics, toasterService, i18nService, apiService,
|
||||
platformUtilsService, window, environmentService);
|
||||
this.successRoute = '/vault';
|
||||
}
|
||||
|
||||
anotherMethod() {
|
||||
this.router.navigate(['2fa-options']);
|
||||
}
|
||||
}
|
@ -4,10 +4,19 @@ import {
|
||||
Routes,
|
||||
} from '@angular/router';
|
||||
|
||||
import { HintComponent } from './accounts/hint.component';
|
||||
import { LoginComponent } from './accounts/login.component';
|
||||
import { RegisterComponent } from './accounts/register.component';
|
||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||
|
||||
import { VaultComponent } from './vault/vault.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: '/vault', pathMatch: 'full' },
|
||||
{ path: '', redirectTo: '/login', pathMatch: 'full' },
|
||||
{ path: 'login', component: LoginComponent },
|
||||
{ path: '2fa', component: TwoFactorComponent },
|
||||
{ path: 'register', component: RegisterComponent },
|
||||
{ path: 'hint', component: HintComponent },
|
||||
{
|
||||
path: 'vault',
|
||||
component: VaultComponent,
|
||||
|
@ -1,6 +1,10 @@
|
||||
import 'core-js';
|
||||
import 'zone.js/dist/zone';
|
||||
|
||||
import { ToasterModule } from 'angular2-toaster';
|
||||
import { Angulartics2Module } from 'angulartics2';
|
||||
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
@ -8,19 +12,59 @@ import { FormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
import { ServicesModule } from './services.module';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
import { HintComponent } from './accounts/hint.component';
|
||||
import { LoginComponent } from './accounts/login.component';
|
||||
import { RegisterComponent } from './accounts/register.component';
|
||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||
|
||||
import { VaultComponent } from './vault/vault.component';
|
||||
|
||||
import { ApiActionDirective } from 'jslib/angular/directives/api-action.directive';
|
||||
import { AutofocusDirective } from 'jslib/angular/directives/autofocus.directive';
|
||||
import { BlurClickDirective } from 'jslib/angular/directives/blur-click.directive';
|
||||
import { BoxRowDirective } from 'jslib/angular/directives/box-row.directive';
|
||||
import { FallbackSrcDirective } from 'jslib/angular/directives/fallback-src.directive';
|
||||
import { InputVerbatimDirective } from 'jslib/angular/directives/input-verbatim.directive';
|
||||
import { StopClickDirective } from 'jslib/angular/directives/stop-click.directive';
|
||||
import { StopPropDirective } from 'jslib/angular/directives/stop-prop.directive';
|
||||
|
||||
import { I18nPipe } from 'jslib/angular/pipes/i18n.pipe';
|
||||
import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
FormsModule,
|
||||
AppRoutingModule,
|
||||
ServicesModule,
|
||||
Angulartics2Module.forRoot([Angulartics2GoogleAnalytics], {
|
||||
pageTracking: {
|
||||
clearQueryParams: true,
|
||||
},
|
||||
}),
|
||||
ToasterModule,
|
||||
],
|
||||
declarations: [
|
||||
ApiActionDirective,
|
||||
AppComponent,
|
||||
AutofocusDirective,
|
||||
BlurClickDirective,
|
||||
BoxRowDirective,
|
||||
FallbackSrcDirective,
|
||||
HintComponent,
|
||||
I18nPipe,
|
||||
InputVerbatimDirective,
|
||||
LoginComponent,
|
||||
RegisterComponent,
|
||||
SearchCiphersPipe,
|
||||
StopClickDirective,
|
||||
StopPropDirective,
|
||||
TwoFactorComponent,
|
||||
VaultComponent,
|
||||
],
|
||||
entryComponents: [
|
||||
|
167
src/app/services.module.ts
Normal file
167
src/app/services.module.ts
Normal file
@ -0,0 +1,167 @@
|
||||
import {
|
||||
APP_INITIALIZER,
|
||||
NgModule,
|
||||
} from '@angular/core';
|
||||
|
||||
import { ToasterModule } from 'angular2-toaster';
|
||||
|
||||
import { I18nService } from '../services/i18n.service';
|
||||
import { WebMessagingService } from '../services/webMessaging.service';
|
||||
import { WebPlatformUtilsService } from '../services/webPlatformUtils.service';
|
||||
import { WebStorageService } from '../services/webStorage.service';
|
||||
|
||||
import { AuthGuardService } from 'jslib/angular/services/auth-guard.service';
|
||||
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
|
||||
import { ValidationService } from 'jslib/angular/services/validation.service';
|
||||
|
||||
import { Analytics } from 'jslib/misc/analytics';
|
||||
|
||||
import { ApiService } from 'jslib/services/api.service';
|
||||
import { AppIdService } from 'jslib/services/appId.service';
|
||||
import { AuditService } from 'jslib/services/audit.service';
|
||||
import { AuthService } from 'jslib/services/auth.service';
|
||||
import { CipherService } from 'jslib/services/cipher.service';
|
||||
import { CollectionService } from 'jslib/services/collection.service';
|
||||
import { ConstantsService } from 'jslib/services/constants.service';
|
||||
import { ContainerService } from 'jslib/services/container.service';
|
||||
import { CryptoService } from 'jslib/services/crypto.service';
|
||||
import { EnvironmentService } from 'jslib/services/environment.service';
|
||||
import { ExportService } from 'jslib/services/export.service';
|
||||
import { FolderService } from 'jslib/services/folder.service';
|
||||
import { LockService } from 'jslib/services/lock.service';
|
||||
import { PasswordGenerationService } from 'jslib/services/passwordGeneration.service';
|
||||
import { SettingsService } from 'jslib/services/settings.service';
|
||||
import { StateService } from 'jslib/services/state.service';
|
||||
import { SyncService } from 'jslib/services/sync.service';
|
||||
import { TokenService } from 'jslib/services/token.service';
|
||||
import { TotpService } from 'jslib/services/totp.service';
|
||||
import { UserService } from 'jslib/services/user.service';
|
||||
import { WebCryptoFunctionService } from 'jslib/services/webCryptoFunction.service';
|
||||
|
||||
import { ApiService as ApiServiceAbstraction } from 'jslib/abstractions/api.service';
|
||||
import { AppIdService as AppIdServiceAbstraction } from 'jslib/abstractions/appId.service';
|
||||
import { AuditService as AuditServiceAbstraction } from 'jslib/abstractions/audit.service';
|
||||
import { AuthService as AuthServiceAbstraction } from 'jslib/abstractions/auth.service';
|
||||
import { CipherService as CipherServiceAbstraction } from 'jslib/abstractions/cipher.service';
|
||||
import { CollectionService as CollectionServiceAbstraction } from 'jslib/abstractions/collection.service';
|
||||
import { CryptoService as CryptoServiceAbstraction } from 'jslib/abstractions/crypto.service';
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from 'jslib/abstractions/cryptoFunction.service';
|
||||
import { EnvironmentService as EnvironmentServiceAbstraction } from 'jslib/abstractions/environment.service';
|
||||
import { ExportService as ExportServiceAbstraction } from 'jslib/abstractions/export.service';
|
||||
import { FolderService as FolderServiceAbstraction } from 'jslib/abstractions/folder.service';
|
||||
import { I18nService as I18nServiceAbstraction } from 'jslib/abstractions/i18n.service';
|
||||
import { LockService as LockServiceAbstraction } from 'jslib/abstractions/lock.service';
|
||||
import { LogService as LogServiceAbstraction } from 'jslib/abstractions/log.service';
|
||||
import { MessagingService as MessagingServiceAbstraction } from 'jslib/abstractions/messaging.service';
|
||||
import {
|
||||
PasswordGenerationService as PasswordGenerationServiceAbstraction,
|
||||
} from 'jslib/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from 'jslib/abstractions/platformUtils.service';
|
||||
import { SettingsService as SettingsServiceAbstraction } from 'jslib/abstractions/settings.service';
|
||||
import { StateService as StateServiceAbstraction } from 'jslib/abstractions/state.service';
|
||||
import { StorageService as StorageServiceAbstraction } from 'jslib/abstractions/storage.service';
|
||||
import { SyncService as SyncServiceAbstraction } from 'jslib/abstractions/sync.service';
|
||||
import { TokenService as TokenServiceAbstraction } from 'jslib/abstractions/token.service';
|
||||
import { TotpService as TotpServiceAbstraction } from 'jslib/abstractions/totp.service';
|
||||
import { UserService as UserServiceAbstraction } from 'jslib/abstractions/user.service';
|
||||
|
||||
const i18nService = new I18nService(window.navigator.language, 'locales');
|
||||
const stateService = new StateService();
|
||||
const broadcasterService = new BroadcasterService();
|
||||
const messagingService = new WebMessagingService();
|
||||
const platformUtilsService = new WebPlatformUtilsService(messagingService);
|
||||
const storageService: StorageServiceAbstraction = new WebStorageService();
|
||||
const cryptoFunctionService: CryptoFunctionServiceAbstraction = new WebCryptoFunctionService(window,
|
||||
platformUtilsService);
|
||||
const cryptoService = new CryptoService(storageService, storageService, cryptoFunctionService);
|
||||
const tokenService = new TokenService(storageService);
|
||||
const appIdService = new AppIdService(storageService);
|
||||
const apiService = new ApiService(tokenService, platformUtilsService,
|
||||
async (expired: boolean) => messagingService.send('logout', { expired: expired }));
|
||||
const environmentService = new EnvironmentService(apiService, storageService);
|
||||
const userService = new UserService(tokenService, storageService);
|
||||
const settingsService = new SettingsService(userService, storageService);
|
||||
const cipherService = new CipherService(cryptoService, userService, settingsService,
|
||||
apiService, storageService, i18nService, platformUtilsService);
|
||||
const folderService = new FolderService(cryptoService, userService,
|
||||
() => i18nService.t('noneFolder'), apiService, storageService, i18nService);
|
||||
const collectionService = new CollectionService(cryptoService, userService, storageService, i18nService);
|
||||
const lockService = new LockService(cipherService, folderService, collectionService,
|
||||
cryptoService, platformUtilsService, storageService, messagingService, null);
|
||||
const syncService = new SyncService(userService, apiService, settingsService,
|
||||
folderService, cipherService, cryptoService, collectionService, storageService, messagingService,
|
||||
async (expired: boolean) => messagingService.send('logout', { expired: expired }));
|
||||
const passwordGenerationService = new PasswordGenerationService(cryptoService, storageService);
|
||||
const totpService = new TotpService(storageService, cryptoFunctionService);
|
||||
const containerService = new ContainerService(cryptoService, platformUtilsService);
|
||||
const authService = new AuthService(cryptoService, apiService,
|
||||
userService, tokenService, appIdService, i18nService, platformUtilsService, messagingService);
|
||||
const exportService = new ExportService(folderService, cipherService);
|
||||
const auditService = new AuditService(cryptoFunctionService);
|
||||
|
||||
const analytics = new Analytics(window, () => platformUtilsService.isDev(),
|
||||
platformUtilsService, storageService, appIdService);
|
||||
containerService.attachToWindow(window);
|
||||
environmentService.setUrlsFromStorage().then(() => {
|
||||
return syncService.fullSync(true);
|
||||
});
|
||||
|
||||
export function initFactory(): Function {
|
||||
return async () => {
|
||||
lockService.init(true);
|
||||
const locale = await storageService.get<string>(ConstantsService.localeKey);
|
||||
await i18nService.init(locale);
|
||||
await authService.init();
|
||||
const htmlEl = window.document.documentElement;
|
||||
htmlEl.classList.add('os_' + platformUtilsService.getDeviceString());
|
||||
htmlEl.classList.add('locale_' + i18nService.translationLocale);
|
||||
let theme = await storageService.get<string>(ConstantsService.themeKey);
|
||||
if (theme == null) {
|
||||
theme = 'light';
|
||||
}
|
||||
htmlEl.classList.add('theme_' + theme);
|
||||
stateService.save(ConstantsService.disableFaviconKey,
|
||||
await storageService.get<boolean>(ConstantsService.disableFaviconKey));
|
||||
};
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
ToasterModule,
|
||||
],
|
||||
declarations: [],
|
||||
providers: [
|
||||
ValidationService,
|
||||
AuthGuardService,
|
||||
{ provide: AuditServiceAbstraction, useValue: auditService },
|
||||
{ provide: AuthServiceAbstraction, useValue: authService },
|
||||
{ provide: CipherServiceAbstraction, useValue: cipherService },
|
||||
{ provide: FolderServiceAbstraction, useValue: folderService },
|
||||
{ provide: CollectionServiceAbstraction, useValue: collectionService },
|
||||
{ provide: EnvironmentServiceAbstraction, useValue: environmentService },
|
||||
{ provide: TotpServiceAbstraction, useValue: totpService },
|
||||
{ provide: TokenServiceAbstraction, useValue: tokenService },
|
||||
{ provide: I18nServiceAbstraction, useValue: i18nService },
|
||||
{ provide: CryptoServiceAbstraction, useValue: cryptoService },
|
||||
{ provide: PlatformUtilsServiceAbstraction, useValue: platformUtilsService },
|
||||
{ provide: PasswordGenerationServiceAbstraction, useValue: passwordGenerationService },
|
||||
{ provide: ApiServiceAbstraction, useValue: apiService },
|
||||
{ provide: SyncServiceAbstraction, useValue: syncService },
|
||||
{ provide: UserServiceAbstraction, useValue: userService },
|
||||
{ provide: MessagingServiceAbstraction, useValue: messagingService },
|
||||
{ provide: BroadcasterService, useValue: broadcasterService },
|
||||
{ provide: SettingsServiceAbstraction, useValue: settingsService },
|
||||
{ provide: LockServiceAbstraction, useValue: lockService },
|
||||
{ provide: StorageServiceAbstraction, useValue: storageService },
|
||||
{ provide: StateServiceAbstraction, useValue: stateService },
|
||||
{ provide: ExportServiceAbstraction, useValue: exportService },
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initFactory,
|
||||
deps: [],
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class ServicesModule {
|
||||
}
|
@ -1 +1 @@
|
||||
<b>The vault!!</b>
|
||||
<b>The vault!! {{ 'hello' | i18n }}</b>
|
||||
|
5
src/locales/en/messages.json
Normal file
5
src/locales/en/messages.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"hello": {
|
||||
"message": "hello world"
|
||||
}
|
||||
}
|
5
src/locales/es/messages.json
Normal file
5
src/locales/es/messages.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"hello": {
|
||||
"message": "hola mundo"
|
||||
}
|
||||
}
|
16
src/services/i18n.service.ts
Normal file
16
src/services/i18n.service.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { I18nService as BaseI18nService } from 'jslib/services/i18n.service';
|
||||
|
||||
export class I18nService extends BaseI18nService {
|
||||
constructor(systemLanguage: string, localesDirectory: string) {
|
||||
super(systemLanguage, localesDirectory, async (formattedLocale: string) => {
|
||||
const filePath = this.localesDirectory + '/' + formattedLocale + '/messages.json';
|
||||
const localesResult = await fetch(filePath);
|
||||
const locales = await localesResult.json();
|
||||
return locales;
|
||||
});
|
||||
|
||||
this.supportedTranslationLocales = [
|
||||
'en', 'es',
|
||||
];
|
||||
}
|
||||
}
|
7
src/services/webMessaging.service.ts
Normal file
7
src/services/webMessaging.service.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||
|
||||
export class WebMessagingService implements MessagingService {
|
||||
send(subscriber: string, arg: any = {}) {
|
||||
//
|
||||
}
|
||||
}
|
181
src/services/webPlatformUtils.service.ts
Normal file
181
src/services/webPlatformUtils.service.ts
Normal file
@ -0,0 +1,181 @@
|
||||
import { DeviceType } from 'jslib/enums/deviceType';
|
||||
|
||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
|
||||
import { AnalyticsIds } from 'jslib/misc/analytics';
|
||||
|
||||
const DialogPromiseExpiration = 600000; // 10 minutes
|
||||
|
||||
export class WebPlatformUtilsService implements PlatformUtilsService {
|
||||
identityClientId: string = 'web';
|
||||
|
||||
private showDialogResolves = new Map<number, { resolve: (value: boolean) => void, date: Date }>();
|
||||
private deviceCache: DeviceType = null;
|
||||
private analyticsIdCache: string = null;
|
||||
|
||||
constructor(private messagingService: MessagingService) { }
|
||||
|
||||
getDevice(): DeviceType {
|
||||
if (this.deviceCache) {
|
||||
return this.deviceCache;
|
||||
}
|
||||
|
||||
if (navigator.userAgent.indexOf(' Firefox/') !== -1 || navigator.userAgent.indexOf(' Gecko/') !== -1) {
|
||||
this.deviceCache = DeviceType.Firefox;
|
||||
} else if (navigator.userAgent.indexOf(' OPR/') >= 0) {
|
||||
this.deviceCache = DeviceType.Opera;
|
||||
} else if (navigator.userAgent.indexOf(' Edge/') !== -1) {
|
||||
this.deviceCache = DeviceType.Edge;
|
||||
} else if (navigator.userAgent.indexOf(' Vivaldi/') !== -1) {
|
||||
this.deviceCache = DeviceType.Vivaldi;
|
||||
} else if (navigator.userAgent.indexOf(' Safari/') !== -1 && navigator.userAgent.indexOf('Chrome') === -1) {
|
||||
this.deviceCache = DeviceType.Safari;
|
||||
} else if ((window as any).chrome && navigator.userAgent.indexOf(' Chrome/') !== -1) {
|
||||
this.deviceCache = DeviceType.Chrome;
|
||||
}
|
||||
|
||||
return this.deviceCache;
|
||||
}
|
||||
|
||||
getDeviceString(): string {
|
||||
return DeviceType[this.getDevice()].toLowerCase();
|
||||
}
|
||||
|
||||
isFirefox(): boolean {
|
||||
return this.getDevice() === DeviceType.Firefox;
|
||||
}
|
||||
|
||||
isChrome(): boolean {
|
||||
return this.getDevice() === DeviceType.Chrome;
|
||||
}
|
||||
|
||||
isEdge(): boolean {
|
||||
return this.getDevice() === DeviceType.Edge;
|
||||
}
|
||||
|
||||
isOpera(): boolean {
|
||||
return this.getDevice() === DeviceType.Opera;
|
||||
}
|
||||
|
||||
isVivaldi(): boolean {
|
||||
return this.getDevice() === DeviceType.Vivaldi;
|
||||
}
|
||||
|
||||
isSafari(): boolean {
|
||||
return this.getDevice() === DeviceType.Safari;
|
||||
}
|
||||
|
||||
isMacAppStore(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
analyticsId(): string {
|
||||
if (this.analyticsIdCache) {
|
||||
return this.analyticsIdCache;
|
||||
}
|
||||
|
||||
this.analyticsIdCache = (AnalyticsIds as any)[this.getDevice()];
|
||||
return this.analyticsIdCache;
|
||||
}
|
||||
|
||||
getDomain(uriString: string): string {
|
||||
return uriString;
|
||||
}
|
||||
|
||||
isViewOpen(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
launchUri(uri: string, options?: any): void {
|
||||
//
|
||||
}
|
||||
|
||||
saveFile(win: Window, blobData: any, blobOptions: any, fileName: string): void {
|
||||
//
|
||||
}
|
||||
|
||||
getApplicationVersion(): string {
|
||||
return '1.2.3';
|
||||
}
|
||||
|
||||
supportsU2f(win: Window): boolean {
|
||||
if (win != null && (win as any).u2f != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.isChrome() || this.isOpera();
|
||||
}
|
||||
|
||||
supportsDuo(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
showToast(type: 'error' | 'success' | 'warning' | 'info', title: string, text: string, global?: any): void {
|
||||
throw new Error('showToast not implemented');
|
||||
}
|
||||
|
||||
showDialog(text: string, title?: string, confirmText?: string, cancelText?: string, type?: string) {
|
||||
const dialogId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
|
||||
this.messagingService.send('showDialog', {
|
||||
text: text,
|
||||
title: title,
|
||||
confirmText: confirmText,
|
||||
cancelText: cancelText,
|
||||
type: type,
|
||||
dialogId: dialogId,
|
||||
});
|
||||
return new Promise<boolean>((resolve) => {
|
||||
this.showDialogResolves.set(dialogId, { resolve: resolve, date: new Date() });
|
||||
});
|
||||
}
|
||||
|
||||
isDev(): boolean {
|
||||
return process.env.ENV === 'development';
|
||||
}
|
||||
|
||||
copyToClipboard(text: string, options?: any): void {
|
||||
const doc = options ? options.doc : window.document;
|
||||
if ((window as any).clipboardData && (window as any).clipboardData.setData) {
|
||||
// IE specific code path to prevent textarea being shown while dialog is visible.
|
||||
(window as any).clipboardData.setData('Text', text);
|
||||
} else if (doc.queryCommandSupported && doc.queryCommandSupported('copy')) {
|
||||
const textarea = doc.createElement('textarea');
|
||||
textarea.textContent = text;
|
||||
// Prevent scrolling to bottom of page in MS Edge.
|
||||
textarea.style.position = 'fixed';
|
||||
doc.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
|
||||
try {
|
||||
// Security exception may be thrown by some browsers.
|
||||
doc.execCommand('copy');
|
||||
} catch (e) {
|
||||
// tslint:disable-next-line
|
||||
console.warn('Copy to clipboard failed.', e);
|
||||
} finally {
|
||||
doc.body.removeChild(textarea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolveDialogPromise(dialogId: number, confirmed: boolean) {
|
||||
if (this.showDialogResolves.has(dialogId)) {
|
||||
const resolveObj = this.showDialogResolves.get(dialogId);
|
||||
resolveObj.resolve(confirmed);
|
||||
this.showDialogResolves.delete(dialogId);
|
||||
}
|
||||
|
||||
// Clean up old promises
|
||||
const deleteIds: number[] = [];
|
||||
this.showDialogResolves.forEach((val, key) => {
|
||||
const age = new Date().getTime() - val.date.getTime();
|
||||
if (age > DialogPromiseExpiration) {
|
||||
deleteIds.push(key);
|
||||
}
|
||||
});
|
||||
deleteIds.forEach((id) => {
|
||||
this.showDialogResolves.delete(id);
|
||||
});
|
||||
}
|
||||
}
|
24
src/services/webStorage.service.ts
Normal file
24
src/services/webStorage.service.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { StorageService } from 'jslib/abstractions/storage.service';
|
||||
|
||||
export class WebStorageService implements StorageService {
|
||||
private store: any = {};
|
||||
|
||||
get<T>(key: string): Promise<T> {
|
||||
const val = this.store[key];
|
||||
if (val == null) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
return Promise.resolve(val as T);
|
||||
}
|
||||
|
||||
save(key: string, obj: any): Promise<any> {
|
||||
this.store[key] = obj;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
remove(key: string): Promise<any> {
|
||||
delete this.store[key];
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
}
|
@ -29,5 +29,6 @@
|
||||
"jslib/src/angular/dummy.module.ts",
|
||||
"jslib/src/services/nodeApi.service.ts",
|
||||
"jslib/src/services/lowdbStorage.service.ts",
|
||||
"jslib/src/misc/nodeUtils.ts",
|
||||
]
|
||||
}
|
||||
|
@ -97,6 +97,7 @@ const plugins = [
|
||||
{ from: './src/browserconfig.xml' },
|
||||
{ from: './src/app-id.json' },
|
||||
{ from: './src/images', to: 'images' },
|
||||
{ from: './src/locales', to: 'locales' },
|
||||
]),
|
||||
extractCss,
|
||||
new webpack.DefinePlugin({
|
||||
|
Loading…
Reference in New Issue
Block a user