sdk_client/src/services.ts
2024-04-08 18:34:53 +02:00

454 lines
17 KiB
TypeScript

import { createUserReturn, User, Process } 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 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();
}
// public async getSpAddressDefaultClient(): Promise<string | null> {
// try {
// const indexedDB = await IndexedDB.getInstance();
// const db = indexedDB.getDb();
// const spClient = await indexedDB.getObject<string>(db, indexedDB.getStoreList().SpClient, "default");
// if (spClient) {
// return this.sdkClient.get_receiving_address(spClient);
// } else {
// console.error("SP client not found");
// return null;
// }
// } catch (error) {
// console.error("Failed to retrieve object or get sp address:", error);
// return null;
// }
// }
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 = 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.injectHtml('CREATE_ID');
services.attachSubmitListener("form4nk", (event) => services.createId(event));
services.attachClickListener("displayrecover", services.displayRecover);
await services.displayProcess();
}
public async createId(event: Event): Promise<void> {
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;
this.current_process = processElement.value;
console.log("JS password: " + password + " process: " + this.current_process);
// To comment if test
// if (!Services.instance.isPasswordValid(password)) return;
let label = null;
let birthday_signet = 50000;
let birthday_main = 500000;
const user: createUserReturn = this.sdkClient.create_user(password, label, birthday_main, birthday_signet, this.current_process);
try {
const indexedDb = await IndexedDB.getInstance();
const db = indexedDb.getDb();
await indexedDb.writeObject(db, indexedDb.getStoreList().AnkUser, user.user, null);
await indexedDb.writeObject(db, indexedDb.getStoreList().SpOutputs, user.output_list_vec, null);
} catch (error) {
console.error("Failed to write user object :", error);
}
await Services.instance.displayRevokeImage();
}
public async displayRecover(): Promise<void> {
const services = await Services.getInstance();
await services.injectHtml('RECOVER');
services.attachSubmitListener("form4nk", services.recover);
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();
console.log("JS recover submit ");
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;
// TODO
alert("Recover submit to do ...");
}
public async displayRevokeImage(): Promise<void> {
const services = await Services.getInstance();
await services.injectHtml('REVOKE_IMAGE');
services.attachClickListener("displayupdateanid", services.displayUpdateAnId);
let imageBytes = await services.getRecoverImage('assets/4nk_revoke.jpg');
if (imageBytes != null) {
let blob = new Blob([imageBytes], {type: 'image/png'});
var elem = document.getElementById("revoke") as HTMLAnchorElement;
if (elem != null) {
elem.href = URL.createObjectURL(blob);
}
}
}
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 parseBitcoinMessage(raw: Blob): Promise<string | null> {
try {
const buffer = await raw.arrayBuffer();
const uint8Array = new Uint8Array(buffer);
const msg: string = this.sdkClient.parse_bitcoin_network_msg(uint8Array);
return msg;
} catch (error) {
console.error("Error processing the blob:", error);
return null;
}
}
public async displayRevoke(): Promise<void> {
const services = await Services.getInstance();
services.injectHtml('REVOKE');
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();
console.log("JS displayUpdateAnId process : "+services.current_process);
let body = "";
let style = "";
let script = "";
try {
const processObject = await services.getProcessByName("UPDATE_ID");
if (processObject) {
body = processObject.html;
style = processObject.style;
script = processObject.script;
console.log("JS displayUpdateAnId body : "+body);
}
} catch (error) {
console.error("Failed to retrieve process with Error:", error);
}
services.injectUpdateAnIdHtml(body, style, script);
services.attachSubmitListener("form4nk", services.updateAnId);
}
public async parse4nkMessage(raw: string): Promise<string | null> {
const msg: string = this.sdkClient.parse_4nk_msg(raw);
return msg;
}
public injectUpdateAnIdHtml(bodyToInject: string, styleToInject: string, scriptToInject: string) {
console.log("JS html : "+bodyToInject);
const body = document.getElementsByTagName('body')[0];
if (!body) {
console.error("No body tag");
return;
}
body.innerHTML = styleToInject + bodyToInject;
const script = document.createElement("script");
script.innerHTML = scriptToInject;
document.body.appendChild(script);
script.onload = () => {
console.log('Script loaded successfuly');
};
script.onerror = () => {
console.log('Error loading script');
};
}
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 getAllProcess(): Promise<Process[]> {
try {
const indexedDB = await IndexedDB.getInstance();
const db = indexedDB.getDb();
let processListObject = await indexedDB.getAll<Process>(db, indexedDB.getStoreList().AnkProcess);
return processListObject;
} catch (error) {
console.log('getAllProcess failed: ',error);
return [];
}
}
public async checkTransaction(tx: string): Promise<string | null> {
const services = await Services.getInstance();
try {
return services.sdkClient.check_network_transaction(tx);
} catch (error) {
console.error(error);
return null;
}
}
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 = 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 = indexedDB.getDb();
const process = await indexedDB.getFirstMatchWithIndex<Process>(db, indexedDB.getStoreList().AnkProcess, 'by_name', name);
console.log('getProcessByName process: '+process);
return process;
}
public async loadProcesses(): Promise<void> {
const services = await Services.getInstance();
const processList: Process[] = services.sdkClient.get_processes();
console.error('processList size: '+processList.length);
processList.forEach(async (process: Process) => {
const indexedDB = await IndexedDB.getInstance();
const db = indexedDB.getDb();
try {
const processStore = await indexedDB.getObject<Process>(db, indexedDB.getStoreList().AnkProcess, process.id);
if (!processStore) {
console.error('Add process.id : '+process.id);
await indexedDB.writeObject(db, indexedDB.getStoreList().AnkProcess, process, null);
}
} catch (error) {
console.warn('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 injectHtml(processName: string) {
// console.log("JS html : "+html);
const container = document.getElementById('containerId');
if (!container) {
console.error("No html container");
return;
}
const services = await Services.getInstance();
await services.loadProcesses();
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;
}
public obtainTokenWithFaucet(wsclient: WebSocketClient, spaddress: string): string | null {
try {
wsclient.sendMessage(spaddress);
} catch (error) {
console.error("Failed to obtain tokens with relay ", wsclient.getUrl());
return null;
}
return null;
}
}
export default Services;