910 lines
33 KiB
TypeScript
910 lines
33 KiB
TypeScript
import { createUserReturn, User, Process, createTransactionReturn, outputs_list, FaucetMessage, AnkFlag, NewTxMessage, CipherMessage, CachedMessage } from '../dist/pkg/sdk_client';
|
|
import IndexedDB from './database'
|
|
import { WebSocketClient } from './websockets';
|
|
|
|
class Services {
|
|
private static instance: Services;
|
|
private sdkClient: any;
|
|
private current_process: string | null = null;
|
|
private websocketConnection: WebSocketClient[] = [];
|
|
private sp_address: string | null = null;
|
|
|
|
// Private constructor to prevent direct instantiation from outside
|
|
private constructor() {}
|
|
|
|
// Method to access the singleton instance of Services
|
|
public static async getInstance(): Promise<Services> {
|
|
if (!Services.instance) {
|
|
Services.instance = new Services();
|
|
await Services.instance.init();
|
|
}
|
|
return Services.instance;
|
|
}
|
|
|
|
// The init method is now part of the instance, and should only be called once
|
|
private async init(): Promise<void> {
|
|
this.sdkClient = await import("../dist/pkg/sdk_client");
|
|
this.sdkClient.setup();
|
|
await this.updateProcesses();
|
|
}
|
|
|
|
public async addWebsocketConnection(url: string): Promise<void> {
|
|
const services = await Services.getInstance();
|
|
const newClient = new WebSocketClient(url, services);
|
|
if (!services.websocketConnection.includes(newClient)) {
|
|
services.websocketConnection.push(newClient);
|
|
}
|
|
}
|
|
|
|
public async isNewUser(): Promise<boolean> {
|
|
let isNew = false;
|
|
try {
|
|
const indexedDB = await IndexedDB.getInstance();
|
|
const db = await indexedDB.getDb();
|
|
let userListObject = await indexedDB.getAll<User>(db, indexedDB.getStoreList().AnkUser);
|
|
if (userListObject.length == 0) {
|
|
isNew = true;
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to retrieve isNewUser :", error);
|
|
}
|
|
return isNew;
|
|
}
|
|
|
|
public async displayCreateId(): Promise<void> {
|
|
const services = await Services.getInstance();
|
|
await services.createIdInjectHtml();
|
|
services.attachSubmitListener("form4nk", (event) => services.createId(event));
|
|
services.attachClickListener("displayrecover", services.displayRecover);
|
|
await services.displayProcess();
|
|
}
|
|
|
|
public async displaySendMessage(): Promise<void> {
|
|
const services = await Services.getInstance();
|
|
await services.injectHtml('Messaging');
|
|
services.attachSubmitListener("form4nk", (event) => services.sendMessage(event));
|
|
// const ourAddress = document.getElementById('our_address');
|
|
// if (ourAddress) {
|
|
// ourAddress.innerHTML = `<strong>Our Address:</strong> ${this.sp_address}`
|
|
// }
|
|
// services.attachClickListener("displaysendmessage", services.displaySendMessage);
|
|
// await services.displayProcess();
|
|
}
|
|
|
|
public async sendMessage(event: Event): Promise<void> {
|
|
event.preventDefault();
|
|
|
|
const services = await Services.getInstance();
|
|
let availableAmt: number = 0;
|
|
|
|
// check available amount
|
|
try {
|
|
availableAmt = await services.sdkClient.get_available_amount_for_user(true);
|
|
} catch (error) {
|
|
console.error('Failed to get available amount');
|
|
return;
|
|
}
|
|
|
|
if (availableAmt < 2000) {
|
|
try {
|
|
await services.obtainTokenWithFaucet();
|
|
} catch (error) {
|
|
console.error('Failed to obtain faucet token:', error);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const spAddressElement = document.getElementById("sp_address") as HTMLInputElement;
|
|
const messageElement = document.getElementById("message") as HTMLInputElement;
|
|
|
|
if (!spAddressElement || !messageElement) {
|
|
console.error("One or more elements not found");
|
|
return;
|
|
}
|
|
|
|
const recipientSpAddress = spAddressElement.value;
|
|
const message = messageElement.value;
|
|
|
|
const msg_payload: CipherMessage = {sender: this.sp_address!, message: message, error: null};
|
|
|
|
let notificationInfo = await services.notify_address_for_message(recipientSpAddress, msg_payload);
|
|
if (notificationInfo) {
|
|
let networkMsg = notificationInfo.new_network_msg;
|
|
console.debug(networkMsg);
|
|
|
|
const connection = await services.pickWebsocketConnectionRandom();
|
|
const flag: AnkFlag = 'Cipher';
|
|
try {
|
|
// send message (transaction in envelope)
|
|
await services.updateMessages(networkMsg);
|
|
connection?.sendMessage(flag, networkMsg.ciphertext!);
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
// add peers list
|
|
// add processes list
|
|
}
|
|
}
|
|
|
|
public async createId(event: Event): Promise<void> {
|
|
event.preventDefault();
|
|
|
|
// verify we don't already have an user
|
|
const services = await Services.getInstance();
|
|
try {
|
|
let user = await services.getUserInfo();
|
|
if (user) {
|
|
console.error("User already exists, please recover");
|
|
return;
|
|
}
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
|
|
const passwordElement = document.getElementById("password") as HTMLInputElement;
|
|
const processElement = document.getElementById("selectProcess") as HTMLSelectElement;
|
|
|
|
if (!passwordElement || !processElement) {
|
|
throw 'One or more elements not found';
|
|
}
|
|
|
|
const password = passwordElement.value;
|
|
this.current_process = processElement.value;
|
|
// console.log("JS password: " + password + " process: " + this.current_process);
|
|
// To comment if test
|
|
// if (!Services.instance.isPasswordValid(password)) return;
|
|
|
|
const label = null;
|
|
const birthday_signet = 50000;
|
|
const birthday_main = 500000;
|
|
|
|
let createUserReturn: createUserReturn;
|
|
try {
|
|
createUserReturn = services.sdkClient.create_user(password, label, birthday_main, birthday_signet, this.current_process);
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
|
|
let user = createUserReturn.user;
|
|
|
|
// const shares = user.shares;
|
|
// send the shares on the network
|
|
const revokeData = user.revoke_data;
|
|
if (!revokeData) {
|
|
throw 'Failed to get revoke data from wasm';
|
|
}
|
|
|
|
// user.shares = [];
|
|
user.revoke_data = null;
|
|
|
|
try {
|
|
const indexedDb = await IndexedDB.getInstance();
|
|
const db = await indexedDb.getDb();
|
|
await indexedDb.writeObject(db, indexedDb.getStoreList().AnkUser, user, null);
|
|
} catch (error) {
|
|
throw `Failed to write user object: ${error}`;
|
|
}
|
|
|
|
try {
|
|
await services.obtainTokenWithFaucet();
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
|
|
await services.displayRevokeImage(new Uint8Array(revokeData));
|
|
}
|
|
|
|
public async displayRecover(): Promise<void> {
|
|
const services = await Services.getInstance();
|
|
await services.recoverInjectHtml();
|
|
services.attachSubmitListener("form4nk", (event) => services.recover(event));
|
|
services.attachClickListener("displaycreateid", services.displayCreateId);
|
|
services.attachClickListener("displayrevoke", services.displayRevoke);
|
|
services.attachClickListener("submitButtonRevoke", services.revoke);
|
|
await services.displayProcess();
|
|
}
|
|
|
|
public async recover(event: Event) {
|
|
event.preventDefault();
|
|
|
|
const passwordElement = document.getElementById("password") as HTMLInputElement;
|
|
const processElement = document.getElementById("selectProcess") as HTMLSelectElement;
|
|
|
|
if (!passwordElement || !processElement) {
|
|
console.error("One or more elements not found");
|
|
return;
|
|
}
|
|
|
|
const password = passwordElement.value;
|
|
const process = processElement.value;
|
|
// console.log("JS password: " + password + " process: " + process);
|
|
// To comment if test
|
|
// if (!Services.instance.isPasswordValid(password)) return;
|
|
|
|
// Get user in db
|
|
const services = await Services.getInstance();
|
|
try {
|
|
const user = await services.getUserInfo();
|
|
if (user) {
|
|
services.sdkClient.login_user(password, user.pre_id, user.recover_data, user.shares, user.outputs);
|
|
this.sp_address = services.sdkClient.get_recover_address();
|
|
if (this.sp_address) {
|
|
console.info('Using sp_address:', this.sp_address);
|
|
await services.obtainTokenWithFaucet();
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
|
|
console.info(this.sp_address);
|
|
|
|
// TODO: check blocks since last_scan and update outputs
|
|
|
|
await services.displaySendMessage();
|
|
}
|
|
|
|
public async displayRevokeImage(revokeData: Uint8Array): Promise<void> {
|
|
const services = await Services.getInstance();
|
|
await services.revokeImageInjectHtml();
|
|
services.attachClickListener("displayupdateanid", services.displayUpdateAnId);
|
|
|
|
let imageBytes = await services.getRecoverImage('assets/4nk_revoke.jpg');
|
|
if (imageBytes != null) {
|
|
var elem = document.getElementById("revoke") as HTMLAnchorElement;
|
|
if (elem != null) {
|
|
let imageWithData = services.sdkClient.add_data_to_image(imageBytes, revokeData, true);
|
|
const blob = new Blob([imageWithData], { type: 'image/jpeg' });
|
|
const url = URL.createObjectURL(blob);
|
|
|
|
// Set the href attribute for download
|
|
elem.href = url;
|
|
elem.download = 'revoke_4NK.jpg';
|
|
}
|
|
}
|
|
}
|
|
|
|
private async getRecoverImage(imageUrl:string): Promise<Uint8Array|null> {
|
|
let imageBytes = null;
|
|
try {
|
|
const response = await fetch(imageUrl);
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch image: ${response.status} ${response.statusText}`);
|
|
}
|
|
const arrayBuffer = await response.arrayBuffer();
|
|
imageBytes = new Uint8Array(arrayBuffer);
|
|
} catch (error) {
|
|
console.error("Failed to get image : "+imageUrl, error);
|
|
}
|
|
return imageBytes;
|
|
}
|
|
|
|
public async displayRevoke(): Promise<void> {
|
|
const services = await Services.getInstance();
|
|
await services.revokeInjectHtml();
|
|
services.attachClickListener("displayrecover", Services.instance.displayRecover);
|
|
services.attachSubmitListener("form4nk", Services.instance.revoke);
|
|
}
|
|
|
|
public async revoke(event: Event): Promise<void> {
|
|
event.preventDefault();
|
|
console.log("JS revoke click ");
|
|
// TODO
|
|
alert("revoke click to do ...");
|
|
}
|
|
|
|
public async displayUpdateAnId() {
|
|
const services = await Services.getInstance();
|
|
|
|
await services.updateIdInjectHtml();
|
|
|
|
services.attachSubmitListener("form4nk", services.updateAnId);
|
|
}
|
|
|
|
public async parseNetworkMessage(raw: string, feeRate: number): Promise<CachedMessage> {
|
|
const services = await Services.getInstance();
|
|
try {
|
|
const msg: CachedMessage = services.sdkClient.parse_network_msg(raw, feeRate);
|
|
return msg;
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public async updateAnId(event: Event): Promise<void> {
|
|
event.preventDefault();
|
|
|
|
// TODO get values
|
|
const firstNameElement = 'firstName';
|
|
const lastNameElement = 'lastName';
|
|
const firstName = document.getElementById(firstNameElement) as HTMLInputElement;
|
|
const lastName = document.getElementById(lastNameElement) as HTMLInputElement;
|
|
|
|
console.log("JS updateAnId submit ");
|
|
// TODO
|
|
alert("updateAnId submit to do ... Name : "+firstName.value + " "+lastName.value);
|
|
|
|
// TODO Mock add user member to process
|
|
}
|
|
|
|
public async displayProcess(): Promise<void> {
|
|
const services = await Services.getInstance();
|
|
const processList = await services.getAllProcess();
|
|
const selectProcess = document.getElementById("selectProcess");
|
|
if (selectProcess) {
|
|
processList.forEach((process) => {
|
|
let child = new Option(process.name, process.name);
|
|
if (!selectProcess.contains(child)) {
|
|
selectProcess.appendChild(child);
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
public async addProcess(process: Process): Promise<void> {
|
|
try {
|
|
const indexedDB = await IndexedDB.getInstance();
|
|
const db = await indexedDB.getDb();
|
|
await indexedDB.writeObject(db, indexedDB.getStoreList().AnkProcess, process, null);
|
|
} catch (error) {
|
|
console.log('addProcess failed: ',error);
|
|
}
|
|
}
|
|
|
|
public async getAllProcess(): Promise<Process[]> {
|
|
try {
|
|
const indexedDB = await IndexedDB.getInstance();
|
|
const db = await indexedDB.getDb();
|
|
let processListObject = await indexedDB.getAll<Process>(db, indexedDB.getStoreList().AnkProcess);
|
|
return processListObject;
|
|
} catch (error) {
|
|
console.log('getAllProcess failed: ',error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
public async updateOwnedOutputsForUser(): Promise<void> {
|
|
const services = await Services.getInstance();
|
|
let latest_outputs: outputs_list;
|
|
try {
|
|
latest_outputs = services.sdkClient.get_outpoints_for_user();
|
|
} catch (error) {
|
|
console.error(error);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
let user = await services.getUserInfo();
|
|
if (user) {
|
|
user.outputs = latest_outputs;
|
|
// console.warn(user);
|
|
await services.updateUser(user);
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
|
|
public async getAllProcessForUser(pre_id: string): Promise<Process[]> {
|
|
const services = await Services.getInstance();
|
|
let user: User;
|
|
let userProcessList: Process[] = [];
|
|
try {
|
|
const indexedDB = await IndexedDB.getInstance();
|
|
const db = await indexedDB.getDb();
|
|
user = await indexedDB.getObject<User>(db, indexedDB.getStoreList().AnkUser, pre_id);
|
|
} catch (error) {
|
|
console.error('getAllUserProcess failed: ',error);
|
|
return [];
|
|
}
|
|
|
|
try {
|
|
const processListObject = await services.getAllProcess();
|
|
processListObject.forEach(async (processObject) => {
|
|
if (processObject.members.includes(user.pre_id)) {
|
|
userProcessList.push(processObject);
|
|
}
|
|
})
|
|
} catch (error) {
|
|
console.error('getAllUserProcess failed: ',error);
|
|
return [];
|
|
}
|
|
return userProcessList;
|
|
}
|
|
|
|
public async getProcessByName(name: string): Promise<Process | null> {
|
|
console.log('getProcessByName name: '+name);
|
|
const indexedDB = await IndexedDB.getInstance();
|
|
const db = await indexedDB.getDb();
|
|
const process = await indexedDB.getFirstMatchWithIndex<Process>(db, indexedDB.getStoreList().AnkProcess, 'by_name', name);
|
|
console.log('getProcessByName process: '+process);
|
|
|
|
return process;
|
|
}
|
|
|
|
public async updateMessages(message: CachedMessage): Promise<void> {
|
|
const indexedDb = await IndexedDB.getInstance();
|
|
const db = await indexedDb.getDb();
|
|
|
|
try {
|
|
await indexedDb.setObject(db, indexedDb.getStoreList().AnkMessages, message, null);
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public async removeMessage(id: number): Promise<void> {
|
|
const indexedDb = await IndexedDB.getInstance();
|
|
const db = await indexedDb.getDb();
|
|
|
|
try {
|
|
await indexedDb.rmObject(db, indexedDb.getStoreList().AnkMessages, id);
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public async updateProcesses(): Promise<void> {
|
|
const services = await Services.getInstance();
|
|
const processList: Process[] = services.sdkClient.get_processes();
|
|
|
|
processList.forEach(async (process: Process) => {
|
|
const indexedDB = await IndexedDB.getInstance();
|
|
const db = await indexedDB.getDb();
|
|
try {
|
|
const processStore = await indexedDB.getObject<Process>(db, indexedDB.getStoreList().AnkProcess, process.id);
|
|
if (!processStore) {
|
|
await indexedDB.writeObject(db, indexedDB.getStoreList().AnkProcess, process, null);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error while writing process', process.name, 'to indexedDB:', error);
|
|
}
|
|
})
|
|
}
|
|
|
|
public attachClickListener(elementId: string, callback: (event: Event) => void): void {
|
|
const element = document.getElementById(elementId);
|
|
element?.removeEventListener("click", callback);
|
|
element?.addEventListener("click", callback);
|
|
}
|
|
|
|
public attachSubmitListener(elementId: string, callback: (event: Event) => void): void {
|
|
const element = document.getElementById(elementId);
|
|
element?.removeEventListener("submit", callback);
|
|
element?.addEventListener("submit", callback);
|
|
}
|
|
public async revokeInjectHtml() {
|
|
const container = document.getElementById('containerId');
|
|
|
|
if (!container) {
|
|
console.error("No html container");
|
|
return;
|
|
}
|
|
|
|
container.innerHTML =
|
|
` <div class='card'>
|
|
<div class='side-by-side'>
|
|
<h3>Revoke an Id</h3>
|
|
<div>
|
|
<a href='#' id='displayrecover'>Recover</a>
|
|
</div>
|
|
</div>
|
|
<form id='form4nk' action='#'>
|
|
<label for='password'>Password :</label>
|
|
<input type='password' id='password' />
|
|
<hr/>
|
|
<div class='image-container'>
|
|
<label class='image-label'>Revoke image</label>
|
|
<img src='assets/revoke.jpeg' alt='' />
|
|
</div>
|
|
<hr/>
|
|
<button type='submit' id='submitButton' class='recover bg-primary'>Revoke</button>
|
|
</form>
|
|
</div>
|
|
`;
|
|
}
|
|
public async revokeImageInjectHtml() {
|
|
const container = document.getElementById('containerId');
|
|
|
|
if (!container) {
|
|
console.error("No html container");
|
|
return;
|
|
}
|
|
|
|
container.innerHTML =
|
|
`<div class='card'>
|
|
<div class='side-by-side'>
|
|
<h3>Revoke image</h3>
|
|
<div><a href='#' id='displayupdateanid'>Update an Id</a></div>
|
|
</div>
|
|
</div>
|
|
<div class='card-revoke'>
|
|
<a href='#' download='revoke_4NK.jpg' id='revoke'>
|
|
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'>
|
|
<path
|
|
d='M246.6 9.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 109.3V320c0 17.7 14.3 32 32 32s32-14.3 32-32V109.3l73.4 73.4c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-128-128zM64 352c0-17.7-14.3-32-32-32s-32 14.3-32 32v64c0 53 43 96 96 96H352c53 0 96-43 96-96V352c0-17.7-14.3-32-32-32s-32 14.3-32 32v64c0 17.7-14.3 32-32 32H96c-17.7 0-32-14.3-32-32V352z'
|
|
/>
|
|
</svg>
|
|
</a>
|
|
<div class='image-container'>
|
|
<img src='assets/4nk_revoke.jpg' alt='' />
|
|
</div>
|
|
</div>`;
|
|
}
|
|
|
|
public async recoverInjectHtml() {
|
|
const container = document.getElementById('containerId');
|
|
|
|
if (!container) {
|
|
console.error("No html container");
|
|
return;
|
|
}
|
|
|
|
const services = await Services.getInstance();
|
|
await services.updateProcesses();
|
|
|
|
container.innerHTML =
|
|
`<div class='card'>
|
|
<div class='side-by-side'>
|
|
<h3>Recover my Id</h3>
|
|
<div><a href='#'>Processes</a></div>
|
|
</div>
|
|
<form id='form4nk' action='#'>
|
|
<label for='password'>Password :</label>
|
|
<input type='password' id='password' />
|
|
<input type='hidden' id='currentpage' value='recover' />
|
|
<select id='selectProcess' class='custom-select'></select><hr/>
|
|
<div class='side-by-side'>
|
|
<button type='submit' id='submitButton' class='recover bg-primary'>Recover</button>
|
|
<div>
|
|
<a href='#' id='displaycreateid'>Create an Id</a>
|
|
</div>
|
|
</div><hr/>
|
|
<a href='#' id='displayrevoke' class='btn'>Revoke</a>
|
|
</form><br/>
|
|
<div id='passwordalert' class='passwordalert'></div>
|
|
</div>`;
|
|
}
|
|
|
|
public async createIdInjectHtml() {
|
|
const container = document.getElementById('containerId');
|
|
|
|
if (!container) {
|
|
console.error("No html container");
|
|
return;
|
|
}
|
|
|
|
container.innerHTML =
|
|
`<div class='card'>
|
|
<div class='side-by-side'>
|
|
<h3>Create an Id</h3>
|
|
<div><a href='#'>Processes</a></div>
|
|
</div>
|
|
<form id='form4nk' action='#'>
|
|
<label for='password'>Password :</label>
|
|
<input type='password' id='password' /><hr/>
|
|
<input type='hidden' id='currentpage' value='creatid' />
|
|
<select id='selectProcess' class='custom-select'></select><hr/>
|
|
<div class='side-by-side'>
|
|
<button type='submit' id='submitButton' class='bg-primary'>Create</button>
|
|
<div>
|
|
<a href='#' id='displayrecover'>Recover</a>
|
|
</div>
|
|
</div>
|
|
</form><br/>
|
|
<div id='passwordalert' class='passwordalert'></div>
|
|
</div>`;
|
|
}
|
|
|
|
public async updateIdInjectHtml() {
|
|
const container = document.getElementById('containerId');
|
|
|
|
if (!container) {
|
|
console.error("No html container");
|
|
return;
|
|
}
|
|
|
|
container.innerHTML =
|
|
`<body>
|
|
<div class='container'>
|
|
<div>
|
|
<h3>Update an Id</h3>
|
|
</div>
|
|
<hr />
|
|
<form id='form4nk' action='#'>
|
|
<label for='firstName'>First Name:</label>
|
|
<input type='text' id='firstName' name='firstName' required />
|
|
|
|
<label for='lastName'>Last Name:</label>
|
|
<input type='text' id='lastName' name='lastName' required />
|
|
|
|
<label for='Birthday'>Birthday:</label>
|
|
<input type='date' id='Birthday' name='birthday' />
|
|
|
|
<label for='file'>File:</label>
|
|
<input type='file' id='fileInput' name='file' />
|
|
|
|
<label>Third parties:</label>
|
|
<div id='sp-address-block'>
|
|
<div class='side-by-side'>
|
|
<input
|
|
type='text'
|
|
name='sp-address'
|
|
id='sp-address'
|
|
placeholder='sp address'
|
|
form='no-form'
|
|
/>
|
|
<button
|
|
type='button'
|
|
class='circle-btn bg-secondary'
|
|
id='add-sp-address-btn'
|
|
>
|
|
+
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class='div-text-area'>
|
|
<textarea
|
|
name='bio'
|
|
id=''
|
|
cols='30'
|
|
rows='10'
|
|
placeholder='Bio'
|
|
></textarea>
|
|
</div>
|
|
<button type='submit' class='bg-primary'>Update</button>
|
|
</form>
|
|
</div>
|
|
</body>`;
|
|
}
|
|
|
|
public async injectHtml(processName: string) {
|
|
const container = document.getElementById('containerId');
|
|
|
|
if (!container) {
|
|
console.error("No html container");
|
|
return;
|
|
}
|
|
|
|
const services = await Services.getInstance();
|
|
|
|
// do we have all processes in db?
|
|
const knownProcesses = await services.getAllProcess();
|
|
const processesFromNetwork: Process[] = services.sdkClient.get_processes();
|
|
|
|
const processToAdd = processesFromNetwork.filter(processFromNetwork => !knownProcesses.some(knownProcess => knownProcess.id === processFromNetwork.id));
|
|
|
|
processToAdd.forEach(async p => {
|
|
await services.addProcess(p);
|
|
})
|
|
|
|
// get the process we need
|
|
const process = await services.getProcessByName(processName);
|
|
if (process) {
|
|
container.innerHTML = process.html;
|
|
} else {
|
|
console.error("No process ", processName);
|
|
}
|
|
}
|
|
|
|
// public async getCurrentProcess(): Promise<string> {
|
|
// let currentProcess = "";
|
|
// try {
|
|
// const indexedDB = await IndexedDB.getInstance();
|
|
// const db = indexedDB.getDb();
|
|
// currentProcess = await indexedDB.getObject<string>(db, indexedDB.getStoreList().AnkSession, Services.CURRENT_PROCESS);
|
|
// } catch (error) {
|
|
// console.error("Failed to retrieve currentprocess object :", error);
|
|
// }
|
|
// return currentProcess;
|
|
// }
|
|
|
|
public isPasswordValid(password: string) {
|
|
var alertElem = document.getElementById("passwordalert");
|
|
var success = true;
|
|
var strength = 0;
|
|
if (password.match(/[a-z]+/)) {
|
|
var strength = 0;
|
|
strength += 1;
|
|
}
|
|
if (password.match(/[A-Z]+/)) {
|
|
strength += 1;
|
|
}
|
|
if (password.match(/[0-9]+/)) {
|
|
strength += 1;
|
|
}
|
|
if (password.match(/[$@#&!]+/)) {
|
|
strength += 1;
|
|
}
|
|
if (alertElem !== null) {
|
|
// TODO Passer à 18
|
|
if (password.length < 4) {
|
|
alertElem.innerHTML = "Password size is < 4";
|
|
success = false;
|
|
} else {
|
|
if (password.length > 30) {
|
|
alertElem.innerHTML = "Password size is > 30";
|
|
success = false;
|
|
} else {
|
|
if (strength < 4) {
|
|
alertElem.innerHTML = "Password need [a-z] [A-Z] [0-9]+ [$@#&!]+";
|
|
success = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
private async pickWebsocketConnectionRandom(): Promise<WebSocketClient | null> {
|
|
const services = await Services.getInstance();
|
|
const websockets = services.websocketConnection;
|
|
if (websockets.length === 0) {
|
|
console.error("No websocket connection available at the moment");
|
|
return null;
|
|
} else {
|
|
const random = Math.floor(Math.random() * websockets.length);
|
|
return websockets[random];
|
|
}
|
|
}
|
|
|
|
public async obtainTokenWithFaucet(): Promise<void> {
|
|
const services = await Services.getInstance();
|
|
const connection = await services.pickWebsocketConnectionRandom();
|
|
if (!connection) {
|
|
throw 'no available relay connections';
|
|
}
|
|
|
|
let cachedMsg: CachedMessage;
|
|
try {
|
|
const flag: AnkFlag = 'Faucet';
|
|
cachedMsg = services.sdkClient.create_faucet_msg();
|
|
if (cachedMsg.commitment && cachedMsg.recipient) {
|
|
let faucetMsg: FaucetMessage = {
|
|
sp_address: cachedMsg.recipient,
|
|
commitment: cachedMsg.commitment,
|
|
error: null,
|
|
}
|
|
connection.sendMessage(flag, JSON.stringify(faucetMsg));
|
|
}
|
|
} catch (error) {
|
|
throw `Failed to obtain tokens with relay ${connection.getUrl()}: ${error}`;
|
|
}
|
|
|
|
try {
|
|
await services.updateMessages(cachedMsg);
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public async updateUser(user: User): Promise<void> {
|
|
try {
|
|
const indexedDB = await IndexedDB.getInstance();
|
|
const db = await indexedDB.getDb();
|
|
await indexedDB.setObject(db, indexedDB.getStoreList().AnkUser, user, null);
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public async getUserInfo(): Promise<User | null> {
|
|
try {
|
|
const indexedDB = await IndexedDB.getInstance();
|
|
const db = await indexedDB.getDb();
|
|
let user = await indexedDB.getAll<User>(db, indexedDB.getStoreList().AnkUser);
|
|
// This should never happen
|
|
if (user.length > 1) {
|
|
throw "Multiple users in db";
|
|
} else {
|
|
let res = user.pop();
|
|
if (res === undefined) {
|
|
return null;
|
|
} else {
|
|
return res;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
public async answer_confirmation_message(msg: CachedMessage): Promise<void> {
|
|
const services = await Services.getInstance();
|
|
const connection = await services.pickWebsocketConnectionRandom();
|
|
if (!connection) {
|
|
throw new Error("No connection to relay");
|
|
}
|
|
let user: User;
|
|
try {
|
|
let possibleUser = await services.getUserInfo();
|
|
if (!possibleUser) {
|
|
throw new Error("No user loaded, please first create a new user or login");
|
|
} else {
|
|
user = possibleUser;
|
|
}
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
|
|
let notificationInfo: createTransactionReturn;
|
|
try {
|
|
const feeRate = 1;
|
|
notificationInfo = services.sdkClient.answer_confirmation_transaction(msg.id, feeRate);
|
|
} catch (error) {
|
|
throw new Error(`Failed to create confirmation transaction: ${error}`);
|
|
}
|
|
const flag: AnkFlag = "NewTx";
|
|
const newTxMsg: NewTxMessage = {
|
|
'transaction': notificationInfo.transaction,
|
|
'tweak_data': null,
|
|
'error': null,
|
|
}
|
|
connection.sendMessage(flag, JSON.stringify(newTxMsg));
|
|
await services.updateMessages(notificationInfo.new_network_msg);
|
|
return;
|
|
}
|
|
|
|
public async confirm_sender_address(msg: CachedMessage): Promise<void> {
|
|
const services = await Services.getInstance();
|
|
const connection = await services.pickWebsocketConnectionRandom();
|
|
if (!connection) {
|
|
throw new Error("No connection to relay");
|
|
}
|
|
let user: User;
|
|
try {
|
|
let possibleUser = await services.getUserInfo();
|
|
if (!possibleUser) {
|
|
throw new Error("No user loaded, please first create a new user or login");
|
|
} else {
|
|
user = possibleUser;
|
|
}
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
|
|
let notificationInfo: createTransactionReturn;
|
|
try {
|
|
const feeRate = 1;
|
|
notificationInfo = services.sdkClient.create_confirmation_transaction(msg.id, feeRate);
|
|
} catch (error) {
|
|
throw new Error(`Failed to create confirmation transaction: ${error}`);
|
|
}
|
|
const flag: AnkFlag = "NewTx";
|
|
const newTxMsg: NewTxMessage = {
|
|
'transaction': notificationInfo.transaction,
|
|
'tweak_data': null,
|
|
'error': null,
|
|
}
|
|
connection.sendMessage(flag, JSON.stringify(newTxMsg));
|
|
await services.updateMessages(notificationInfo.new_network_msg);
|
|
return;
|
|
}
|
|
|
|
public async notify_address_for_message(sp_address: string, message: CipherMessage): Promise<createTransactionReturn> {
|
|
const services = await Services.getInstance();
|
|
const connection = await services.pickWebsocketConnectionRandom();
|
|
if (!connection) {
|
|
throw 'No available connection';
|
|
}
|
|
|
|
try {
|
|
const feeRate = 1;
|
|
let notificationInfo: createTransactionReturn = services.sdkClient.create_notification_transaction(sp_address, message, feeRate);
|
|
const flag: AnkFlag = "NewTx";
|
|
const newTxMsg: NewTxMessage = {
|
|
'transaction': notificationInfo.transaction,
|
|
'tweak_data': null,
|
|
'error': null,
|
|
}
|
|
connection.sendMessage(flag, JSON.stringify(newTxMsg));
|
|
console.info('Successfully sent notification transaction');
|
|
return notificationInfo;
|
|
} catch (error) {
|
|
throw 'Failed to create notification transaction:", error';
|
|
}
|
|
}
|
|
}
|
|
|
|
export default Services;
|