454 lines
17 KiB
TypeScript
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;
|