diff --git a/README.md b/README.md index d7709f6..0416b41 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ An angular admin web client for managing users and transactions in the CIC netwo This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.2.0. +## Angular CLI + +Run `npm install -g @angular/cli` to install the angular CLI. + ## Development server Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. diff --git a/src/app/_interceptors/error.interceptor.ts b/src/app/_interceptors/error.interceptor.ts index d1aaee9..b1f717a 100644 --- a/src/app/_interceptors/error.interceptor.ts +++ b/src/app/_interceptors/error.interceptor.ts @@ -16,16 +16,13 @@ export class ErrorInterceptor implements HttpInterceptor { intercept(request: HttpRequest, next: HttpHandler): Observable> { return next.handle(request).pipe(catchError((err: HttpErrorResponse) => { - const data = { + this.errorDialogService.openDialog({ message: err.error.message || err.statusText, - reason: err && err.error && err.error.reason ? err.error.reason : '', status: err.status - }; - this.errorDialogService.openDialog(data); + }); if ([401, 403].indexOf(err.status) !== -1) { location.reload(true); } - // const error = err.error.message || err.statusText; return throwError(err); })); } diff --git a/src/app/_services/auth.service.ts b/src/app/_services/auth.service.ts index 0b35b0b..d0e19fa 100644 --- a/src/app/_services/auth.service.ts +++ b/src/app/_services/auth.service.ts @@ -1,10 +1,11 @@ import { Injectable } from '@angular/core'; -import {MutableKeyStore, MutablePgpKeyStore} from '@app/_helpers'; import { hobaParseChallengeHeader } from '@src/assets/js/hoba.js'; import { signChallenge } from '@src/assets/js/hoba-pgp.js'; import {environment} from '@src/environments/environment'; import {LoggingService} from '@app/_services/logging.service'; import {HttpWrapperService} from '@app/_services/http-wrapper.service'; +import {MutableKeyStore, MutablePgpKeyStore} from '@app/_pgp'; +import {ErrorDialogService} from '@app/_services/error-dialog.service'; import {first} from 'rxjs/operators'; @Injectable({ @@ -18,7 +19,8 @@ export class AuthService { constructor( private httpWrapperService: HttpWrapperService, - private loggingService: LoggingService + private loggingService: LoggingService, + private errorDialogService: ErrorDialogService ) { if (sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN'))) { this.sessionToken = sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN')); @@ -29,7 +31,7 @@ export class AuthService { } setState(s): void { - (document.getElementById('state') as HTMLInputElement).value = s; + document.getElementById('state').innerHTML = s; } getWithToken(): void { @@ -44,7 +46,7 @@ export class AuthService { throw new Error('login rejected'); } this.sessionLoginCount++; - this.setState('click to perform login ' + this.sessionLoginCount + ' with token ' + this.sessionToken); + this.setState('Click button to perform login ' + this.sessionLoginCount + ' with token ' + this.sessionToken); return; }); xhr.send(); @@ -64,7 +66,7 @@ export class AuthService { this.sessionToken = xhr.getResponseHeader('Token'); sessionStorage.setItem(btoa('CICADA_SESSION_TOKEN'), this.sessionToken); this.sessionLoginCount++; - this.setState('click to perform login ' + this.sessionLoginCount + ' with token ' + this.sessionToken); + this.setState('Click button to perform login ' + this.sessionLoginCount + ' with token ' + this.sessionToken); return; }); xhr.send(); @@ -95,8 +97,7 @@ export class AuthService { } } else { try { - const o = this.getChallenge(); - return true; + this.getChallenge(); } catch (e) { this.loggingService.sendErrorLevelMessage('Login challenge failed', this, {error: e}); } @@ -113,15 +114,19 @@ export class AuthService { loginView(): void { document.getElementById('one').style.display = 'none'; document.getElementById('two').style.display = 'block'; - this.setState('click to log in with PGP key ' + this.mutableKeyStore.getPrivateKeyId()); + this.setState('Click button to log in with PGP key ' + this.mutableKeyStore.getPrivateKeyId()); } async setKey(privateKeyArmored): Promise { try { await this.mutableKeyStore.importPrivateKey(privateKeyArmored); localStorage.setItem(btoa('CICADA_PRIVATE_KEY'), privateKeyArmored); - } catch (e) { - this.loggingService.sendErrorLevelMessage('Failed setting key', this, {error: e}); + } catch (err) { + this.loggingService.sendErrorLevelMessage('Failed setting key', this, {error: err}); + this.errorDialogService.openDialog({ + message: `Failed to set key, Enter your private key again. Reason: ${err.error.message || err.statusText}`, + status: err.status + }); return false; } this.loginView(); @@ -134,10 +139,10 @@ export class AuthService { } async getPublicKeys(): Promise { - this.httpWrapperService.get(`${environment.publicKeysUrl}/keys.asc`).pipe(first()).subscribe(async res => { + this.httpWrapperService.get(`${environment.publicKeysUrl}`).pipe(first()).subscribe(async res => { await this.mutableKeyStore.importPublicKey(res.body); }, error => { - this.loggingService.sendErrorLevelMessage('There was an error!', this, {error}); + this.loggingService.sendErrorLevelMessage('There was an error fetching public keys!', this, {error}); }); if (this.privateKey !== undefined) { await this.mutableKeyStore.importPrivateKey(this.privateKey); diff --git a/src/app/_services/block-sync.service.ts b/src/app/_services/block-sync.service.ts index f6637a5..cc07386 100644 --- a/src/app/_services/block-sync.service.ts +++ b/src/app/_services/block-sync.service.ts @@ -21,7 +21,8 @@ export class BlockSyncService { private loggingService: LoggingService ) { } - blockSync(): any { + blockSync(address: string = null, offset: number = 0, limit: number = 100): any { + this.transactionService.resetTransactionsList(); const settings = new Settings(this.scan); const provider = environment.web3Provider; const readyStateElements = { network: 2 }; @@ -40,13 +41,13 @@ export class BlockSyncService { }; settings.registry.onload = (addressReturned: number): void => { this.loggingService.sendInfoLevelMessage(`Loaded network contracts ${addressReturned}`); - this.readyStateProcessor(settings, readyStateElements.network); + this.readyStateProcessor(settings, readyStateElements.network, address, offset, limit); }; settings.registry.load(); } - readyStateProcessor(settings, bit): void { + readyStateProcessor(settings: Settings, bit: number, address: string, offset: number, limit: number): void { this.readyState |= bit; if (this.readyStateTarget === this.readyState && this.readyStateTarget) { const wHeadSync = new Worker('./../assets/js/block-sync/head.js'); @@ -56,7 +57,15 @@ export class BlockSyncService { wHeadSync.postMessage({ w3_provider: settings.w3.provider, }); - this.fetcher(settings); + if (address === null) { + this.transactionService.getAllTransactions(offset, limit).pipe(first()).subscribe(res => { + this.fetcher(settings, res.body); + }); + } else { + this.transactionService.getAddressTransactions(address, offset, limit).pipe(first()).subscribe(res => { + this.fetcher(settings, res.body); + }); + } } } @@ -93,17 +102,15 @@ export class BlockSyncService { }); } - fetcher(settings: any, offset: number = 0, limit: number = 100): void { - this.transactionService.getAllTransactions(offset, limit).pipe(first()).subscribe(res => { - const blockFilterBinstr = window.atob(res.body.block_filter); - const bOne = new Uint8Array(blockFilterBinstr.length); - bOne.map((e, i, v) => v[i] = blockFilterBinstr.charCodeAt(i)); + fetcher(settings: Settings, transactionsInfo: any): void { + const blockFilterBinstr = window.atob(transactionsInfo.block_filter); + const bOne = new Uint8Array(blockFilterBinstr.length); + bOne.map((e, i, v) => v[i] = blockFilterBinstr.charCodeAt(i)); - const blocktxFilterBinstr = window.atob(res.body.blocktx_filter); - const bTwo = new Uint8Array(blocktxFilterBinstr.length); - bTwo.map((e, i, v) => v[i] = blocktxFilterBinstr.charCodeAt(i)); + const blocktxFilterBinstr = window.atob(transactionsInfo.blocktx_filter); + const bTwo = new Uint8Array(blocktxFilterBinstr.length); + bTwo.map((e, i, v) => v[i] = blocktxFilterBinstr.charCodeAt(i)); - settings.scanFilter(settings, res.body.low, res.body.high, bOne, bTwo, res.body.filter_rounds); - }); + settings.scanFilter(settings, transactionsInfo.low, transactionsInfo.high, bOne, bTwo, transactionsInfo.filter_rounds); } } diff --git a/src/app/_services/http-wrapper.service.ts b/src/app/_services/http-wrapper.service.ts index 4e17804..4987de9 100644 --- a/src/app/_services/http-wrapper.service.ts +++ b/src/app/_services/http-wrapper.service.ts @@ -41,6 +41,7 @@ export class HttpWrapperService { return Observable.create((observer: any) => { const requestBeginTime = moment(); this.http.request(new HttpRequest(method, url, body, options)).subscribe((response) => { + this.loggingService.sendInfoLevelMessage(response); this.logTime(requestBeginTime, `${url}`, method); observer.next(response); observer.complete(); diff --git a/src/app/_services/index.ts b/src/app/_services/index.ts index a8a1b33..ec50f32 100644 --- a/src/app/_services/index.ts +++ b/src/app/_services/index.ts @@ -6,3 +6,4 @@ export * from '@app/_services/block-sync.service'; export * from '@app/_services/location.service'; export * from '@app/_services/logging.service'; export * from '@app/_services/http-wrapper.service'; +export * from '@app/_services/error-dialog.service'; diff --git a/src/app/_services/token.service.ts b/src/app/_services/token.service.ts index b3e0934..8dc81ce 100644 --- a/src/app/_services/token.service.ts +++ b/src/app/_services/token.service.ts @@ -1,10 +1,11 @@ import { Injectable } from '@angular/core'; import {environment} from '@src/environments/environment'; import {BehaviorSubject, Observable} from 'rxjs'; -import {HttpGetter, Registry, TokenRegistry} from '@app/_helpers'; +import {HttpGetter} from '@app/_helpers'; import {CICRegistry} from 'cic-client'; import Web3 from 'web3'; import {HttpWrapperService} from '@app/_services/http-wrapper.service'; +import {Registry, TokenRegistry} from '@app/_eth'; @Injectable({ providedIn: 'root' diff --git a/src/app/_services/transaction.service.ts b/src/app/_services/transaction.service.ts index 590f979..365c296 100644 --- a/src/app/_services/transaction.service.ts +++ b/src/app/_services/transaction.service.ts @@ -11,10 +11,10 @@ import {Tx} from '@src/assets/js/ethtx/dist'; import {toValue} from '@src/assets/js/ethtx/dist/tx'; import * as secp256k1 from 'secp256k1'; import {AuthService} from '@app/_services/auth.service'; -import {Registry} from '@app/_helpers'; import {defaultAccount} from '@app/_models'; import {LoggingService} from '@app/_services/logging.service'; import {HttpWrapperService} from '@app/_services/http-wrapper.service'; +import {Registry} from '@app/_eth'; const Web3 = require('web3'); const vCard = require('vcard-parser'); @@ -45,22 +45,22 @@ export class TransactionService { } async setTransaction(transaction, cacheSize: number): Promise { + if (this.transactions.find(cachedTx => cachedTx.tx.txHash === transaction.tx.txHash)) { return; } transaction.value = Number(transaction.value); transaction.type = 'transaction'; - if (this.transactions.find(cachedTx => cachedTx.tx.txHash === transaction.tx.txHash)) { return; } try { - this.userService.getAccountDetailsFromMeta(await User.toKey(transaction.from)).pipe(first()).subscribe(async (res) => { + this.userService.getAccountDetailsFromMeta(await User.toKey(transaction.from)).pipe(first()).subscribe((res) => { transaction.sender = this.getAccountInfo(res.body); }, error => { transaction.sender = defaultAccount; }); - this.userService.getAccountDetailsFromMeta(await User.toKey(transaction.to)).pipe(first()).subscribe(async (res) => { + this.userService.getAccountDetailsFromMeta(await User.toKey(transaction.to)).pipe(first()).subscribe((res) => { transaction.recipient = this.getAccountInfo(res.body); }, error => { transaction.recipient = defaultAccount; }); } finally { - await this.addTransaction(transaction, cacheSize); + this.addTransaction(transaction, cacheSize); } } @@ -70,17 +70,17 @@ export class TransactionService { conversion.fromValue = Number(conversion.fromValue); conversion.toValue = Number(conversion.toValue); try { - this.userService.getAccountDetailsFromMeta(await User.toKey(conversion.trader)).pipe(first()).subscribe(async (res) => { + this.userService.getAccountDetailsFromMeta(await User.toKey(conversion.trader)).pipe(first()).subscribe((res) => { conversion.sender = conversion.recipient = this.getAccountInfo(res.body); }, error => { conversion.sender = conversion.recipient = defaultAccount; }); } finally { - await this.addTransaction(conversion, cacheSize); + this.addTransaction(conversion, cacheSize); } } - async addTransaction(transaction, cacheSize: number): Promise { + addTransaction(transaction, cacheSize: number): void { this.transactions.unshift(transaction); if (this.transactions.length > cacheSize) { this.transactions.length = cacheSize; @@ -88,6 +88,11 @@ export class TransactionService { this.transactionList.next(this.transactions); } + resetTransactionsList(): void { + this.transactions = []; + this.transactionList.next(this.transactions); + } + getAccountInfo(account: string): any { let accountInfo = Envelope.fromJSON(JSON.stringify(account)).unwrap().m.data; accountInfo.vcard = vCard.parse(atob(accountInfo.vcard)); diff --git a/src/app/_services/user.service.ts b/src/app/_services/user.service.ts index 3407f9c..a7c6430 100644 --- a/src/app/_services/user.service.ts +++ b/src/app/_services/user.service.ts @@ -3,11 +3,13 @@ import {BehaviorSubject, Observable} from 'rxjs'; import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http'; import {environment} from '@src/environments/environment'; import {first} from 'rxjs/operators'; -import {AccountIndex, MutableKeyStore, MutablePgpKeyStore, PGPSigner, Registry, Signer} from '@app/_helpers'; import {ArgPair, Envelope, Syncable, User} from 'cic-client-meta'; import {MetaResponse} from '@app/_models'; import {LoggingService} from '@app/_services/logging.service'; import {HttpWrapperService} from '@app/_services/http-wrapper.service'; +import {TokenService} from '@app/_services/token.service'; +import {AccountIndex, Registry} from '@app/_eth'; +import {MutableKeyStore, MutablePgpKeyStore, PGPSigner, Signer} from '@app/_pgp'; const vCard = require('vcard-parser'); @Injectable({ @@ -35,7 +37,8 @@ export class UserService { constructor( private http: HttpClient, private httpWrapperService: HttpWrapperService, - private loggingService: LoggingService + private loggingService: LoggingService, + private tokenService: TokenService ) { } @@ -145,7 +148,7 @@ export class UserService { } getAccountDetailsFromMeta(userKey: string): Observable { - return this.httpWrapperService.get(`${environment.cicMetaUrl}/${userKey}`, { headers: this.headers }); + return this.http.get(`${environment.cicMetaUrl}/${userKey}`, { headers: this.headers }); } getUser(userKey: string): any { @@ -169,16 +172,18 @@ export class UserService { }); } - async loadAccounts(limit: number, offset: number = 0): Promise { + async loadAccounts(limit: number = 100, offset: number = 0): Promise { this.resetAccountsList(); const accountIndexAddress = await this.registry.addressOf('AccountRegistry'); const accountIndexQuery = new AccountIndex(accountIndexAddress); const accountAddresses = await accountIndexQuery.last(await accountIndexQuery.totalAccounts()); + this.loggingService.sendInfoLevelMessage(accountAddresses); for (const accountAddress of accountAddresses.slice(offset, offset + limit)) { - this.getAccountDetailsFromMeta(await User.toKey(accountAddress)).pipe(first()).subscribe(res => { + this.getAccountDetailsFromMeta(await User.toKey(accountAddress)).pipe(first()).subscribe(async res => { const account = Envelope.fromJSON(JSON.stringify(res)).unwrap(); this.accountsMeta.push(account); const accountInfo = account.m.data; + accountInfo.balance = await this.tokenService.getTokenBalance(accountInfo.identities.evm['bloxberg:8996'][0]); accountInfo.vcard = vCard.parse(atob(accountInfo.vcard)); this.accounts.unshift(accountInfo); if (this.accounts.length > limit) { diff --git a/src/app/auth/auth.component.html b/src/app/auth/auth.component.html index 99501a2..2355239 100644 --- a/src/app/auth/auth.component.html +++ b/src/app/auth/auth.component.html @@ -17,8 +17,8 @@ Private Key - +
Private Key is required.
@@ -34,23 +34,10 @@