2021-05-10 18:15:25 +02:00
|
|
|
import { Injectable } from '@angular/core';
|
|
|
|
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
|
|
|
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
|
|
|
|
import { environment } from '@src/environments/environment';
|
|
|
|
import { first } from 'rxjs/operators';
|
|
|
|
import { ArgPair, Envelope, Phone, Syncable, User } from 'cic-client-meta';
|
|
|
|
import { AccountDetails } from '@app/_models';
|
|
|
|
import { LoggingService } from '@app/_services/logging.service';
|
|
|
|
import { TokenService } from '@app/_services/token.service';
|
|
|
|
import { AccountIndex } from '@app/_eth';
|
|
|
|
import { MutableKeyStore, PGPSigner, Signer } from '@app/_pgp';
|
|
|
|
import { RegistryService } from '@app/_services/registry.service';
|
|
|
|
import { CICRegistry } from 'cic-client';
|
|
|
|
import { AuthService } from '@app/_services/auth.service';
|
|
|
|
import { personValidation, vcardValidation } from '@app/_helpers';
|
|
|
|
import { add0x } from '@src/assets/js/ethtx/dist/hex';
|
2021-03-02 08:29:14 +01:00
|
|
|
const vCard = require('vcard-parser');
|
2020-11-25 08:51:15 +01:00
|
|
|
|
|
|
|
@Injectable({
|
2021-05-10 18:15:25 +02:00
|
|
|
providedIn: 'root',
|
2020-11-25 08:51:15 +01:00
|
|
|
})
|
|
|
|
export class UserService {
|
2021-05-10 18:15:25 +02:00
|
|
|
headers: HttpHeaders = new HttpHeaders({ 'x-cic-automerge': 'client' });
|
2021-04-25 12:32:23 +02:00
|
|
|
keystore: MutableKeyStore;
|
|
|
|
signer: Signer;
|
2021-04-20 10:28:40 +02:00
|
|
|
registry: CICRegistry;
|
2021-02-17 11:00:38 +01:00
|
|
|
|
2021-04-29 19:10:39 +02:00
|
|
|
accounts: Array<AccountDetails> = [];
|
2021-05-10 18:15:25 +02:00
|
|
|
private accountsList: BehaviorSubject<Array<AccountDetails>> = new BehaviorSubject<
|
|
|
|
Array<AccountDetails>
|
|
|
|
>(this.accounts);
|
2021-04-29 19:10:39 +02:00
|
|
|
accountsSubject: Observable<Array<AccountDetails>> = this.accountsList.asObservable();
|
2020-12-05 07:29:18 +01:00
|
|
|
|
2021-04-29 19:10:39 +02:00
|
|
|
actions: Array<any> = [];
|
|
|
|
private actionsList: BehaviorSubject<any> = new BehaviorSubject<any>(this.actions);
|
|
|
|
actionsSubject: Observable<Array<any>> = this.actionsList.asObservable();
|
2020-11-25 08:51:15 +01:00
|
|
|
|
2021-03-14 09:19:25 +01:00
|
|
|
constructor(
|
2021-03-21 12:02:18 +01:00
|
|
|
private httpClient: HttpClient,
|
2021-03-16 11:08:18 +01:00
|
|
|
private loggingService: LoggingService,
|
2021-04-20 10:28:40 +02:00
|
|
|
private tokenService: TokenService,
|
2021-05-10 18:15:25 +02:00
|
|
|
private authService: AuthService
|
2021-05-19 18:57:10 +02:00
|
|
|
) {}
|
|
|
|
|
|
|
|
async init(): Promise<void> {
|
|
|
|
await this.authService.init();
|
|
|
|
await this.tokenService.init();
|
|
|
|
this.keystore = this.authService.mutableKeyStore;
|
|
|
|
this.signer = new PGPSigner(this.keystore);
|
|
|
|
this.registry = await RegistryService.getRegistry();
|
2021-03-06 07:28:29 +01:00
|
|
|
}
|
2020-11-25 08:51:15 +01:00
|
|
|
|
2021-02-08 12:47:07 +01:00
|
|
|
resetPin(phone: string): Observable<any> {
|
2021-04-29 19:10:39 +02:00
|
|
|
const params: HttpParams = new HttpParams().set('phoneNumber', phone);
|
2021-05-26 09:26:11 +02:00
|
|
|
return this.httpClient.put(`${environment.cicUssdUrl}/pin`, { params });
|
2021-03-14 09:19:25 +01:00
|
|
|
}
|
|
|
|
|
2021-04-29 19:10:39 +02:00
|
|
|
getAccountStatus(phone: string): Observable<any> {
|
|
|
|
const params: HttpParams = new HttpParams().set('phoneNumber', phone);
|
2021-05-10 18:15:25 +02:00
|
|
|
return this.httpClient.get(`${environment.cicUssdUrl}/pin`, { params });
|
2021-03-14 09:19:25 +01:00
|
|
|
}
|
|
|
|
|
2021-04-29 19:10:39 +02:00
|
|
|
getLockedAccounts(offset: number, limit: number): Observable<any> {
|
2021-03-21 12:02:18 +01:00
|
|
|
return this.httpClient.get(`${environment.cicUssdUrl}/accounts/locked/${offset}/${limit}`);
|
2021-03-14 09:19:25 +01:00
|
|
|
}
|
|
|
|
|
2021-05-10 18:15:25 +02:00
|
|
|
async changeAccountInfo(
|
|
|
|
address: string,
|
|
|
|
name: string,
|
|
|
|
phoneNumber: string,
|
|
|
|
age: string,
|
|
|
|
type: string,
|
|
|
|
bio: string,
|
|
|
|
gender: string,
|
|
|
|
businessCategory: string,
|
|
|
|
userLocation: string,
|
|
|
|
location: string,
|
|
|
|
locationType: string
|
2021-03-14 09:19:25 +01:00
|
|
|
): Promise<any> {
|
2021-04-29 19:10:39 +02:00
|
|
|
const accountInfo: any = {
|
2021-04-25 12:32:23 +02:00
|
|
|
vcard: {
|
|
|
|
fn: [{}],
|
|
|
|
n: [{}],
|
2021-05-10 18:15:25 +02:00
|
|
|
tel: [{}],
|
2021-04-25 12:32:23 +02:00
|
|
|
},
|
2021-05-10 18:15:25 +02:00
|
|
|
location: {},
|
2021-04-25 12:32:23 +02:00
|
|
|
};
|
2021-03-14 09:19:25 +01:00
|
|
|
accountInfo.vcard.fn[0].value = name;
|
|
|
|
accountInfo.vcard.n[0].value = name.split(' ');
|
|
|
|
accountInfo.vcard.tel[0].value = phoneNumber;
|
|
|
|
accountInfo.products = [bio];
|
|
|
|
accountInfo.gender = gender;
|
|
|
|
accountInfo.age = age;
|
|
|
|
accountInfo.type = type;
|
|
|
|
accountInfo.category = businessCategory;
|
|
|
|
accountInfo.location.area = location;
|
|
|
|
accountInfo.location.area_name = userLocation;
|
|
|
|
accountInfo.location.area_type = locationType;
|
2021-04-27 16:10:50 +02:00
|
|
|
await vcardValidation(accountInfo.vcard);
|
2021-04-25 12:32:23 +02:00
|
|
|
accountInfo.vcard = btoa(vCard.generate(accountInfo.vcard));
|
2021-04-29 19:10:39 +02:00
|
|
|
const accountKey: string = await User.toKey(address);
|
2021-05-10 18:15:25 +02:00
|
|
|
this.getAccountDetailsFromMeta(accountKey)
|
|
|
|
.pipe(first())
|
|
|
|
.subscribe(
|
|
|
|
async (res) => {
|
|
|
|
const syncableAccount: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
|
|
|
|
const update: Array<ArgPair> = [];
|
|
|
|
for (const prop of Object.keys(accountInfo)) {
|
|
|
|
update.push(new ArgPair(prop, accountInfo[prop]));
|
|
|
|
}
|
|
|
|
syncableAccount.update(update, 'client-branch');
|
|
|
|
await personValidation(syncableAccount.m.data);
|
|
|
|
await this.updateMeta(syncableAccount, accountKey, this.headers);
|
|
|
|
},
|
|
|
|
async (error) => {
|
|
|
|
this.loggingService.sendErrorLevelMessage(
|
|
|
|
'Cannot find account info in meta service',
|
|
|
|
this,
|
|
|
|
{ error }
|
|
|
|
);
|
|
|
|
const syncableAccount: Syncable = new Syncable(accountKey, accountInfo);
|
|
|
|
await this.updateMeta(syncableAccount, accountKey, this.headers);
|
|
|
|
}
|
|
|
|
);
|
2021-02-17 19:22:06 +01:00
|
|
|
return accountKey;
|
2021-02-17 11:00:38 +01:00
|
|
|
}
|
|
|
|
|
2021-05-10 18:15:25 +02:00
|
|
|
async updateMeta(
|
|
|
|
syncableAccount: Syncable,
|
|
|
|
accountKey: string,
|
|
|
|
headers: HttpHeaders
|
|
|
|
): Promise<any> {
|
|
|
|
const envelope: Envelope = await this.wrap(syncableAccount, this.signer);
|
2021-04-29 19:10:39 +02:00
|
|
|
const reqBody: string = envelope.toJSON();
|
2021-05-10 18:15:25 +02:00
|
|
|
this.httpClient
|
|
|
|
.put(`${environment.cicMetaUrl}/${accountKey}`, reqBody, { headers })
|
|
|
|
.pipe(first())
|
|
|
|
.subscribe((res) => {
|
|
|
|
this.loggingService.sendInfoLevelMessage(`Response: ${res}`);
|
|
|
|
});
|
2021-02-08 12:47:07 +01:00
|
|
|
}
|
|
|
|
|
2020-12-05 07:29:18 +01:00
|
|
|
getActions(): void {
|
2021-05-10 18:15:25 +02:00
|
|
|
this.httpClient
|
|
|
|
.get(`${environment.cicCacheUrl}/actions`)
|
|
|
|
.pipe(first())
|
|
|
|
.subscribe((res) => this.actionsList.next(res));
|
2020-11-25 08:51:15 +01:00
|
|
|
}
|
|
|
|
|
2021-04-29 19:10:39 +02:00
|
|
|
getActionById(id: string): Observable<any> {
|
2021-03-21 12:02:18 +01:00
|
|
|
return this.httpClient.get(`${environment.cicCacheUrl}/actions/${id}`);
|
2020-12-05 07:29:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
approveAction(id: string): Observable<any> {
|
2021-03-21 12:02:18 +01:00
|
|
|
return this.httpClient.post(`${environment.cicCacheUrl}/actions/${id}`, { approval: true });
|
2020-12-05 07:29:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
revokeAction(id: string): Observable<any> {
|
2021-03-21 12:02:18 +01:00
|
|
|
return this.httpClient.post(`${environment.cicCacheUrl}/actions/${id}`, { approval: false });
|
2020-12-05 07:29:18 +01:00
|
|
|
}
|
|
|
|
|
2021-02-23 13:32:02 +01:00
|
|
|
getAccountDetailsFromMeta(userKey: string): Observable<any> {
|
2021-03-21 12:02:18 +01:00
|
|
|
return this.httpClient.get(`${environment.cicMetaUrl}/${userKey}`, { headers: this.headers });
|
2021-02-23 13:32:02 +01:00
|
|
|
}
|
|
|
|
|
2021-02-17 11:00:38 +01:00
|
|
|
wrap(syncable: Syncable, signer: Signer): Promise<Envelope> {
|
2021-04-29 19:10:39 +02:00
|
|
|
return new Promise<Envelope>(async (resolve, reject) => {
|
2021-02-17 11:00:38 +01:00
|
|
|
syncable.setSigner(signer);
|
|
|
|
syncable.onwrap = async (env) => {
|
|
|
|
if (env === undefined) {
|
2021-04-29 19:10:39 +02:00
|
|
|
reject();
|
2021-02-17 11:00:38 +01:00
|
|
|
return;
|
|
|
|
}
|
2021-04-29 19:10:39 +02:00
|
|
|
resolve(env);
|
2021-02-17 11:00:38 +01:00
|
|
|
};
|
2021-02-17 19:22:06 +01:00
|
|
|
await syncable.sign();
|
2021-02-17 11:00:38 +01:00
|
|
|
});
|
|
|
|
}
|
2021-03-01 09:12:49 +01:00
|
|
|
|
2021-03-16 11:08:18 +01:00
|
|
|
async loadAccounts(limit: number = 100, offset: number = 0): Promise<void> {
|
2021-03-10 10:47:01 +01:00
|
|
|
this.resetAccountsList();
|
2021-06-04 14:16:57 +02:00
|
|
|
const accountIndexAddress: string = await this.registry.getContractAddressByName(
|
|
|
|
'AccountRegistry'
|
|
|
|
);
|
|
|
|
const accountIndexQuery = new AccountIndex(accountIndexAddress);
|
|
|
|
const accountAddresses: Array<string> = await accountIndexQuery.last(limit);
|
|
|
|
this.loggingService.sendInfoLevelMessage(accountAddresses);
|
|
|
|
for (const accountAddress of accountAddresses.slice(offset, offset + limit)) {
|
|
|
|
await this.getAccountByAddress(accountAddress, limit);
|
|
|
|
}
|
2021-03-01 09:12:49 +01:00
|
|
|
}
|
2021-03-10 10:47:01 +01:00
|
|
|
|
2021-05-10 18:15:25 +02:00
|
|
|
async getAccountByAddress(
|
|
|
|
accountAddress: string,
|
|
|
|
limit: number = 100
|
|
|
|
): Promise<Observable<AccountDetails>> {
|
|
|
|
const accountSubject: Subject<any> = new Subject<any>();
|
|
|
|
this.getAccountDetailsFromMeta(await User.toKey(add0x(accountAddress)))
|
|
|
|
.pipe(first())
|
|
|
|
.subscribe(async (res) => {
|
|
|
|
const account: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
|
2021-06-04 14:16:57 +02:00
|
|
|
const accountInfo = account.m.data;
|
2021-05-10 18:15:25 +02:00
|
|
|
await personValidation(accountInfo);
|
2021-05-18 12:24:41 +02:00
|
|
|
this.tokenService.onload = async (status: boolean): Promise<void> => {
|
|
|
|
accountInfo.balance = await this.tokenService.getTokenBalance(
|
|
|
|
accountInfo.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]
|
|
|
|
);
|
|
|
|
};
|
2021-05-10 18:15:25 +02:00
|
|
|
accountInfo.vcard = vCard.parse(atob(accountInfo.vcard));
|
|
|
|
await vcardValidation(accountInfo.vcard);
|
2021-05-19 09:56:39 +02:00
|
|
|
this.addAccount(accountInfo, limit);
|
2021-05-10 18:15:25 +02:00
|
|
|
accountSubject.next(accountInfo);
|
|
|
|
});
|
2021-04-08 14:16:00 +02:00
|
|
|
return accountSubject.asObservable();
|
|
|
|
}
|
|
|
|
|
2021-05-10 18:15:25 +02:00
|
|
|
async getAccountByPhone(
|
|
|
|
phoneNumber: string,
|
|
|
|
limit: number = 100
|
|
|
|
): Promise<Observable<AccountDetails>> {
|
|
|
|
const accountSubject: Subject<any> = new Subject<any>();
|
|
|
|
this.getAccountDetailsFromMeta(await Phone.toKey(phoneNumber))
|
|
|
|
.pipe(first())
|
|
|
|
.subscribe(async (res) => {
|
|
|
|
const response: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
|
|
|
|
const address: string = response.m.data;
|
|
|
|
const account: Observable<AccountDetails> = await this.getAccountByAddress(address, limit);
|
|
|
|
account.subscribe((result) => {
|
|
|
|
accountSubject.next(result);
|
|
|
|
});
|
2021-04-24 21:21:15 +02:00
|
|
|
});
|
|
|
|
return accountSubject.asObservable();
|
2021-04-08 14:16:00 +02:00
|
|
|
}
|
|
|
|
|
2021-03-10 10:47:01 +01:00
|
|
|
resetAccountsList(): void {
|
|
|
|
this.accounts = [];
|
|
|
|
this.accountsList.next(this.accounts);
|
|
|
|
}
|
2021-04-07 11:24:32 +02:00
|
|
|
|
2021-05-10 18:15:25 +02:00
|
|
|
searchAccountByName(name: string): any {
|
|
|
|
return;
|
|
|
|
}
|
2021-04-28 14:53:25 +02:00
|
|
|
|
|
|
|
getCategories(): Observable<any> {
|
2021-04-28 16:31:36 +02:00
|
|
|
return this.httpClient.get(`${environment.cicMetaUrl}/categories`);
|
2021-04-28 14:53:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getCategoryByProduct(product: string): Observable<any> {
|
2021-04-28 16:31:36 +02:00
|
|
|
return this.httpClient.get(`${environment.cicMetaUrl}/categories/${product.toLowerCase()}`);
|
2021-04-28 14:53:25 +02:00
|
|
|
}
|
2021-04-28 19:24:25 +02:00
|
|
|
|
|
|
|
getAccountTypes(): Observable<any> {
|
|
|
|
return this.httpClient.get(`${environment.cicMetaUrl}/accounttypes`);
|
|
|
|
}
|
|
|
|
|
|
|
|
getTransactionTypes(): Observable<any> {
|
|
|
|
return this.httpClient.get(`${environment.cicMetaUrl}/transactiontypes`);
|
|
|
|
}
|
|
|
|
|
|
|
|
getGenders(): Observable<any> {
|
|
|
|
return this.httpClient.get(`${environment.cicMetaUrl}/genders`);
|
|
|
|
}
|
2021-05-19 09:56:39 +02:00
|
|
|
|
|
|
|
addAccount(account: AccountDetails, cacheSize: number): void {
|
|
|
|
const savedIndex = this.accounts.findIndex(
|
|
|
|
(acc) =>
|
|
|
|
acc.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0] ===
|
|
|
|
account.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]
|
|
|
|
);
|
|
|
|
if (savedIndex === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (savedIndex > 0) {
|
|
|
|
this.accounts.splice(savedIndex, 1);
|
|
|
|
}
|
|
|
|
this.accounts.unshift(account);
|
|
|
|
if (this.accounts.length > cacheSize) {
|
|
|
|
this.accounts.length = Math.min(this.accounts.length, cacheSize);
|
|
|
|
}
|
|
|
|
this.accountsList.next(this.accounts);
|
|
|
|
}
|
2020-11-25 08:51:15 +01:00
|
|
|
}
|