From dd421227450eb846bf7642357cbaf2b1346158d9 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 18 Jul 2024 00:27:13 +0300 Subject: [PATCH 01/58] Support es6 build --- packages/monitoring/package.json | 21 +++++++++++++------ packages/monitoring/tsconfig-es6.json | 20 ++++++++++++++++++ .../{tsconfig.json => tsconfig-node.json} | 0 packages/playground/config.js | 17 +++++++++++++++ 4 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 packages/monitoring/tsconfig-es6.json rename packages/monitoring/{tsconfig.json => tsconfig-node.json} (100%) create mode 100644 packages/playground/config.js diff --git a/packages/monitoring/package.json b/packages/monitoring/package.json index e74799ac1d..cf20b4254a 100644 --- a/packages/monitoring/package.json +++ b/packages/monitoring/package.json @@ -3,11 +3,21 @@ "version": "2.5.0", "description": "Threefold monitoring package", "license": "Apache-2.0", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "scripts": { - "example": "yarn run ts-node --project tsconfig.json example/index.ts", - "build": "tsc", + "main": "./dist/node/index.js", + "module": "./dist/es6/index.js", + "exports": { + "require": "./dist/node/index.js", + "import": "./dist/es6/index.js" + }, + "types": "dist/es6/index.d.ts", + "files": [ + "/dist" + ], + "scripts": { + "example": "yarn run ts-node --project tsconfig-node.json example/index.ts", + "build": "npm-run-all es6-build node-build", + "node-build": "tsc --build tsconfig-node.json", + "es6-build": "tsc --build tsconfig-es6.json", "test": "jest " }, "author": "Omar Kassem", @@ -19,7 +29,6 @@ "@threefold/rmb_direct_client": "2.5.0", "@threefold/tfchain_client": "2.5.0", "@threefold/types": "2.5.0", - "axios": "^0.27.2", "chalk": "4.1.2", "ts-node": "^10.9.1", "typescript": "^5.3.3" diff --git a/packages/monitoring/tsconfig-es6.json b/packages/monitoring/tsconfig-es6.json new file mode 100644 index 0000000000..055227145e --- /dev/null +++ b/packages/monitoring/tsconfig-es6.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "module": "ES2015", + "target": "es6", + "lib": ["DOM", "ES6", "DOM.Iterable", "ScriptHost", "ES2016.Array.Include"], + "types": ["node"], + "declaration": true, + "declarationMap": true, + "outDir": "./dist/es6", + "esModuleInterop": true, + "resolveJsonModule": true, + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "baseUrl": ".", + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": ["src/**/*"], + "exclude": ["src/server/*"] +} \ No newline at end of file diff --git a/packages/monitoring/tsconfig.json b/packages/monitoring/tsconfig-node.json similarity index 100% rename from packages/monitoring/tsconfig.json rename to packages/monitoring/tsconfig-node.json diff --git a/packages/playground/config.js b/packages/playground/config.js new file mode 100644 index 0000000000..cfc777261b --- /dev/null +++ b/packages/playground/config.js @@ -0,0 +1,17 @@ +window.env = { + NETWORK: "dev", + GRAPHQL_URL: "https://graphql.dev.grid.tf/graphql", + GRIDPROXY_URL: "https://gridproxy.dev.grid.tf", + SUBSTRATE_URL: "wss://tfchain.dev.grid.tf/ws", + ACTIVATION_SERVICE_URL: "https://activation.dev.grid.tf/activation/activate", + RELAY_DOMAIN: "wss://relay.dev.grid.tf", + BRIDGE_TFT_ADDRESS: "GDHJP6TF3UXYXTNEZ2P36J5FH7W4BJJQ4AYYAXC66I2Q2AH5B6O6BCFG", + STELLAR_NETWORK: "test", + STELLAR_HORIZON_URL: "https://horizon-testnet.stellar.org", + TFT_ASSET_ISSUER: "GA47YZA3PKFUZMPLQ3B5F2E3CJIB57TGGU7SPCQT2WAEYKN766PWIMB3", + MINTING_URL: "https://alpha.minting.tfchain.grid.tf", + STATS_URL: "https://stats.dev.grid.tf", + TIMEOUT: +"10000", + PAGE_SIZE: +"20", + MANUAL_URL: "https://www.manual.grid.tf", +}; From 4d57afa76aebc53c43704f0a5c374fc9c71b9bb5 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 18 Jul 2024 00:37:44 +0300 Subject: [PATCH 02/58] Refactor: update events make the error optional and support log with color --- packages/monitoring/src/helpers/events.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/monitoring/src/helpers/events.ts b/packages/monitoring/src/helpers/events.ts index 14ffc56120..bf20627b40 100644 --- a/packages/monitoring/src/helpers/events.ts +++ b/packages/monitoring/src/helpers/events.ts @@ -17,8 +17,8 @@ class MonitorEventEmitter extends EventEmitter { this.addListener(MonitorEvents.storeStatus, this.addToServiceSummary); this.addListener(MonitorEvents.summarize, this.printStatusSummary); } - public log(message: string) { - this.emit("MonitorLog", message); + public log(message: string, color?: string) { + this.emit("MonitorLog", message, color); } public summarize() { this.emit("MonitorSummarize"); @@ -26,12 +26,16 @@ class MonitorEventEmitter extends EventEmitter { public storeStatus(serviceName: string, isAlive: boolean) { this.emit("MonitorStoreStatus", serviceName, isAlive); } - public serviceDown(serviceName: string, error: Error) { + public serviceDown(serviceName: string, error?: Error) { this.emit("MonitorServiceDown", serviceName, error); } - private monitorLogsHandler(msg) { - console.log(msg); + private monitorLogsHandler(msg: unknown, color?: string) { + if (color && chalk[color]) { + console.log(chalk[color](msg)); + } else { + console.log(msg); + } } private serviceDownHandler(serviceName: string, error: Error) { console.log(`${chalk.red.bold(serviceName + " is Down")}`); From be6736e9617e9a6dcca3809bde16df6ab897cc92 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 18 Jul 2024 06:59:55 +0300 Subject: [PATCH 03/58] - Refactor - sendGetRequest to sendRequest and use fetch instead of axios. - replace serviceUrl and serviceName with getters - add update method that uses URL setter - Make all IServiceBase classes take an optional options on their contractor, add error handling for side effect - Grouped all RMB properties to RMB to RMBProps - Feat: - add stats monitor class that monitors stats service - add activation monitor class that monitor activation service --- packages/monitoring/src/helpers/utils.ts | 14 +++--- .../src/serviceMonitor/activations.ts | 26 ++++++++++ .../src/serviceMonitor/alivenessChecker.ts | 6 +-- .../monitoring/src/serviceMonitor/graphql.ts | 29 ++++++++--- .../src/serviceMonitor/gridproxy.ts | 19 ++++--- packages/monitoring/src/serviceMonitor/rmb.ts | 49 ++++++++++++++----- .../monitoring/src/serviceMonitor/stats.ts | 26 ++++++++++ .../monitoring/src/serviceMonitor/tfChain.ts | 20 ++++++-- packages/monitoring/src/types/index.ts | 31 +++++++++--- 9 files changed, 173 insertions(+), 47 deletions(-) create mode 100644 packages/monitoring/src/serviceMonitor/activations.ts create mode 100644 packages/monitoring/src/serviceMonitor/stats.ts diff --git a/packages/monitoring/src/helpers/utils.ts b/packages/monitoring/src/helpers/utils.ts index 9855f4c696..51d8be6da4 100644 --- a/packages/monitoring/src/helpers/utils.ts +++ b/packages/monitoring/src/helpers/utils.ts @@ -1,15 +1,13 @@ import { RequestError } from "@threefold/types"; -import axios, { AxiosError, AxiosRequestConfig } from "axios"; -import { ServiceStatus } from "src/types"; -export async function sendGetRequest(url: string, options: AxiosRequestConfig = {}) { +import { ServiceStatus } from "../types"; + +export async function sendRequest(url: string, options: RequestInit) { try { - return await axios.get(url, options); + const res = await fetch(url, options); + if (!res?.ok) throw Error(`HTTP Response Code: ${res?.status}`); } catch (e) { - const { response } = e as AxiosError; - const errorMessage = (response?.data as { error: string })?.error || (e as Error).message; - - throw new RequestError(`HTTP request failed ${errorMessage ? "due to " + errorMessage : ""}.`); + throw new RequestError(`HTTP request failed due to ${e}.`); } } diff --git a/packages/monitoring/src/serviceMonitor/activations.ts b/packages/monitoring/src/serviceMonitor/activations.ts new file mode 100644 index 0000000000..cf56becbd6 --- /dev/null +++ b/packages/monitoring/src/serviceMonitor/activations.ts @@ -0,0 +1,26 @@ +import { resolveServiceStatus, sendRequest } from "../helpers/utils"; +import { ILivenessChecker, ServiceStatus } from "../types"; + +export class ActivationMonitor implements ILivenessChecker { + private readonly name = "Activation"; + private url: string; + constructor(activationServiceUrl?: string) { + if (activationServiceUrl) this.url = activationServiceUrl; + } + public get Name() { + return this.name; + } + public get URL() { + if (!this.url) throw new Error("Can't access before initialization"); + return this.url; + } + private set URL(url: string) { + this.url = url; + } + public update(param: { url: string }): void { + this.URL = param.url; + } + async isAlive(): Promise { + return resolveServiceStatus(sendRequest(this.url, { method: "Get" })); + } +} diff --git a/packages/monitoring/src/serviceMonitor/alivenessChecker.ts b/packages/monitoring/src/serviceMonitor/alivenessChecker.ts index 3bfa5769d3..15f942ebf9 100644 --- a/packages/monitoring/src/serviceMonitor/alivenessChecker.ts +++ b/packages/monitoring/src/serviceMonitor/alivenessChecker.ts @@ -23,13 +23,13 @@ export class ServiceMonitor { for (let retryCount = 1; retryCount <= this.retries; retryCount++) { const { alive, error } = await service.isAlive(); if (alive) { - monitorEvents.storeStatus(service.serviceName(), alive); + monitorEvents.storeStatus(service.Name, alive); break; } if (retryCount < this.retries) { - monitorEvents.log(`${service.serviceName()} seems to be down; Retrying (${retryCount}/${this.retries})...`); + monitorEvents.log(`${service.Name} seems to be down; Retrying (${retryCount}/${this.retries})...`); await new Promise(resolve => setTimeout(resolve, this.retryInterval * 60)); - } else monitorEvents.serviceDown(service.serviceName(), error); + } else monitorEvents.serviceDown(service.Name, error); } } monitorEvents.summarize(); diff --git a/packages/monitoring/src/serviceMonitor/graphql.ts b/packages/monitoring/src/serviceMonitor/graphql.ts index bbb1207cbf..1f48cdd54c 100644 --- a/packages/monitoring/src/serviceMonitor/graphql.ts +++ b/packages/monitoring/src/serviceMonitor/graphql.ts @@ -1,20 +1,35 @@ -import { resolveServiceStatus, sendGetRequest } from "../helpers/utils"; +import { resolveServiceStatus, sendRequest } from "../helpers/utils"; import { ILivenessChecker, ServiceStatus } from "../types"; export class GraphQLMonitor implements ILivenessChecker { private readonly name = "GraphQl"; - private readonly url: string; - constructor(graphQlUrl: string) { - this.url = graphQlUrl; + private url: string; + constructor(graphQlUrl?: string) { + if (graphQlUrl) this.url = graphQlUrl; } - serviceName() { + public get Name() { return this.name; } - serviceUrl() { + public get URL() { + if (!this.url) throw new Error("Can't access before initialization"); return this.url; } + private set URL(url: string) { + this.url = url; + } + public update(param: { url: string }) { + this.URL = param.url; + } async isAlive(): Promise { - return resolveServiceStatus(sendGetRequest(this.url)); + return resolveServiceStatus( + sendRequest(this.url, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + query: "query monitoring{__typename}", + }), + }), + ); } } diff --git a/packages/monitoring/src/serviceMonitor/gridproxy.ts b/packages/monitoring/src/serviceMonitor/gridproxy.ts index cab403be69..1762db2981 100644 --- a/packages/monitoring/src/serviceMonitor/gridproxy.ts +++ b/packages/monitoring/src/serviceMonitor/gridproxy.ts @@ -1,19 +1,26 @@ -import { resolveServiceStatus, sendGetRequest } from "../helpers/utils"; +import { resolveServiceStatus, sendRequest } from "../helpers/utils"; import { ILivenessChecker, ServiceStatus } from "../types"; export class GridProxyMonitor implements ILivenessChecker { private readonly name = "GridProxy"; private url: string; - constructor(gridProxyUrl: string) { - this.url = gridProxyUrl; + constructor(gridProxyUrl?: string) { + if (gridProxyUrl) this.url = gridProxyUrl; } - serviceName() { + public get Name() { return this.name; } - serviceUrl() { + public get URL() { + if (!this.url) throw new Error("Can't access before initialization"); return this.url; } + private set URL(url: string) { + this.url = url; + } + public update(param: { url: string }): void { + this.URL = param.url; + } async isAlive(): Promise { - return resolveServiceStatus(sendGetRequest(this.url)); + return resolveServiceStatus(sendRequest(this.url, { method: "Get" })); } } diff --git a/packages/monitoring/src/serviceMonitor/rmb.ts b/packages/monitoring/src/serviceMonitor/rmb.ts index 9a00d96f67..0f999dc6e9 100644 --- a/packages/monitoring/src/serviceMonitor/rmb.ts +++ b/packages/monitoring/src/serviceMonitor/rmb.ts @@ -1,29 +1,56 @@ -import { KeypairType } from "@polkadot/util-crypto/types"; import { Client as RMBClient } from "@threefold/rmb_direct_client"; import { generateString, resolveServiceStatus } from "../helpers/utils"; -import { IDisconnectHandler, ILivenessChecker, ServiceStatus } from "../types"; +import { IDisconnectHandler, ILivenessChecker, RMBProps, ServiceStatus } from "../types"; export class RMBMonitor implements ILivenessChecker, IDisconnectHandler { private name = "RMB"; - private url: string; private rmbClient: RMBClient; - constructor(relayUrl: string, chainUrl: string, mnemonic: string, keypairType: KeypairType) { - this.url = relayUrl; - this.rmbClient = new RMBClient(chainUrl, relayUrl, mnemonic, generateString(10), keypairType, 0); + constructor(private RMBProps: RMBProps) { + if (RMBProps) this.factory(); } + + /* + Creates a new RMB Client instance + */ + private factory() { + const { chainUrl, relayUrl, mnemonics, keypairType } = this.RMBProps; + if (relayUrl) { + this.rmbClient = new RMBClient(chainUrl, relayUrl, mnemonics, generateString(10), keypairType, 0); + } + } + private async setUp() { + if (!this.rmbClient) throw new Error("Can't setUp before initialization"); await this.rmbClient.connect(); } - public serviceName() { + public get Name() { return this.name; } - public serviceUrl() { - return this.url; + public get URL() { + if (!this.RMBProps.relayUrl) throw new Error("Can't access before initialization"); + return this.RMBProps.relayUrl; + } + private set URL(url: string) { + this.RMBProps.relayUrl = url; } + + public async update(param: { url: string }) { + if (this.URL === param.url) return; + this.URL = param.url; + if (this.rmbClient) { + await this.disconnect(); + this.rmbClient.relayUrl = this.URL; + } else this.factory(); + } + public async isAlive(): Promise { - if (!this.rmbClient?.con?.OPEN) await this.setUp(); - return resolveServiceStatus(this.rmbClient.ping(2)); + try { + if (!this.rmbClient?.con?.OPEN) await this.setUp(); + return resolveServiceStatus(this.rmbClient.ping(2)); + } catch (error) { + return { alive: false, error }; + } } public async disconnect() { await this.rmbClient.disconnect(); diff --git a/packages/monitoring/src/serviceMonitor/stats.ts b/packages/monitoring/src/serviceMonitor/stats.ts new file mode 100644 index 0000000000..6c580eccd7 --- /dev/null +++ b/packages/monitoring/src/serviceMonitor/stats.ts @@ -0,0 +1,26 @@ +import { resolveServiceStatus, sendRequest } from "../helpers/utils"; +import { ILivenessChecker, ServiceStatus } from "../types"; + +export class StatsMonitor implements ILivenessChecker { + private readonly name = "Stats"; + private url: string; + constructor(statusUrl?: string) { + if (statusUrl) this.url = statusUrl; + } + public get Name() { + return this.name; + } + public get URL() { + if (!this.url) throw new Error("Can't access before initialization"); + return this.url; + } + private set URL(url: string) { + this.url = url; + } + public update(param: { url: string }): void { + this.URL = param.url; + } + async isAlive(): Promise { + return resolveServiceStatus(sendRequest(this.url, { method: "Get" })); + } +} diff --git a/packages/monitoring/src/serviceMonitor/tfChain.ts b/packages/monitoring/src/serviceMonitor/tfChain.ts index 2b5e10ea36..b6684a6f46 100644 --- a/packages/monitoring/src/serviceMonitor/tfChain.ts +++ b/packages/monitoring/src/serviceMonitor/tfChain.ts @@ -7,18 +7,30 @@ export class TFChainMonitor implements ILivenessChecker, IDisconnectHandler { private url: string; private tfClient: QueryClient; constructor(tfChainUrl: string) { - this.url = tfChainUrl; - this.tfClient = new QueryClient(this.url); + if (tfChainUrl) { + this.url = tfChainUrl; + this.tfClient = new QueryClient(this.url); + } } private async setUp() { + if (!this.tfClient) throw new Error("Can't setUp before initialization"); await this.tfClient?.connect(); } - serviceName() { + public get Name() { return this.name; } - serviceUrl() { + public get URL() { + if (!this.url) throw new Error("Can't access before initialization"); return this.url; } + private set URL(url: string) { + this.url = url; + } + public update(param: { url: string }): void { + if (this.URL === param.url) return; + this.URL = param.url; + this.tfClient = new QueryClient(this.url); + } public async isAlive(): Promise { try { if (!this.tfClient.api) await this.setUp(); diff --git a/packages/monitoring/src/types/index.ts b/packages/monitoring/src/types/index.ts index 760c613273..6ce335e247 100644 --- a/packages/monitoring/src/types/index.ts +++ b/packages/monitoring/src/types/index.ts @@ -1,18 +1,24 @@ +import { KeypairType } from "@polkadot/util-crypto/types"; /** * Represents a basic service interface. + * @template P - The type of the parameter object used for the update method. */ -interface IServiceBase { +interface IServiceBase

{ /** - * Returns the name of the service. - * @returns {string} The service name. + * The name of the service. */ - serviceName: () => string; + Name: string; /** - * Returns the URL of the service. - * @returns {string} The service URL. + * The URL of the service. */ - serviceUrl: () => string; + URL: string; + + /** + * Updates the service with the provided parameters. + * @param {P} param - The parameter object with specific keys, should be specified on class. + */ + update(param: P): void; } /** @@ -28,8 +34,9 @@ export interface IDisconnectHandler { /** * Represents a service with liveness checking capability. + * @template P - The type of the parameter object used for the update method. Defaults to {url:string}. */ -export interface ILivenessChecker extends IServiceBase { +export interface ILivenessChecker

extends IServiceBase

{ /** * Checks if the service is alive. * @returns {Promise} A promise that resolves with the current status of the service. @@ -48,3 +55,11 @@ export enum MonitorEvents { "storeStatus" = "MonitorStoreStatus", "serviceDown" = "MonitorServiceDown", } + +export type RMBProps = { + chainUrl: string; + relayUrl?: string; + mnemonics: string; + session: string; + keypairType: KeypairType; +}; From 1607559f2d0a9eb785341293ec7548ffc96180e3 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 18 Jul 2024 07:14:14 +0300 Subject: [PATCH 04/58] Feat: Add serviceURLManager it is class managing service URLs with liveness checking - Define retries and silent mode settings - Implement constructor to handle StackManagerOptions - Add private handleErrorsOnSilentMode method for error handling based on silent mode - Implement getAvailableStack method to find reachable service URL - Implement getAvailableServicesStack method to fetch and store available service URLs for all services --- packages/monitoring/src/index.ts | 1 + .../monitoring/src/serviceMonitor/index.ts | 1 + .../src/serviceMonitor/serviceURLManager.ts | 113 ++++++++++++++++++ packages/monitoring/src/types/index.ts | 35 ++++++ 4 files changed, 150 insertions(+) create mode 100644 packages/monitoring/src/serviceMonitor/serviceURLManager.ts diff --git a/packages/monitoring/src/index.ts b/packages/monitoring/src/index.ts index 66484a7686..209cbc1457 100644 --- a/packages/monitoring/src/index.ts +++ b/packages/monitoring/src/index.ts @@ -1 +1,2 @@ export * from "./serviceMonitor/index"; +export * from "./types"; diff --git a/packages/monitoring/src/serviceMonitor/index.ts b/packages/monitoring/src/serviceMonitor/index.ts index 34e22fa696..5abade0125 100644 --- a/packages/monitoring/src/serviceMonitor/index.ts +++ b/packages/monitoring/src/serviceMonitor/index.ts @@ -3,3 +3,4 @@ export { TFChainMonitor } from "./tfChain"; export { RMBMonitor } from "./rmb"; export { ServiceMonitor } from "./alivenessChecker"; export { GraphQLMonitor } from "./graphql"; +export { ServiceUrlManager } from "./serviceURLManager"; diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts new file mode 100644 index 0000000000..748e1216a7 --- /dev/null +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -0,0 +1,113 @@ +import { monitorEvents } from "../helpers/events"; +import { IDisconnectHandler, ILivenessChecker, Service, ServiceUrl, StackManagerOptions } from "../types"; + +/** + * Manages service URLs, checking their availability and ensuring they are reachable. + * + * @template N - A boolean type that defaults to false, this got effected by silent value and effects the results type; + * + * @constructor + * @param {StackManagerOptions} [options] - The options for configuring the manager. + * + * @property {Service[]} [services] - The list of services to be managed. + */ +export class ServiceUrlManager { + public results: { [key: string]: ServiceUrl } = {}; + private retries = 3; + private silent: N = false as N; + public services: Service[]; + + constructor(options: StackManagerOptions) { + console.log(this); + Object.assign(this, options); + console.log(this); + } + + /** + * Pings the given service to check if it is alive. + * + * This method checks the liveness of the provided service by calling its `isAlive` method. + * If the service supports disconnection (implements IDisconnectHandler), it calls the `disconnect` method after the liveness check. + * + * @param {ILivenessChecker} service - An instance of ILivenessChecker that provides methods to check the service's liveness. + * + * @returns {Promise<{alive: boolean, error?: Error}>} - A promise that resolves with the liveness status of the service. + */ + private async pingService(service: ILivenessChecker) { + const status = await service.isAlive(); + if ("disconnect" in service) { + await (service as IDisconnectHandler).disconnect(); + } + return status; + } + /** + * Handles errors based on the silent mode setting. + * + * If silent mode is enabled, logs the error message and returns null. + * If silent mode is disabled, throws an error with the provided message. + * + * @private + * @param {string} errorMsg - The error message to handle. + * @returns {null} - Returns null if silent mode is enabled, otherwise throws an error. + * @throws {Error} - Throws an error with the provided message if silent mode is disabled. + */ + private handleErrorsOnSilentMode(errorMsg: string) { + if (this.silent) { + console.log(errorMsg); + return null; + } + throw new Error(errorMsg); + } + + /** + * Attempts to find a reachable service URL from a list of provided URLs. + * + * This method iterates through the list of URLs, repeatedly pinging the service to + * check if it is alive. If a reachable URL is found, it is returned. If all URLs + * are exhausted without finding a reachable service, an error is thrown. + * + * @param {string[]} urls - An array of service URLs to check for reachability. + * @param {ILivenessChecker} service - An instance of ILivenessChecker that provides methods + * to check the service's liveness and manage service URLs. + * + * @returns {Promise} - A promise that resolves with the reachable service URL. + * + * @throws {Error} - Throws an error if no reachable service URL is found after checking all URLs. + * + */ + async getAvailableStack(urls: string[], service: ILivenessChecker): Promise> { + let error: Error | string = ""; + for (let i = 0; i < urls.length; i++) { + await service.update({ url: urls[i] }); + monitorEvents.log(`${service.Name}: pinging ${service.URL}`, "gray"); + for (let retry = 0; retry < this.retries; retry++) { + const status = await this.pingService(service); + if (status.alive) { + monitorEvents.log(`${service.Name} on ${service.URL} Success!`, "green"); + return service.URL; + } + error = status.error ?? ""; + } + monitorEvents.log( + `${service.Name}: failed to ping ${service.URL} after ${this.retries} retries; ${error}`, + "red", + ); + } + return this.handleErrorsOnSilentMode(`Failed to reach ${service.Name} on all provided stacks`) as ServiceUrl; + } + + async getAvailableServicesStack(): Promise<{ [key: string]: ServiceUrl }> { + const result: { [key: string]: Promise> } = {}; + + for (const { URLs, service } of this.services) { + result[service.Name] = this.getAvailableStack(URLs, service); + } + + const entries = Object.entries(result); + const values = await Promise.all(entries.map(([_, promise]) => promise)); + for (let i = 0; i < entries.length; i++) { + this.results[entries[i][0]] = values[i]; + } + return this.results; + } +} diff --git a/packages/monitoring/src/types/index.ts b/packages/monitoring/src/types/index.ts index 6ce335e247..bce1c0ebda 100644 --- a/packages/monitoring/src/types/index.ts +++ b/packages/monitoring/src/types/index.ts @@ -63,3 +63,38 @@ export type RMBProps = { session: string; keypairType: KeypairType; }; + +/** + * Represents a service with its stacks and its ILivenessChecker instance. + */ +export type Service = { + /** + * An array of URLs associated with the service. + */ + URLs: string[]; + /** + * An instance of a liveness checker for the service. + */ + service: ILivenessChecker; +}; + +/** + * Options for configuring the stack manager. + * @template N - A boolean type that defaults to false, represents silent property type This will effect the result type as well. + */ +export type StackManagerOptions = { + /** + * An array of services to be managed by the stack manager. + */ + services: Service[]; + /** + * Optional. The number of retries for failed service checks for each stack. + */ + retries?: number; + /** + * Optional. Determines if the stack manager should operate silently without throwing any errors and just return null as result. + */ + silent?: N; +}; + +export type ServiceUrl = N extends false ? string : string | null; From 648533caa42bf3822f7d73ceae76bdc9cba81a17 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 18 Jul 2024 07:21:19 +0300 Subject: [PATCH 05/58] Revert: playground/config.js --- packages/playground/config.js | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 packages/playground/config.js diff --git a/packages/playground/config.js b/packages/playground/config.js deleted file mode 100644 index cfc777261b..0000000000 --- a/packages/playground/config.js +++ /dev/null @@ -1,17 +0,0 @@ -window.env = { - NETWORK: "dev", - GRAPHQL_URL: "https://graphql.dev.grid.tf/graphql", - GRIDPROXY_URL: "https://gridproxy.dev.grid.tf", - SUBSTRATE_URL: "wss://tfchain.dev.grid.tf/ws", - ACTIVATION_SERVICE_URL: "https://activation.dev.grid.tf/activation/activate", - RELAY_DOMAIN: "wss://relay.dev.grid.tf", - BRIDGE_TFT_ADDRESS: "GDHJP6TF3UXYXTNEZ2P36J5FH7W4BJJQ4AYYAXC66I2Q2AH5B6O6BCFG", - STELLAR_NETWORK: "test", - STELLAR_HORIZON_URL: "https://horizon-testnet.stellar.org", - TFT_ASSET_ISSUER: "GA47YZA3PKFUZMPLQ3B5F2E3CJIB57TGGU7SPCQT2WAEYKN766PWIMB3", - MINTING_URL: "https://alpha.minting.tfchain.grid.tf", - STATS_URL: "https://stats.dev.grid.tf", - TIMEOUT: +"10000", - PAGE_SIZE: +"20", - MANUAL_URL: "https://www.manual.grid.tf", -}; From 035f4d57f9a7d0cdf81b43c09484e4647ba7844a Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 18 Jul 2024 07:29:06 +0300 Subject: [PATCH 06/58] Refactor: remove sesstion from RMBOptions it will be handled internally --- packages/monitoring/src/types/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/monitoring/src/types/index.ts b/packages/monitoring/src/types/index.ts index bce1c0ebda..702f30803e 100644 --- a/packages/monitoring/src/types/index.ts +++ b/packages/monitoring/src/types/index.ts @@ -60,7 +60,6 @@ export type RMBProps = { chainUrl: string; relayUrl?: string; mnemonics: string; - session: string; keypairType: KeypairType; }; From aadf52a3b0f59d20531bdb30bc975a225393830f Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 18 Jul 2024 07:31:00 +0300 Subject: [PATCH 07/58] Refactor: update service monitor example --- .../monitoring/example/{index.ts => serviceMonitor.ts} | 9 +++++++-- packages/monitoring/package.json | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) rename packages/monitoring/example/{index.ts => serviceMonitor.ts} (77%) diff --git a/packages/monitoring/example/index.ts b/packages/monitoring/example/serviceMonitor.ts similarity index 77% rename from packages/monitoring/example/index.ts rename to packages/monitoring/example/serviceMonitor.ts index 418df9897c..ca0716ff49 100644 --- a/packages/monitoring/example/index.ts +++ b/packages/monitoring/example/serviceMonitor.ts @@ -1,11 +1,16 @@ -import { GraphQLMonitor, GridProxyMonitor, RMBMonitor, ServiceMonitor, TFChainMonitor } from "../src/"; +import { GraphQLMonitor, GridProxyMonitor, RMBMonitor, ServiceMonitor, TFChainMonitor } from "../src"; async function HealthCheck() { try { const services = [ new GridProxyMonitor(""), new GraphQLMonitor("https://graphql.dev.grid.tf/graphql"), new TFChainMonitor("wss://tfchain.dev.grid.tf/ws"), - new RMBMonitor("wss://relay.dev.grid.tf", "wss://tfchain.dev.grid.tf/ws", "mnemonic", "sr25519"), + new RMBMonitor({ + relayUrl: "wss://relay.dev.grid.tf", + chainUrl: "wss://tfchain.dev.grid.tf/ws", + mnemonics: "mnemonic", + keypairType: "sr25519", + }), ]; const serviceMonitor = new ServiceMonitor(services); diff --git a/packages/monitoring/package.json b/packages/monitoring/package.json index cf20b4254a..a09cef8ccc 100644 --- a/packages/monitoring/package.json +++ b/packages/monitoring/package.json @@ -14,7 +14,6 @@ "/dist" ], "scripts": { - "example": "yarn run ts-node --project tsconfig-node.json example/index.ts", "build": "npm-run-all es6-build node-build", "node-build": "tsc --build tsconfig-node.json", "es6-build": "tsc --build tsconfig-es6.json", From 1b49c5e63d1a83e7979a0ff0ef8321ad0883982b Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 18 Jul 2024 07:36:06 +0300 Subject: [PATCH 08/58] Refactor: Rename service url manager options --- packages/monitoring/src/serviceMonitor/serviceURLManager.ts | 6 +++--- packages/monitoring/src/types/index.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index 748e1216a7..4ef386a07b 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -1,5 +1,5 @@ import { monitorEvents } from "../helpers/events"; -import { IDisconnectHandler, ILivenessChecker, Service, ServiceUrl, StackManagerOptions } from "../types"; +import { IDisconnectHandler, ILivenessChecker, Service, ServiceUrl, URLManagerOptions } from "../types"; /** * Manages service URLs, checking their availability and ensuring they are reachable. @@ -7,7 +7,7 @@ import { IDisconnectHandler, ILivenessChecker, Service, ServiceUrl, StackManager * @template N - A boolean type that defaults to false, this got effected by silent value and effects the results type; * * @constructor - * @param {StackManagerOptions} [options] - The options for configuring the manager. + * @param {URLManagerOptions} [options] - The options for configuring the manager. * * @property {Service[]} [services] - The list of services to be managed. */ @@ -17,7 +17,7 @@ export class ServiceUrlManager { private silent: N = false as N; public services: Service[]; - constructor(options: StackManagerOptions) { + constructor(options: URLManagerOptions) { console.log(this); Object.assign(this, options); console.log(this); diff --git a/packages/monitoring/src/types/index.ts b/packages/monitoring/src/types/index.ts index 702f30803e..60b87538b6 100644 --- a/packages/monitoring/src/types/index.ts +++ b/packages/monitoring/src/types/index.ts @@ -78,10 +78,10 @@ export type Service = { }; /** - * Options for configuring the stack manager. + * Options for configuring the URL manager. * @template N - A boolean type that defaults to false, represents silent property type This will effect the result type as well. */ -export type StackManagerOptions = { +export type URLManagerOptions = { /** * An array of services to be managed by the stack manager. */ From eb942293710ddaf2d52a9d47b34665d6c191cdc3 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 18 Jul 2024 07:42:55 +0300 Subject: [PATCH 09/58] Refactor: make tfchainMonitor consotractor takes an optional param --- packages/monitoring/src/serviceMonitor/tfChain.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/monitoring/src/serviceMonitor/tfChain.ts b/packages/monitoring/src/serviceMonitor/tfChain.ts index b6684a6f46..52a2d37951 100644 --- a/packages/monitoring/src/serviceMonitor/tfChain.ts +++ b/packages/monitoring/src/serviceMonitor/tfChain.ts @@ -6,7 +6,7 @@ export class TFChainMonitor implements ILivenessChecker, IDisconnectHandler { private name = "TFChain"; private url: string; private tfClient: QueryClient; - constructor(tfChainUrl: string) { + constructor(tfChainUrl?: string) { if (tfChainUrl) { this.url = tfChainUrl; this.tfClient = new QueryClient(this.url); From c81f7144af21b31c96ba8f2e5bca9cb05e68ec11 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 18 Jul 2024 07:52:42 +0300 Subject: [PATCH 10/58] Example: add serviceURlManager Example --- .../monitoring/example/serviceURLManager.ts | 38 +++++++++++++++++++ packages/monitoring/package.json | 1 + 2 files changed, 39 insertions(+) create mode 100644 packages/monitoring/example/serviceURLManager.ts diff --git a/packages/monitoring/example/serviceURLManager.ts b/packages/monitoring/example/serviceURLManager.ts new file mode 100644 index 0000000000..ff514f0483 --- /dev/null +++ b/packages/monitoring/example/serviceURLManager.ts @@ -0,0 +1,38 @@ +import { + GraphQLMonitor, + GridProxyMonitor, + RMBMonitor, + ServiceUrlManager, + TFChainMonitor, + URLManagerOptions, +} from "../src"; + +const gridproxy = ["http://www.nonexistentgridProxy.com", "http://www.gooogleTF.com", "https://gridproxy.dev.grid.tf/"]; +const rmb = ["wss://www.nonExistentRelay.com", "wss://relay.dev.grid.tf"]; +const graphql = ["https://graphql.dev.grid.tf/graphql"]; +const tfChain = ["wss://tfchain.dev.grid.tf/ws", "wss://www.nonExistentChain.com"]; +const mnemonics = ""; + +async function checkStacksAvailability(services: URLManagerOptions) { + try { + const pickedUrls = await new ServiceUrlManager(services).getAvailableServicesStack(); + console.log(pickedUrls); + process.exit(0); + } catch (err) { + console.log(err); + } +} + +checkStacksAvailability({ + retries: 3, + silent: false, + services: [ + { service: new GridProxyMonitor(), URLs: gridproxy }, + { service: new GraphQLMonitor(), URLs: graphql }, + { + service: new RMBMonitor({ mnemonics, chainUrl: "wss://tfchain.02.dev.grid.tf/ws", keypairType: "sr25519" }), + URLs: rmb, + }, + { service: new TFChainMonitor(), URLs: tfChain }, + ], +}); diff --git a/packages/monitoring/package.json b/packages/monitoring/package.json index a09cef8ccc..c342f3edb2 100644 --- a/packages/monitoring/package.json +++ b/packages/monitoring/package.json @@ -14,6 +14,7 @@ "/dist" ], "scripts": { + "example": "yarn run ts-node --project tsconfig-node.json", "build": "npm-run-all es6-build node-build", "node-build": "tsc --build tsconfig-node.json", "es6-build": "tsc --build tsconfig-es6.json", From 25b29c4c8a53d1a74f891a4d802afa2b9e7eae32 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 18 Jul 2024 08:00:42 +0300 Subject: [PATCH 11/58] Fix: remove error on getting Url before intialization and make it return undefined or emptystring remove debug lines --- packages/monitoring/src/serviceMonitor/activations.ts | 1 - packages/monitoring/src/serviceMonitor/graphql.ts | 1 - packages/monitoring/src/serviceMonitor/gridproxy.ts | 1 - packages/monitoring/src/serviceMonitor/rmb.ts | 3 +-- packages/monitoring/src/serviceMonitor/serviceURLManager.ts | 2 -- packages/monitoring/src/serviceMonitor/stats.ts | 1 - packages/monitoring/src/serviceMonitor/tfChain.ts | 1 - 7 files changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/activations.ts b/packages/monitoring/src/serviceMonitor/activations.ts index cf56becbd6..23a1709c3b 100644 --- a/packages/monitoring/src/serviceMonitor/activations.ts +++ b/packages/monitoring/src/serviceMonitor/activations.ts @@ -11,7 +11,6 @@ export class ActivationMonitor implements ILivenessChecker { return this.name; } public get URL() { - if (!this.url) throw new Error("Can't access before initialization"); return this.url; } private set URL(url: string) { diff --git a/packages/monitoring/src/serviceMonitor/graphql.ts b/packages/monitoring/src/serviceMonitor/graphql.ts index 1f48cdd54c..b41475b25b 100644 --- a/packages/monitoring/src/serviceMonitor/graphql.ts +++ b/packages/monitoring/src/serviceMonitor/graphql.ts @@ -11,7 +11,6 @@ export class GraphQLMonitor implements ILivenessChecker { return this.name; } public get URL() { - if (!this.url) throw new Error("Can't access before initialization"); return this.url; } private set URL(url: string) { diff --git a/packages/monitoring/src/serviceMonitor/gridproxy.ts b/packages/monitoring/src/serviceMonitor/gridproxy.ts index 1762db2981..00535b2063 100644 --- a/packages/monitoring/src/serviceMonitor/gridproxy.ts +++ b/packages/monitoring/src/serviceMonitor/gridproxy.ts @@ -11,7 +11,6 @@ export class GridProxyMonitor implements ILivenessChecker { return this.name; } public get URL() { - if (!this.url) throw new Error("Can't access before initialization"); return this.url; } private set URL(url: string) { diff --git a/packages/monitoring/src/serviceMonitor/rmb.ts b/packages/monitoring/src/serviceMonitor/rmb.ts index 0f999dc6e9..802b1e34b0 100644 --- a/packages/monitoring/src/serviceMonitor/rmb.ts +++ b/packages/monitoring/src/serviceMonitor/rmb.ts @@ -28,8 +28,7 @@ export class RMBMonitor implements ILivenessChecker, IDisconnectHandler { return this.name; } public get URL() { - if (!this.RMBProps.relayUrl) throw new Error("Can't access before initialization"); - return this.RMBProps.relayUrl; + return this.RMBProps.relayUrl ?? ""; } private set URL(url: string) { this.RMBProps.relayUrl = url; diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index 4ef386a07b..3fb9c17eb2 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -18,9 +18,7 @@ export class ServiceUrlManager { public services: Service[]; constructor(options: URLManagerOptions) { - console.log(this); Object.assign(this, options); - console.log(this); } /** diff --git a/packages/monitoring/src/serviceMonitor/stats.ts b/packages/monitoring/src/serviceMonitor/stats.ts index 6c580eccd7..10fee6c501 100644 --- a/packages/monitoring/src/serviceMonitor/stats.ts +++ b/packages/monitoring/src/serviceMonitor/stats.ts @@ -11,7 +11,6 @@ export class StatsMonitor implements ILivenessChecker { return this.name; } public get URL() { - if (!this.url) throw new Error("Can't access before initialization"); return this.url; } private set URL(url: string) { diff --git a/packages/monitoring/src/serviceMonitor/tfChain.ts b/packages/monitoring/src/serviceMonitor/tfChain.ts index 52a2d37951..c498040f7e 100644 --- a/packages/monitoring/src/serviceMonitor/tfChain.ts +++ b/packages/monitoring/src/serviceMonitor/tfChain.ts @@ -20,7 +20,6 @@ export class TFChainMonitor implements ILivenessChecker, IDisconnectHandler { return this.name; } public get URL() { - if (!this.url) throw new Error("Can't access before initialization"); return this.url; } private set URL(url: string) { From 1dd5d714cd5f15e440100ccbf86c665ce447e702 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 18 Jul 2024 14:12:45 +0300 Subject: [PATCH 12/58] Chore: RMB handle error event --- packages/rmb_direct_client/lib/client.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/rmb_direct_client/lib/client.ts b/packages/rmb_direct_client/lib/client.ts index 16ea6b37e1..35386f0905 100644 --- a/packages/rmb_direct_client/lib/client.ts +++ b/packages/rmb_direct_client/lib/client.ts @@ -86,6 +86,7 @@ class Client { } else { this.con = new WebSocket(this.updateUrl()) as unknown as WSConnection; } + this.con.on("error", () => console.error); this.con.onmessage = async (e: any) => { let data: Uint8Array = e.data; if (!this.isEnvNode()) { From aa6b6cf6d29084dc991bcd30512be0bf53c25d7c Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 18 Jul 2024 14:13:35 +0300 Subject: [PATCH 13/58] Example: add template type to support silent --- packages/monitoring/example/serviceURLManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/monitoring/example/serviceURLManager.ts b/packages/monitoring/example/serviceURLManager.ts index ff514f0483..def7e3abdb 100644 --- a/packages/monitoring/example/serviceURLManager.ts +++ b/packages/monitoring/example/serviceURLManager.ts @@ -13,7 +13,7 @@ const graphql = ["https://graphql.dev.grid.tf/graphql"]; const tfChain = ["wss://tfchain.dev.grid.tf/ws", "wss://www.nonExistentChain.com"]; const mnemonics = ""; -async function checkStacksAvailability(services: URLManagerOptions) { +async function checkStacksAvailability(services: URLManagerOptions) { try { const pickedUrls = await new ServiceUrlManager(services).getAvailableServicesStack(); console.log(pickedUrls); From 6dde916aea5e4943b6c0c55eee483010ea85947a Mon Sep 17 00:00:00 2001 From: kassem Date: Sun, 21 Jul 2024 15:55:27 +0300 Subject: [PATCH 14/58] Chore: - change naming - remove error on get url and return empty string in case the url is not set yet --- .../src/serviceMonitor/activations.ts | 18 ++++---- .../src/serviceMonitor/alivenessChecker.ts | 6 +-- .../monitoring/src/serviceMonitor/graphql.ts | 20 ++++----- .../src/serviceMonitor/gridproxy.ts | 18 ++++---- packages/monitoring/src/serviceMonitor/rmb.ts | 42 +++++++++---------- .../src/serviceMonitor/serviceURLManager.ts | 12 +++--- .../monitoring/src/serviceMonitor/stats.ts | 18 ++++---- .../monitoring/src/serviceMonitor/tfChain.ts | 38 ++++++++--------- packages/monitoring/src/types/index.ts | 4 +- 9 files changed, 88 insertions(+), 88 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/activations.ts b/packages/monitoring/src/serviceMonitor/activations.ts index 23a1709c3b..cbe56878c1 100644 --- a/packages/monitoring/src/serviceMonitor/activations.ts +++ b/packages/monitoring/src/serviceMonitor/activations.ts @@ -2,22 +2,22 @@ import { resolveServiceStatus, sendRequest } from "../helpers/utils"; import { ILivenessChecker, ServiceStatus } from "../types"; export class ActivationMonitor implements ILivenessChecker { - private readonly name = "Activation"; - private url: string; + private readonly _name = "Activation"; + private _url: string; constructor(activationServiceUrl?: string) { if (activationServiceUrl) this.url = activationServiceUrl; } - public get Name() { - return this.name; + public get name() { + return this._name; } - public get URL() { - return this.url; + public get url() { + return this._url ?? ""; } - private set URL(url: string) { - this.url = url; + private set url(url: string) { + this._url = url; } public update(param: { url: string }): void { - this.URL = param.url; + this.url = param.url; } async isAlive(): Promise { return resolveServiceStatus(sendRequest(this.url, { method: "Get" })); diff --git a/packages/monitoring/src/serviceMonitor/alivenessChecker.ts b/packages/monitoring/src/serviceMonitor/alivenessChecker.ts index 15f942ebf9..603c2818df 100644 --- a/packages/monitoring/src/serviceMonitor/alivenessChecker.ts +++ b/packages/monitoring/src/serviceMonitor/alivenessChecker.ts @@ -23,13 +23,13 @@ export class ServiceMonitor { for (let retryCount = 1; retryCount <= this.retries; retryCount++) { const { alive, error } = await service.isAlive(); if (alive) { - monitorEvents.storeStatus(service.Name, alive); + monitorEvents.storeStatus(service.name, alive); break; } if (retryCount < this.retries) { - monitorEvents.log(`${service.Name} seems to be down; Retrying (${retryCount}/${this.retries})...`); + monitorEvents.log(`${service.name} seems to be down; Retrying (${retryCount}/${this.retries})...`); await new Promise(resolve => setTimeout(resolve, this.retryInterval * 60)); - } else monitorEvents.serviceDown(service.Name, error); + } else monitorEvents.serviceDown(service.name, error); } } monitorEvents.summarize(); diff --git a/packages/monitoring/src/serviceMonitor/graphql.ts b/packages/monitoring/src/serviceMonitor/graphql.ts index b41475b25b..7f75295292 100644 --- a/packages/monitoring/src/serviceMonitor/graphql.ts +++ b/packages/monitoring/src/serviceMonitor/graphql.ts @@ -2,22 +2,22 @@ import { resolveServiceStatus, sendRequest } from "../helpers/utils"; import { ILivenessChecker, ServiceStatus } from "../types"; export class GraphQLMonitor implements ILivenessChecker { - private readonly name = "GraphQl"; - private url: string; + private readonly _name = "GraphQl"; + private _url: string; constructor(graphQlUrl?: string) { - if (graphQlUrl) this.url = graphQlUrl; + if (graphQlUrl) this._url = graphQlUrl; } - public get Name() { - return this.name; + public get name() { + return this._name; } - public get URL() { - return this.url; + public get url() { + return this._url ?? ""; } - private set URL(url: string) { - this.url = url; + private set url(url: string) { + this._url = url; } public update(param: { url: string }) { - this.URL = param.url; + this._url = param.url; } async isAlive(): Promise { diff --git a/packages/monitoring/src/serviceMonitor/gridproxy.ts b/packages/monitoring/src/serviceMonitor/gridproxy.ts index 00535b2063..eb0fd219b6 100644 --- a/packages/monitoring/src/serviceMonitor/gridproxy.ts +++ b/packages/monitoring/src/serviceMonitor/gridproxy.ts @@ -2,22 +2,22 @@ import { resolveServiceStatus, sendRequest } from "../helpers/utils"; import { ILivenessChecker, ServiceStatus } from "../types"; export class GridProxyMonitor implements ILivenessChecker { - private readonly name = "GridProxy"; - private url: string; + private readonly _name = "GridProxy"; + private _url: string; constructor(gridProxyUrl?: string) { if (gridProxyUrl) this.url = gridProxyUrl; } - public get Name() { - return this.name; + public get name() { + return this._name; } - public get URL() { - return this.url; + public get url() { + return this._url ?? ""; } - private set URL(url: string) { - this.url = url; + private set url(url: string) { + this._url = url; } public update(param: { url: string }): void { - this.URL = param.url; + this._url = param.url; } async isAlive(): Promise { return resolveServiceStatus(sendRequest(this.url, { method: "Get" })); diff --git a/packages/monitoring/src/serviceMonitor/rmb.ts b/packages/monitoring/src/serviceMonitor/rmb.ts index 802b1e34b0..53471d8917 100644 --- a/packages/monitoring/src/serviceMonitor/rmb.ts +++ b/packages/monitoring/src/serviceMonitor/rmb.ts @@ -4,54 +4,54 @@ import { generateString, resolveServiceStatus } from "../helpers/utils"; import { IDisconnectHandler, ILivenessChecker, RMBProps, ServiceStatus } from "../types"; export class RMBMonitor implements ILivenessChecker, IDisconnectHandler { - private name = "RMB"; - private rmbClient: RMBClient; - constructor(private RMBProps: RMBProps) { - if (RMBProps) this.factory(); + private _name = "RMB"; + private _rmbClient: RMBClient; + constructor(private _rmbProps: RMBProps) { + if (_rmbProps) this.factory(); } /* Creates a new RMB Client instance */ private factory() { - const { chainUrl, relayUrl, mnemonics, keypairType } = this.RMBProps; + const { chainUrl, relayUrl, mnemonics, keypairType } = this._rmbProps; if (relayUrl) { - this.rmbClient = new RMBClient(chainUrl, relayUrl, mnemonics, generateString(10), keypairType, 0); + this._rmbClient = new RMBClient(chainUrl, relayUrl, mnemonics, generateString(10), keypairType, 0); } } private async setUp() { - if (!this.rmbClient) throw new Error("Can't setUp before initialization"); - await this.rmbClient.connect(); + if (!this._rmbClient) throw new Error("Can't setUp before initialization"); + await this._rmbClient?.connect(); } - public get Name() { - return this.name; + public get name() { + return this._name; } - public get URL() { - return this.RMBProps.relayUrl ?? ""; + public get url() { + return this._rmbProps?.relayUrl ?? ""; } - private set URL(url: string) { - this.RMBProps.relayUrl = url; + private set url(url: string) { + this._rmbProps.relayUrl = url; } public async update(param: { url: string }) { - if (this.URL === param.url) return; - this.URL = param.url; - if (this.rmbClient) { + if (this.url === param.url) return; + this.url = param.url; + if (this._rmbClient) { await this.disconnect(); - this.rmbClient.relayUrl = this.URL; + this._rmbClient.relayUrl = this.url; } else this.factory(); } public async isAlive(): Promise { try { - if (!this.rmbClient?.con?.OPEN) await this.setUp(); - return resolveServiceStatus(this.rmbClient.ping(2)); + if (!this._rmbClient?.con?.OPEN) await this.setUp(); + return resolveServiceStatus(this._rmbClient.ping(2)); } catch (error) { return { alive: false, error }; } } public async disconnect() { - await this.rmbClient.disconnect(); + await this._rmbClient.disconnect(); } } diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index 3fb9c17eb2..8ea96569da 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -77,28 +77,28 @@ export class ServiceUrlManager { let error: Error | string = ""; for (let i = 0; i < urls.length; i++) { await service.update({ url: urls[i] }); - monitorEvents.log(`${service.Name}: pinging ${service.URL}`, "gray"); + monitorEvents.log(`${service.name}: pinging ${service.url}`, "gray"); for (let retry = 0; retry < this.retries; retry++) { const status = await this.pingService(service); if (status.alive) { - monitorEvents.log(`${service.Name} on ${service.URL} Success!`, "green"); - return service.URL; + monitorEvents.log(`${service.name} on ${service.url} Success!`, "green"); + return service.url; } error = status.error ?? ""; } monitorEvents.log( - `${service.Name}: failed to ping ${service.URL} after ${this.retries} retries; ${error}`, + `${service.name}: failed to ping ${service.url} after ${this.retries} retries; ${error}`, "red", ); } - return this.handleErrorsOnSilentMode(`Failed to reach ${service.Name} on all provided stacks`) as ServiceUrl; + return this.handleErrorsOnSilentMode(`Failed to reach ${service.name} on all provided stacks`) as ServiceUrl; } async getAvailableServicesStack(): Promise<{ [key: string]: ServiceUrl }> { const result: { [key: string]: Promise> } = {}; for (const { URLs, service } of this.services) { - result[service.Name] = this.getAvailableStack(URLs, service); + result[service.name] = this.getAvailableStack(URLs, service); } const entries = Object.entries(result); diff --git a/packages/monitoring/src/serviceMonitor/stats.ts b/packages/monitoring/src/serviceMonitor/stats.ts index 10fee6c501..82821c99d4 100644 --- a/packages/monitoring/src/serviceMonitor/stats.ts +++ b/packages/monitoring/src/serviceMonitor/stats.ts @@ -2,22 +2,22 @@ import { resolveServiceStatus, sendRequest } from "../helpers/utils"; import { ILivenessChecker, ServiceStatus } from "../types"; export class StatsMonitor implements ILivenessChecker { - private readonly name = "Stats"; - private url: string; + private readonly _name = "Stats"; + private _url: string; constructor(statusUrl?: string) { if (statusUrl) this.url = statusUrl; } - public get Name() { - return this.name; + public get name() { + return this._name; } - public get URL() { - return this.url; + public get url() { + return this._url ?? ""; } - private set URL(url: string) { - this.url = url; + private set url(url: string) { + this._url = url; } public update(param: { url: string }): void { - this.URL = param.url; + this.url = param.url; } async isAlive(): Promise { return resolveServiceStatus(sendRequest(this.url, { method: "Get" })); diff --git a/packages/monitoring/src/serviceMonitor/tfChain.ts b/packages/monitoring/src/serviceMonitor/tfChain.ts index c498040f7e..8f821efc28 100644 --- a/packages/monitoring/src/serviceMonitor/tfChain.ts +++ b/packages/monitoring/src/serviceMonitor/tfChain.ts @@ -3,36 +3,36 @@ import { QueryClient } from "@threefold/tfchain_client"; import { IDisconnectHandler, ILivenessChecker, ServiceStatus } from "../types"; export class TFChainMonitor implements ILivenessChecker, IDisconnectHandler { - private name = "TFChain"; - private url: string; - private tfClient: QueryClient; - constructor(tfChainUrl?: string) { + private _name = "TFChain"; + private _url: string; + private _tfClient: QueryClient; + constructor(tfChainUrl: string) { if (tfChainUrl) { - this.url = tfChainUrl; - this.tfClient = new QueryClient(this.url); + this._url = tfChainUrl; + this._tfClient = new QueryClient(this.url); } } private async setUp() { - if (!this.tfClient) throw new Error("Can't setUp before initialization"); - await this.tfClient?.connect(); + if (!this._tfClient) throw new Error("Can't setUp before initialization"); + await this._tfClient?.connect(); } - public get Name() { - return this.name; + public get name() { + return this._name; } - public get URL() { - return this.url; + public get url() { + return this._url ?? ""; } - private set URL(url: string) { - this.url = url; + private set url(url: string) { + this._url = url; } public update(param: { url: string }): void { - if (this.URL === param.url) return; - this.URL = param.url; - this.tfClient = new QueryClient(this.url); + if (this.url === param.url) return; + this.url = param.url; + this._tfClient = new QueryClient(this.url); } public async isAlive(): Promise { try { - if (!this.tfClient.api) await this.setUp(); + if (!this._tfClient.api) await this.setUp(); return { alive: true, }; @@ -44,6 +44,6 @@ export class TFChainMonitor implements ILivenessChecker, IDisconnectHandler { } } public async disconnect() { - await this.tfClient.disconnect(); + await this._tfClient.disconnect(); } } diff --git a/packages/monitoring/src/types/index.ts b/packages/monitoring/src/types/index.ts index 60b87538b6..90d2265703 100644 --- a/packages/monitoring/src/types/index.ts +++ b/packages/monitoring/src/types/index.ts @@ -7,12 +7,12 @@ interface IServiceBase

{ /** * The name of the service. */ - Name: string; + name: string; /** * The URL of the service. */ - URL: string; + url: string; /** * Updates the service with the provided parameters. From 1542e471e7e4e34bb6c8ce87563fc33a53432398 Mon Sep 17 00:00:00 2001 From: kassem Date: Sun, 21 Jul 2024 15:58:02 +0300 Subject: [PATCH 15/58] Chore: - add errror handling in isAlive, will throw in case the service url is not set --- packages/monitoring/src/serviceMonitor/activations.ts | 1 + packages/monitoring/src/serviceMonitor/graphql.ts | 1 + packages/monitoring/src/serviceMonitor/gridproxy.ts | 1 + packages/monitoring/src/serviceMonitor/rmb.ts | 1 + packages/monitoring/src/serviceMonitor/stats.ts | 1 + packages/monitoring/src/serviceMonitor/tfChain.ts | 1 + 6 files changed, 6 insertions(+) diff --git a/packages/monitoring/src/serviceMonitor/activations.ts b/packages/monitoring/src/serviceMonitor/activations.ts index cbe56878c1..54325310c7 100644 --- a/packages/monitoring/src/serviceMonitor/activations.ts +++ b/packages/monitoring/src/serviceMonitor/activations.ts @@ -20,6 +20,7 @@ export class ActivationMonitor implements ILivenessChecker { this.url = param.url; } async isAlive(): Promise { + if (!this.url) throw new Error("Can't access before initialization"); return resolveServiceStatus(sendRequest(this.url, { method: "Get" })); } } diff --git a/packages/monitoring/src/serviceMonitor/graphql.ts b/packages/monitoring/src/serviceMonitor/graphql.ts index 7f75295292..73e6e535e4 100644 --- a/packages/monitoring/src/serviceMonitor/graphql.ts +++ b/packages/monitoring/src/serviceMonitor/graphql.ts @@ -21,6 +21,7 @@ export class GraphQLMonitor implements ILivenessChecker { } async isAlive(): Promise { + if (!this.url) throw new Error("Can't access before initialization"); return resolveServiceStatus( sendRequest(this.url, { method: "POST", diff --git a/packages/monitoring/src/serviceMonitor/gridproxy.ts b/packages/monitoring/src/serviceMonitor/gridproxy.ts index eb0fd219b6..f111b96236 100644 --- a/packages/monitoring/src/serviceMonitor/gridproxy.ts +++ b/packages/monitoring/src/serviceMonitor/gridproxy.ts @@ -20,6 +20,7 @@ export class GridProxyMonitor implements ILivenessChecker { this._url = param.url; } async isAlive(): Promise { + if (!this.url) throw new Error("Can't access before initialization"); return resolveServiceStatus(sendRequest(this.url, { method: "Get" })); } } diff --git a/packages/monitoring/src/serviceMonitor/rmb.ts b/packages/monitoring/src/serviceMonitor/rmb.ts index 53471d8917..aff1aea519 100644 --- a/packages/monitoring/src/serviceMonitor/rmb.ts +++ b/packages/monitoring/src/serviceMonitor/rmb.ts @@ -44,6 +44,7 @@ export class RMBMonitor implements ILivenessChecker, IDisconnectHandler { } public async isAlive(): Promise { + if (!this.url) throw new Error("Can't access before initialization"); try { if (!this._rmbClient?.con?.OPEN) await this.setUp(); return resolveServiceStatus(this._rmbClient.ping(2)); diff --git a/packages/monitoring/src/serviceMonitor/stats.ts b/packages/monitoring/src/serviceMonitor/stats.ts index 82821c99d4..b2c810cdf5 100644 --- a/packages/monitoring/src/serviceMonitor/stats.ts +++ b/packages/monitoring/src/serviceMonitor/stats.ts @@ -20,6 +20,7 @@ export class StatsMonitor implements ILivenessChecker { this.url = param.url; } async isAlive(): Promise { + if (!this.url) throw new Error("Can't access before initialization"); return resolveServiceStatus(sendRequest(this.url, { method: "Get" })); } } diff --git a/packages/monitoring/src/serviceMonitor/tfChain.ts b/packages/monitoring/src/serviceMonitor/tfChain.ts index 8f821efc28..e2ebffda65 100644 --- a/packages/monitoring/src/serviceMonitor/tfChain.ts +++ b/packages/monitoring/src/serviceMonitor/tfChain.ts @@ -31,6 +31,7 @@ export class TFChainMonitor implements ILivenessChecker, IDisconnectHandler { this._tfClient = new QueryClient(this.url); } public async isAlive(): Promise { + if (!this.url) throw new Error("Can't access before initialization"); try { if (!this._tfClient.api) await this.setUp(); return { From ff29b60904057d000f1b80ded1dc23f982d29c1c Mon Sep 17 00:00:00 2001 From: kassem Date: Tue, 23 Jul 2024 14:35:50 +0300 Subject: [PATCH 16/58] chore: export new services --- packages/monitoring/src/serviceMonitor/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/monitoring/src/serviceMonitor/index.ts b/packages/monitoring/src/serviceMonitor/index.ts index 5abade0125..bba43fa152 100644 --- a/packages/monitoring/src/serviceMonitor/index.ts +++ b/packages/monitoring/src/serviceMonitor/index.ts @@ -3,4 +3,6 @@ export { TFChainMonitor } from "./tfChain"; export { RMBMonitor } from "./rmb"; export { ServiceMonitor } from "./alivenessChecker"; export { GraphQLMonitor } from "./graphql"; +export { StatsMonitor } from "./stats"; +export { ActivationMonitor } from "./activations"; export { ServiceUrlManager } from "./serviceURLManager"; From d57c608b77b7c19f13b743ca81de3bd6adc6bed6 Mon Sep 17 00:00:00 2001 From: Omar Kassem Date: Tue, 23 Jul 2024 14:47:12 +0300 Subject: [PATCH 17/58] Chore: make tfchain url optional --- packages/monitoring/src/serviceMonitor/tfChain.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/monitoring/src/serviceMonitor/tfChain.ts b/packages/monitoring/src/serviceMonitor/tfChain.ts index e2ebffda65..f4ebae62d6 100644 --- a/packages/monitoring/src/serviceMonitor/tfChain.ts +++ b/packages/monitoring/src/serviceMonitor/tfChain.ts @@ -6,7 +6,7 @@ export class TFChainMonitor implements ILivenessChecker, IDisconnectHandler { private _name = "TFChain"; private _url: string; private _tfClient: QueryClient; - constructor(tfChainUrl: string) { + constructor(tfChainUrl?: string) { if (tfChainUrl) { this._url = tfChainUrl; this._tfClient = new QueryClient(this.url); From 64771401de501199d9fca3460d07bba2e9c5fb7a Mon Sep 17 00:00:00 2001 From: kassem Date: Tue, 23 Jul 2024 17:51:43 +0300 Subject: [PATCH 18/58] chore: change stats url to api/summary --- packages/monitoring/src/serviceMonitor/stats.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/monitoring/src/serviceMonitor/stats.ts b/packages/monitoring/src/serviceMonitor/stats.ts index b2c810cdf5..9c3a4fa4df 100644 --- a/packages/monitoring/src/serviceMonitor/stats.ts +++ b/packages/monitoring/src/serviceMonitor/stats.ts @@ -21,6 +21,6 @@ export class StatsMonitor implements ILivenessChecker { } async isAlive(): Promise { if (!this.url) throw new Error("Can't access before initialization"); - return resolveServiceStatus(sendRequest(this.url, { method: "Get" })); + return resolveServiceStatus(sendRequest(`${this.url}/api/stats-summary`, { method: "Get" })); } } From 9768212dc574578f453e67ae39630e5b573202e5 Mon Sep 17 00:00:00 2001 From: kassem Date: Wed, 24 Jul 2024 18:34:48 +0300 Subject: [PATCH 19/58] feat: Support array of stacks in build env --- packages/playground/scripts/build-env.sh | 75 ++++++++++++++---------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/packages/playground/scripts/build-env.sh b/packages/playground/scripts/build-env.sh index 4043795563..ab79ab0130 100644 --- a/packages/playground/scripts/build-env.sh +++ b/packages/playground/scripts/build-env.sh @@ -15,43 +15,43 @@ STELLAR_ENV_Vars=( case $MODE in "dev") - GRAPHQL_URL="${GRAPHQL_URL:-https://graphql.dev.grid.tf/graphql}" - GRIDPROXY_URL="${GRIDPROXY_URL:-https://gridproxy.dev.grid.tf}" - SUBSTRATE_URL="${SUBSTRATE_URL:-wss://tfchain.dev.grid.tf/ws}" - ACTIVATION_SERVICE_URL="${ACTIVATION_SERVICE_URL:-https://activation.dev.grid.tf/activation/activate}" - RELAY_DOMAIN="${RELAY_DOMAIN:-wss://relay.dev.grid.tf}" + GRAPHQL_URL="${GRAPHQL_URL:-"https://graphql.dev.grid.tf/graphql,https://graphql.02.dev.grid.tf/graphql"}" + GRIDPROXY_URL="${GRIDPROXY_URL:-"https://gridproxy.dev.grid.tf,https://gridproxy.02.dev.grid.tf"}" + SUBSTRATE_URL="${SUBSTRATE_URL:-"wss://tfchain.dev.grid.tf/ws,wss://tfchain.02.dev.grid.tf/ws"}" + ACTIVATION_SERVICE_URL="${ACTIVATION_SERVICE_URL:-"https://activation.dev.grid.tf/activation/activate,https://activation.02.dev.grid.tf/activation/activate"}" + RELAY_DOMAIN="${RELAY_DOMAIN:-"wss://relay.dev.grid.tf,wss://relay.02.dev.grid.tf"}" BRIDGE_TFT_ADDRESS="${BRIDGE_TFT_ADDRESS:-GDHJP6TF3UXYXTNEZ2P36J5FH7W4BJJQ4AYYAXC66I2Q2AH5B6O6BCFG}" - STATS_URL="${STATS_URL:-https://stats.dev.grid.tf}" + STATS_URL="${STATS_URL:-"https://stats.dev.grid.tf,https://stats.02.dev.grid.tf"}" STELLAR_NETWORK="${STELLAR_NETWORK:-test}" ;; "qa") - GRAPHQL_URL="${GRAPHQL_URL:-https://graphql.qa.grid.tf/graphql}" - GRIDPROXY_URL="${GRIDPROXY_URL:-https://gridproxy.qa.grid.tf}" - SUBSTRATE_URL="${SUBSTRATE_URL:-wss://tfchain.qa.grid.tf/ws}" - ACTIVATION_SERVICE_URL="${ACTIVATION_SERVICE_URL:-https://activation.qa.grid.tf/activation/activate}" - RELAY_DOMAIN="${RELAY_DOMAIN:-wss://relay.qa.grid.tf}" + GRAPHQL_URL="${GRAPHQL_URL:-"https://graphql.qa.grid.tf/graphql,https://graphql.02.qa.grid.tf/graphql"}" + GRIDPROXY_URL="${GRIDPROXY_URL:-"https://gridproxy.qa.grid.tf,https://gridproxy.02.qa.grid.tf"}" + SUBSTRATE_URL="${SUBSTRATE_URL:-"wss://tfchain.qa.grid.tf/ws,wss://tfchain.02.qa.grid.tf/ws"}" + ACTIVATION_SERVICE_URL="${ACTIVATION_SERVICE_URL:-"https://activation.qa.grid.tf/activation/activate,https://activation.02.qa.grid.tf/activation/activate"}" + RELAY_DOMAIN="${RELAY_DOMAIN:-"wss://relay.qa.grid.tf,wss://relay.02.qa.grid.tf"}" BRIDGE_TFT_ADDRESS="${BRIDGE_TFT_ADDRESS:-GAQH7XXFBRWXT2SBK6AHPOLXDCLXVFAKFSOJIRMRNCDINWKHGI6UYVKM}" - STATS_URL="${STATS_URL:-https://stats.qa.grid.tf}" + STATS_URL="${STATS_URL:-"https://stats.qa.grid.tf,https://stats.02.qa.grid.tf"}" STELLAR_NETWORK="${STELLAR_NETWORK:-test}" ;; "test") - GRAPHQL_URL="${GRAPHQL_URL:-https://graphql.test.grid.tf/graphql}" - GRIDPROXY_URL="${GRIDPROXY_URL:-https://gridproxy.test.grid.tf}" - SUBSTRATE_URL="${SUBSTRATE_URL:-wss://tfchain.test.grid.tf/ws}" - ACTIVATION_SERVICE_URL="${ACTIVATION_SERVICE_URL:-https://activation.test.grid.tf/activation/activate}" - RELAY_DOMAIN="${RELAY_DOMAIN:-wss://relay.test.grid.tf}" + GRAPHQL_URL="${GRAPHQL_URL:-"https://graphql.test.grid.tf/graphql,https://graphql.02.test.grid.tf/graphql"}" + GRIDPROXY_URL="${GRIDPROXY_URL:-"https://gridproxy.test.grid.tf,https://gridproxy.02.test.grid.tf"}" + SUBSTRATE_URL="${SUBSTRATE_URL:-"wss://tfchain.test.grid.tf/ws,wss://tfchain.02.test.grid.tf/ws"}" + ACTIVATION_SERVICE_URL="${ACTIVATION_SERVICE_URL:-"https://activation.test.grid.tf/activation/activate,https://activation.02.test.grid.tf/activation/activate"}" + RELAY_DOMAIN="${RELAY_DOMAIN:-"wss://relay.test.grid.tf,wss://relay.02.test.grid.tf"}" BRIDGE_TFT_ADDRESS="${BRIDGE_TFT_ADDRESS:-GA2CWNBUHX7NZ3B5GR4I23FMU7VY5RPA77IUJTIXTTTGKYSKDSV6LUA4}" - STATS_URL="${STATS_URL:-https://stats.test.grid.tf}" + STATS_URL="${STATS_URL:-"https://stats.test.grid.tf,https://stats.02.test.grid.tf"}" STELLAR_NETWORK="${STELLAR_NETWORK:-main}" ;; "main") - GRAPHQL_URL="${GRAPHQL_URL:-https://graphql.grid.tf/graphql}" - GRIDPROXY_URL="${GRIDPROXY_URL:-https://gridproxy.grid.tf}" - SUBSTRATE_URL="${SUBSTRATE_URL:-wss://tfchain.grid.tf/ws}" - ACTIVATION_SERVICE_URL="${ACTIVATION_SERVICE_URL:-https://activation.grid.tf/activation/activate}" - RELAY_DOMAIN="${RELAY_DOMAIN:-wss://relay.grid.tf}" + GRAPHQL_URL="${GRAPHQL_URL:-"https://graphql.grid.tf/graphql,https://graphql.02.grid.tf/graphql"}" + GRIDPROXY_URL="${GRIDPROXY_URL:-"https://gridproxy.grid.tf,https://gridproxy.02.grid.tf"}" + SUBSTRATE_URL="${SUBSTRATE_URL:-"wss://tfchain.grid.tf/ws,wss://tfchain.02.grid.tf/ws"}" + ACTIVATION_SERVICE_URL="${ACTIVATION_SERVICE_URL:-"https://activation.grid.tf/activation/activate,https://activation.02.grid.tf/activation/activate"}" + RELAY_DOMAIN="${RELAY_DOMAIN:-"wss://relay.grid.tf,wss://relay.02.grid.tf"}" BRIDGE_TFT_ADDRESS="${BRIDGE_TFT_ADDRESS:-GBNOTAYUMXVO5QDYWYO2SOCOYIJ3XFIP65GKOQN7H65ZZSO6BK4SLWSC}" - STATS_URL="${STATS_URL:-https://stats.grid.tf}" + STATS_URL="${STATS_URL:-"https://stats.grid.tf,https://stats.02.grid.tf"}" STELLAR_NETWORK="${STELLAR_NETWORK:-main}" ;; *) @@ -77,20 +77,35 @@ case $STELLAR_NETWORK in ;; esac +parss_array(){ + local service_urls=$1 + toString=($(echo $service_urls | tr ',' "\n")) + for item in "${toString[@]}"; do + quoted_string+="'$item' " + done + + # add single quate to each elament + quoted_string=${quoted_string// /,} + + # remove trailing comma + echo "$quoted_string" | sed 's/.$//' + +} + configs=" window.env = { NETWORK: '$MODE', - GRAPHQL_URL: '$GRAPHQL_URL', - GRIDPROXY_URL: '$GRIDPROXY_URL', - SUBSTRATE_URL: '$SUBSTRATE_URL', - ACTIVATION_SERVICE_URL: '$ACTIVATION_SERVICE_URL', - RELAY_DOMAIN: '$RELAY_DOMAIN', + GRAPHQL_STACKS: "[$(parss_array $GRAPHQL_URL)]", + GRIDPROXY_STACKS: "[$(parss_array $GRIDPROXY_URL)]", + SUBSTRATE_STACKS: "[$(parss_array $SUBSTRATE_URL)]", + ACTIVATION_SERVICE_STACKS: "[$(parss_array $ACTIVATION_SERVICE_URL)]", + RELAY_STACKS: "[$(parss_array $RELAY_DOMAIN)]", BRIDGE_TFT_ADDRESS: '$BRIDGE_TFT_ADDRESS', STELLAR_NETWORK: '$STELLAR_NETWORK', STELLAR_HORIZON_URL: '$STELLAR_HORIZON_URL', TFT_ASSET_ISSUER: '$TFT_ASSET_ISSUER', MINTING_URL: '$MINTING_URL', - STATS_URL: '$STATS_URL', + STATS_STACKS: "[$(parss_array $STATS_URL)]", TIMEOUT: +'$TIMEOUT', PAGE_SIZE: +'$PAGE_SIZE', MANUAL_URL: '$MANUAL_URL' From add65d25a2d619a5d63bd771c170ea27f12433a1 Mon Sep 17 00:00:00 2001 From: kassem Date: Wed, 24 Jul 2024 18:38:10 +0300 Subject: [PATCH 20/58] fix: prevent indexedDB error --- packages/playground/src/components/logger.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/playground/src/components/logger.vue b/packages/playground/src/components/logger.vue index fd697d46bd..d133779f08 100644 --- a/packages/playground/src/components/logger.vue +++ b/packages/playground/src/components/logger.vue @@ -202,7 +202,7 @@ export default { let _interceptorQueue: LI[] = []; async function interceptMessage(instance: LI) { - if (connectDB.value.error) { + if (connectDB.value.error || !connectDB.value.data) { _interceptorQueue.push(instance); return; } From 8f25d528fb9411f29de9758f8ced17323cf6965f Mon Sep 17 00:00:00 2001 From: kassem Date: Wed, 24 Jul 2024 18:44:30 +0300 Subject: [PATCH 21/58] fix: change dist path to public --- packages/playground/scripts/build-env.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/playground/scripts/build-env.sh b/packages/playground/scripts/build-env.sh index ab79ab0130..d166c9157a 100644 --- a/packages/playground/scripts/build-env.sh +++ b/packages/playground/scripts/build-env.sh @@ -113,7 +113,7 @@ window.env = { " # decide the config file path -[ -d dist ] && file="dist/config.js" || file="config.js" +[ -d public ] && file="public/config.js" || file="config.js" # override the content of the config file & echo the result echo $configs > $file From aecfbd97b6805f6aca60214a72355c51b8cf5dac Mon Sep 17 00:00:00 2001 From: kassem Date: Wed, 24 Jul 2024 18:47:21 +0300 Subject: [PATCH 22/58] feat: - add Monitor elements in html - define services classes and showMonitorErrors - implement showMonitorErrors that change loader dom to show monitor results --- packages/playground/index.html | 7 ++++- packages/playground/public/loader/loader.js | 28 +++++++++++++++++++ .../playground/src/global-components.d.ts | 7 +++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/packages/playground/index.html b/packages/playground/index.html index 72f6262517..67f78846b6 100644 --- a/packages/playground/index.html +++ b/packages/playground/index.html @@ -41,7 +41,12 @@ }); logo - Loading dashboard. Please wait... + Loading dashboard. Please wait... + +

+
    +
+
diff --git a/packages/playground/public/loader/loader.js b/packages/playground/public/loader/loader.js index 69dd8f766b..71e521e429 100644 --- a/packages/playground/public/loader/loader.js +++ b/packages/playground/public/loader/loader.js @@ -10,6 +10,12 @@ const msgElement = document.querySelector(".app-loader-msg"); /** @type { HTMLButtonElement} */ const refreshBtn = document.querySelector(".app-loader-refresh"); +/** @type { HTMLDivElement } */ +const monitor = document.querySelector(".app-monitor-container"); + +/** @type { HTMLUListElement} */ +const monitorList = document.querySelector(".app-monitor-status"); + const slowConnectionTime = 60 * 1000; const noConnectionTime = 120 * 1000; const appLoaderContainerTime = 0.3 * 1000; @@ -58,3 +64,25 @@ window.$$appLoader = () => { } }, welcomeMsgTime); }; +window.$$showMonitorError = urls => { + if (msgElement) msgElement.classList.remove("active"); + + if (monitor) monitor.classList.add("active"); + + const monitorMsgElement = document.querySelector(".app-monitor-msg"); + if (monitorMsgElement) { + monitorMsgElement.classList.add("active"); + monitorMsgElement.textContent = "Can't reach some services on provided stacks, Please try again"; + } + + if (monitorList) { + monitorList.innerHTML = Object.entries(urls).map(createElement).join(" "); + } + refreshBtn && refreshBtn.classList.add("active"); +}; + +function createElement([serviceName, serviceStatus]) { + return `
  • ${serviceName}

    ${serviceStatus !== null ? "✓" : "✗"}

  • `; +} diff --git a/packages/playground/src/global-components.d.ts b/packages/playground/src/global-components.d.ts index 44f7ab4b49..e0e4c1c12c 100644 --- a/packages/playground/src/global-components.d.ts +++ b/packages/playground/src/global-components.d.ts @@ -38,7 +38,14 @@ declare module "@vue/runtime-core" { declare global { interface Window { $$appLoader: () => void; + $$showMonitorError: (urls: { [key: string]: string | null }) => void; env: { + GRAPHQL_STACKS: string[]; + GRIDPROXY_STACKS: string[]; + SUBSTRATE_STACKS: string[]; + ACTIVATION_SERVICE_STACKS: string[]; + RELAY_STACKS: string[]; + STATS_STACKS: string[]; NETWORK: NetworkEnv; GRAPHQL_URL: string; GRIDPROXY_URL: string; From ea1aa4db1380759f4aeb6e74e882b85d786913a0 Mon Sep 17 00:00:00 2001 From: kassem Date: Wed, 24 Jul 2024 18:48:56 +0300 Subject: [PATCH 23/58] Style: Add monitor result style --- packages/playground/public/loader/loader.css | 59 +++++++++++++++++++- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/packages/playground/public/loader/loader.css b/packages/playground/public/loader/loader.css index 28d5167c91..6804bfc581 100644 --- a/packages/playground/public/loader/loader.css +++ b/packages/playground/public/loader/loader.css @@ -23,6 +23,51 @@ margin-top: 30vh; } +.app-monitor-container { + flex-direction: column; + align-items: center; + margin-top: 20px; + display: none; + border: 1px solid #fff; + border-radius: 5px; + padding: 5px 25px; +} +.app-monitor-container.active { + display: flex; +} +.app-monitor-status { + text-align: left; + color: #fff; + margin: 0; + + font-family: sans-serif; + max-width: 500px; + line-height: 1; +} + +.service-name { + color: #fff; + width: 100px; + padding: 2px 0px; + font-family: sans-serif; + line-height: 1; + font-weight: bold; + text-align: left; +} + +.service-status { + display: inline; + font-family: sans-serif; + line-height: 1; + font-weight: bold; + font-size: x-large; +} +.service-reachable { + color: rgb(var(--v-theme-success, "76, 175, 80")); +} +.service-unreachable { + color: rgb(207, 102, 121); +} .app-loader-logo { max-width: 100%; display: block; @@ -86,7 +131,9 @@ } } -.app-loader-msg { +.app-loader-msg, +.app-monitor-msg { + display: none; color: #fff; margin: 0; margin-top: 20px; @@ -95,7 +142,13 @@ line-height: 1; text-align: center; } - +.app-monitor-msg { + display: none; +} +.app-loader-msg.active, +.app-monitor-msg.active { + display: block; +} .app-loader-refresh { border: none; outline: none; @@ -134,4 +187,4 @@ .app-loader-refresh:focus { box-shadow: 0 0 0 2px rgba(115, 221, 195, 0.5); -} +} \ No newline at end of file From 3aae3ffb0a30cd559ce6223e256f751579c20c68 Mon Sep 17 00:00:00 2001 From: kassem Date: Wed, 24 Jul 2024 18:55:34 +0300 Subject: [PATCH 24/58] chore support defineAsyncComponent and use it for some components add setGlobalEnv that check all services stacks and set the available url in env --- packages/playground/src/config.ts | 65 +++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/packages/playground/src/config.ts b/packages/playground/src/config.ts index 0a9c9201e5..8bacc64652 100644 --- a/packages/playground/src/config.ts +++ b/packages/playground/src/config.ts @@ -1,30 +1,37 @@ +import { + ActivationMonitor, + GraphQLMonitor, + GridProxyMonitor, + RMBMonitor, + ServiceUrlManager, + StatsMonitor, + TFChainMonitor, +} from "@threefold/monitoring"; import { marked } from "marked"; -import type { App, Component } from "vue"; +import { type App, type Component, defineAsyncComponent } from "vue"; import CopyInputWrapper from "./components/copy_input_wrapper.vue"; -import DTabs from "./components/dynamic_tabs.vue"; import Filters from "./components/filter.vue"; import FormValidator from "./components/form_validator.vue"; import InputTooltip from "./components/input_tooltip.vue"; import InputValidator from "./components/input_validator.vue"; import TfSelectCountry from "./components/node_selector/select_location_internals/TfSelectCountry.vue"; import TfSelectRegion from "./components/node_selector/select_location_internals/TfSelectRegion.vue"; -import TfSelectionDetails from "./components/node_selector/TfSelectionDetails.vue"; import PasswordInputWrapper from "./components/password_input_wrapper.vue"; import ViewLayout from "./components/view_layout.vue"; -import WebletLayout from "./components/weblet_layout.vue"; import * as validators from "./utils/validators"; + const GLOBAL_COMPONENTS: { [key: string]: Component } = { PasswordInputWrapper, - WebletLayout, + WebletLayout: defineAsyncComponent(() => import("./components/weblet_layout.vue")), CopyInputWrapper, - DTabs, + DTabs: defineAsyncComponent(() => import("./components/dynamic_tabs.vue")), InputValidator, FormValidator, ViewLayout, InputTooltip, Filters, - TfSelectionDetails, + TfSelectionDetails: defineAsyncComponent(() => import("./components/node_selector/TfSelectionDetails.vue")), TfSelectRegion, TfSelectCountry, }; @@ -68,3 +75,47 @@ function defineGlobalProps(app: App) { app.config.globalProperties.validators = validators; app.config.globalProperties.MANUAL_URL = window.env.MANUAL_URL; } + +export async function setGlobalEnv() { + const { GRIDPROXY_STACKS, GRAPHQL_STACKS, STATS_STACKS, RELAY_STACKS, SUBSTRATE_STACKS, ACTIVATION_SERVICE_STACKS } = + window.env; + const urlManger = new ServiceUrlManager({ + services: [ + { URLs: GRIDPROXY_STACKS, service: new GridProxyMonitor() }, + { URLs: GRAPHQL_STACKS, service: new GraphQLMonitor() }, + { URLs: STATS_STACKS, service: new StatsMonitor() }, + { URLs: SUBSTRATE_STACKS, service: new TFChainMonitor() }, + { URLs: ACTIVATION_SERVICE_STACKS, service: new ActivationMonitor() }, + ], + silent: true, + }); + const result = await urlManger.getAvailableServicesStack(); + + // if (result.TFChain) { + // result.RMB = await urlManger.getAvailableStack( + // RELAY_STACKS, + // new RMBMonitor({ + // chainUrl: result.TFChain, + // mnemonics: "", + // keypairType: "sr25519", + // }), + // ); + // } + // result.test = null; + if (Object.values(result).includes(null)) { + window.$$showMonitorError(result); + return false; + } + const { GridProxy, Stats, TFChain, GraphQl, Activation, RMB } = result; + + window.env.GRIDPROXY_URL = GridProxy!; + window.env.STATS_URL = Stats!; + window.env.GRAPHQL_URL = GraphQl!; + window.env.SUBSTRATE_URL = TFChain!; + window.env.ACTIVATION_SERVICE_URL = Activation!; + window.env.RELAY_DOMAIN = "wss://relay.dev.grid.tf"!; + + await new Promise(r => setTimeout(r, 3000)); + + return true; +} From 5359ef25ec0b481aa7fc968615e19f6c69e52f47 Mon Sep 17 00:00:00 2001 From: kassem Date: Wed, 24 Jul 2024 19:05:45 +0300 Subject: [PATCH 25/58] feat: Support Monitroinng on application --- packages/playground/package.json | 1 + packages/playground/public/config.js | 15 +++++++++------ packages/playground/src/Monitor.vue | 26 ++++++++++++++++++++++++++ packages/playground/src/main.ts | 4 ++-- 4 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 packages/playground/src/Monitor.vue diff --git a/packages/playground/package.json b/packages/playground/package.json index fbe7e80dd4..908363beb4 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -15,6 +15,7 @@ "@threefold/graphql_client": "2.5.0", "@threefold/grid_client": "2.5.0", "@threefold/gridproxy_client": "2.5.0", + "@threefold/monitoring": "2.5.0", "@threefold/types": "2.5.0", "@types/ip": "^1.1.3", "@types/md5": "^2.3.5", diff --git a/packages/playground/public/config.js b/packages/playground/public/config.js index cfc777261b..c91ffd6e66 100644 --- a/packages/playground/public/config.js +++ b/packages/playground/public/config.js @@ -1,16 +1,19 @@ window.env = { NETWORK: "dev", - GRAPHQL_URL: "https://graphql.dev.grid.tf/graphql", - GRIDPROXY_URL: "https://gridproxy.dev.grid.tf", - SUBSTRATE_URL: "wss://tfchain.dev.grid.tf/ws", - ACTIVATION_SERVICE_URL: "https://activation.dev.grid.tf/activation/activate", - RELAY_DOMAIN: "wss://relay.dev.grid.tf", + GRAPHQL_STACKS: ["https://graphql.dev.grid.tf/graphql", "https://graphql.02.dev.grid.tf/graphql"], + GRIDPROXY_STACKS: ["https://gridproxy.dev.grid.tf", "https://gridproxy.02.dev.grid.tf"], + SUBSTRATE_STACKS: ["wss://tfchain.dev.grid.tf/ws", "wss://tfchain.02.dev.grid.tf/ws"], + ACTIVATION_SERVICE_STACKS: [ + "https://activation.dev.grid.tf/activation/activate", + "https://activation.02.dev.grid.tf/activation/activate", + ], + RELAY_STACKS: ["wss://relay.dev.grid.tf", "wss://relay.02.dev.grid.tf"], BRIDGE_TFT_ADDRESS: "GDHJP6TF3UXYXTNEZ2P36J5FH7W4BJJQ4AYYAXC66I2Q2AH5B6O6BCFG", STELLAR_NETWORK: "test", STELLAR_HORIZON_URL: "https://horizon-testnet.stellar.org", TFT_ASSET_ISSUER: "GA47YZA3PKFUZMPLQ3B5F2E3CJIB57TGGU7SPCQT2WAEYKN766PWIMB3", MINTING_URL: "https://alpha.minting.tfchain.grid.tf", - STATS_URL: "https://stats.dev.grid.tf", + STATS_STACKS: ["https://stats.dev.grid.tf", "https://stats.02.dev.grid.tf"], TIMEOUT: +"10000", PAGE_SIZE: +"20", MANUAL_URL: "https://www.manual.grid.tf", diff --git a/packages/playground/src/Monitor.vue b/packages/playground/src/Monitor.vue new file mode 100644 index 0000000000..76bbc61e45 --- /dev/null +++ b/packages/playground/src/Monitor.vue @@ -0,0 +1,26 @@ + + + diff --git a/packages/playground/src/main.ts b/packages/playground/src/main.ts index ac76b55e4d..e1ba4a2b65 100644 --- a/packages/playground/src/main.ts +++ b/packages/playground/src/main.ts @@ -6,12 +6,12 @@ import { createApp } from "vue"; import vuetify from "@/plugins/vuetify"; -import App from "./App.vue"; import { defineGlobals } from "./config"; +import Monitor from "./Monitor.vue"; import router from "./router"; import { normalizeError } from "./utils/helpers"; -const app = createApp(App); +const app = createApp(Monitor); app.config.errorHandler = error => { console.error( From 0aaa9d256f67d58c5deb7423af7be286cac3ab85 Mon Sep 17 00:00:00 2001 From: kassem Date: Wed, 24 Jul 2024 19:21:55 +0300 Subject: [PATCH 26/58] chore: add checks to prevent accessing undefined functions in RMB connect and disconnect --- packages/rmb_direct_client/lib/client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rmb_direct_client/lib/client.ts b/packages/rmb_direct_client/lib/client.ts index 35386f0905..c3bd193f69 100644 --- a/packages/rmb_direct_client/lib/client.ts +++ b/packages/rmb_direct_client/lib/client.ts @@ -86,7 +86,7 @@ class Client { } else { this.con = new WebSocket(this.updateUrl()) as unknown as WSConnection; } - this.con.on("error", () => console.error); + if (this.con && this.con.on) this.con.on("error", () => console.error); this.con.onmessage = async (e: any) => { let data: Uint8Array = e.data; if (!this.isEnvNode()) { @@ -208,7 +208,7 @@ class Client { if (this.con?.readyState !== undefined && this.con?.readyState === this.con?.CONNECTING) await this.waitForOpenConnection(); if (this.__pingPongTimeout) clearTimeout(this.__pingPongTimeout); - if (this.con) this.con.removeAllListeners(); + if (this.con && this.con.removeAllListeners) this.con.removeAllListeners(); await this.waitForResponses(); await this.tfclient.disconnect(); if (this.con?.readyState !== this.con?.CLOSED) this.con.close(); From 76bdbc41734ca5eeefceb4933456932db4b7ec0c Mon Sep 17 00:00:00 2001 From: kassem Date: Sun, 28 Jul 2024 14:33:50 +0300 Subject: [PATCH 27/58] chore: validate rmb connectivity though gridproxy health endpoint --- packages/monitoring/src/serviceMonitor/rmb.ts | 69 ++++++++----------- packages/monitoring/src/types/index.ts | 7 -- 2 files changed, 28 insertions(+), 48 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/rmb.ts b/packages/monitoring/src/serviceMonitor/rmb.ts index aff1aea519..ef3b5a8b86 100644 --- a/packages/monitoring/src/serviceMonitor/rmb.ts +++ b/packages/monitoring/src/serviceMonitor/rmb.ts @@ -1,58 +1,45 @@ -import { Client as RMBClient } from "@threefold/rmb_direct_client"; +import { RequestError } from "@threefold/types"; -import { generateString, resolveServiceStatus } from "../helpers/utils"; -import { IDisconnectHandler, ILivenessChecker, RMBProps, ServiceStatus } from "../types"; +import { ILivenessChecker, ServiceStatus } from "../types"; -export class RMBMonitor implements ILivenessChecker, IDisconnectHandler { +export class RMBMonitor implements ILivenessChecker { private _name = "RMB"; - private _rmbClient: RMBClient; - constructor(private _rmbProps: RMBProps) { - if (_rmbProps) this.factory(); - } - - /* - Creates a new RMB Client instance - */ - private factory() { - const { chainUrl, relayUrl, mnemonics, keypairType } = this._rmbProps; - if (relayUrl) { - this._rmbClient = new RMBClient(chainUrl, relayUrl, mnemonics, generateString(10), keypairType, 0); - } - } - - private async setUp() { - if (!this._rmbClient) throw new Error("Can't setUp before initialization"); - await this._rmbClient?.connect(); + private _url: string; + constructor(gridProxyUrl?: string) { + if (gridProxyUrl) this.url = gridProxyUrl; } public get name() { return this._name; } public get url() { - return this._rmbProps?.relayUrl ?? ""; + return this._url ?? ""; } private set url(url: string) { - this._rmbProps.relayUrl = url; + this._url = url; } - - public async update(param: { url: string }) { - if (this.url === param.url) return; - this.url = param.url; - if (this._rmbClient) { - await this.disconnect(); - this._rmbClient.relayUrl = this.url; - } else this.factory(); + public update(param: { url: string }): void { + this._url = param.url; } - - public async isAlive(): Promise { + async isAlive(): Promise { if (!this.url) throw new Error("Can't access before initialization"); + const splittedURL = this.url.split("relay"); + const url = splittedURL[1] ? "https://gridproxy" + splittedURL[1] : this.url; try { - if (!this._rmbClient?.con?.OPEN) await this.setUp(); - return resolveServiceStatus(this._rmbClient.ping(2)); - } catch (error) { - return { alive: false, error }; + const res = await fetch(url + "/health"); + if (!res?.ok) throw Error(`HTTP Response Code: ${res?.status}`); + const rmb_conn = (await res.json())?.rmb_conn; + if (rmb_conn === "ok") { + return { + alive: true, + }; + } else { + return { + alive: false, + error: new Error(`rmb_conn is ${rmb_conn}`), + }; + } + } catch (e) { + throw new RequestError(`HTTP request failed due to ${e}.`); } } - public async disconnect() { - await this._rmbClient.disconnect(); - } } diff --git a/packages/monitoring/src/types/index.ts b/packages/monitoring/src/types/index.ts index 90d2265703..77a68bcf27 100644 --- a/packages/monitoring/src/types/index.ts +++ b/packages/monitoring/src/types/index.ts @@ -56,13 +56,6 @@ export enum MonitorEvents { "serviceDown" = "MonitorServiceDown", } -export type RMBProps = { - chainUrl: string; - relayUrl?: string; - mnemonics: string; - keypairType: KeypairType; -}; - /** * Represents a service with its stacks and its ILivenessChecker instance. */ From e0c1edbb455c17f0f6f9907cb8d0acb971dba29f Mon Sep 17 00:00:00 2001 From: kassem Date: Sun, 28 Jul 2024 14:44:56 +0300 Subject: [PATCH 28/58] chore: add error handling to isAlive --- .../src/serviceMonitor/serviceURLManager.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index 8ea96569da..517879ae15 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -32,11 +32,15 @@ export class ServiceUrlManager { * @returns {Promise<{alive: boolean, error?: Error}>} - A promise that resolves with the liveness status of the service. */ private async pingService(service: ILivenessChecker) { - const status = await service.isAlive(); - if ("disconnect" in service) { - await (service as IDisconnectHandler).disconnect(); + try { + const status = await service.isAlive(); + if ("disconnect" in service) { + await (service as IDisconnectHandler).disconnect(); + } + return status; + } catch (e) { + return this.handleErrorsOnSilentMode((e as Error).message); } - return status; } /** * Handles errors based on the silent mode setting. @@ -80,11 +84,11 @@ export class ServiceUrlManager { monitorEvents.log(`${service.name}: pinging ${service.url}`, "gray"); for (let retry = 0; retry < this.retries; retry++) { const status = await this.pingService(service); - if (status.alive) { + if (status?.alive) { monitorEvents.log(`${service.name} on ${service.url} Success!`, "green"); return service.url; } - error = status.error ?? ""; + error = status?.error ?? ""; } monitorEvents.log( `${service.name}: failed to ping ${service.url} after ${this.retries} retries; ${error}`, From f2f4e3675cd6cca532224564e6f431ad6508f23b Mon Sep 17 00:00:00 2001 From: kassem Date: Sun, 28 Jul 2024 15:03:03 +0300 Subject: [PATCH 29/58] chore: support timeout, and log errors using events --- .../monitoring/example/serviceURLManager.ts | 5 +++-- .../src/serviceMonitor/serviceURLManager.ts | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/monitoring/example/serviceURLManager.ts b/packages/monitoring/example/serviceURLManager.ts index def7e3abdb..cc8c015e94 100644 --- a/packages/monitoring/example/serviceURLManager.ts +++ b/packages/monitoring/example/serviceURLManager.ts @@ -8,7 +8,7 @@ import { } from "../src"; const gridproxy = ["http://www.nonexistentgridProxy.com", "http://www.gooogleTF.com", "https://gridproxy.dev.grid.tf/"]; -const rmb = ["wss://www.nonExistentRelay.com", "wss://relay.dev.grid.tf"]; +const rmb = ["https://graphql.dev.grid.tf", "wss://relay.dev.grid.tf"]; const graphql = ["https://graphql.dev.grid.tf/graphql"]; const tfChain = ["wss://tfchain.dev.grid.tf/ws", "wss://www.nonExistentChain.com"]; const mnemonics = ""; @@ -30,9 +30,10 @@ checkStacksAvailability({ { service: new GridProxyMonitor(), URLs: gridproxy }, { service: new GraphQLMonitor(), URLs: graphql }, { - service: new RMBMonitor({ mnemonics, chainUrl: "wss://tfchain.02.dev.grid.tf/ws", keypairType: "sr25519" }), + service: new RMBMonitor(), URLs: rmb, }, { service: new TFChainMonitor(), URLs: tfChain }, ], + timeout: 10, }); diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index 517879ae15..f9da1c56e5 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -16,6 +16,7 @@ export class ServiceUrlManager { private retries = 3; private silent: N = false as N; public services: Service[]; + public timeout = 20; constructor(options: URLManagerOptions) { Object.assign(this, options); @@ -33,11 +34,22 @@ export class ServiceUrlManager { */ private async pingService(service: ILivenessChecker) { try { - const status = await service.isAlive(); + const statusPromise = service.isAlive(); + + const timeoutPromise = new Promise((resolve, reject) => { + setTimeout(() => { + reject(new Error("Timeout")); + }, this.timeout * 1000); + }); + + const result = await Promise.race([statusPromise, timeoutPromise]); + if (result instanceof Error && result.message === "Timeout") { + throw result; + } if ("disconnect" in service) { await (service as IDisconnectHandler).disconnect(); } - return status; + return result; } catch (e) { return this.handleErrorsOnSilentMode((e as Error).message); } @@ -55,7 +67,7 @@ export class ServiceUrlManager { */ private handleErrorsOnSilentMode(errorMsg: string) { if (this.silent) { - console.log(errorMsg); + monitorEvents.log(errorMsg, "red"); return null; } throw new Error(errorMsg); From 89ef80cd1bbfe8272c65ed2602cf3264803e8d23 Mon Sep 17 00:00:00 2001 From: kassem Date: Sun, 28 Jul 2024 15:04:10 +0300 Subject: [PATCH 30/58] chore: support timeout --- packages/monitoring/src/types/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/monitoring/src/types/index.ts b/packages/monitoring/src/types/index.ts index 77a68bcf27..f2f2906b0f 100644 --- a/packages/monitoring/src/types/index.ts +++ b/packages/monitoring/src/types/index.ts @@ -87,6 +87,10 @@ export type URLManagerOptions = { * Optional. Determines if the stack manager should operate silently without throwing any errors and just return null as result. */ silent?: N; + /** + * Optional. timeout for each request in secondes; + */ + timeout?: number; }; export type ServiceUrl = N extends false ? string : string | null; From edfdf830e4bed6911f8e463aa1db48d05936efb3 Mon Sep 17 00:00:00 2001 From: kassem Date: Sun, 28 Jul 2024 15:25:49 +0300 Subject: [PATCH 31/58] chore: support RMB --- packages/playground/src/config.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/playground/src/config.ts b/packages/playground/src/config.ts index 8bacc64652..304dea65c0 100644 --- a/packages/playground/src/config.ts +++ b/packages/playground/src/config.ts @@ -86,22 +86,12 @@ export async function setGlobalEnv() { { URLs: STATS_STACKS, service: new StatsMonitor() }, { URLs: SUBSTRATE_STACKS, service: new TFChainMonitor() }, { URLs: ACTIVATION_SERVICE_STACKS, service: new ActivationMonitor() }, + { URLs: RELAY_STACKS, service: new RMBMonitor() }, ], silent: true, }); const result = await urlManger.getAvailableServicesStack(); - // if (result.TFChain) { - // result.RMB = await urlManger.getAvailableStack( - // RELAY_STACKS, - // new RMBMonitor({ - // chainUrl: result.TFChain, - // mnemonics: "", - // keypairType: "sr25519", - // }), - // ); - // } - // result.test = null; if (Object.values(result).includes(null)) { window.$$showMonitorError(result); return false; From 7276bbd9ace0fd23b4378d190ce3e77c7d0a5f77 Mon Sep 17 00:00:00 2001 From: kassem Date: Sun, 28 Jul 2024 15:27:37 +0300 Subject: [PATCH 32/58] chore: support RMB --- packages/playground/src/config.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/packages/playground/src/config.ts b/packages/playground/src/config.ts index 8bacc64652..71b0028489 100644 --- a/packages/playground/src/config.ts +++ b/packages/playground/src/config.ts @@ -86,22 +86,12 @@ export async function setGlobalEnv() { { URLs: STATS_STACKS, service: new StatsMonitor() }, { URLs: SUBSTRATE_STACKS, service: new TFChainMonitor() }, { URLs: ACTIVATION_SERVICE_STACKS, service: new ActivationMonitor() }, + { URLs: RELAY_STACKS, service: new RMBMonitor() }, ], silent: true, }); const result = await urlManger.getAvailableServicesStack(); - // if (result.TFChain) { - // result.RMB = await urlManger.getAvailableStack( - // RELAY_STACKS, - // new RMBMonitor({ - // chainUrl: result.TFChain, - // mnemonics: "", - // keypairType: "sr25519", - // }), - // ); - // } - // result.test = null; if (Object.values(result).includes(null)) { window.$$showMonitorError(result); return false; @@ -113,7 +103,7 @@ export async function setGlobalEnv() { window.env.GRAPHQL_URL = GraphQl!; window.env.SUBSTRATE_URL = TFChain!; window.env.ACTIVATION_SERVICE_URL = Activation!; - window.env.RELAY_DOMAIN = "wss://relay.dev.grid.tf"!; + window.env.RELAY_DOMAIN = RMB!; await new Promise(r => setTimeout(r, 3000)); From c4cdbb7ef8f47766e5f052b4b45e2f8c6f4d37da Mon Sep 17 00:00:00 2001 From: kassem Date: Sun, 28 Jul 2024 16:42:38 +0300 Subject: [PATCH 33/58] refactor: support increasing timeout --- .../src/serviceMonitor/serviceURLManager.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index f9da1c56e5..e308ebc035 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -1,5 +1,5 @@ import { monitorEvents } from "../helpers/events"; -import { IDisconnectHandler, ILivenessChecker, Service, ServiceUrl, URLManagerOptions } from "../types"; +import { IDisconnectHandler, ILivenessChecker, Service, ServiceStatus, ServiceUrl, URLManagerOptions } from "../types"; /** * Manages service URLs, checking their availability and ensuring they are reachable. @@ -16,7 +16,7 @@ export class ServiceUrlManager { private retries = 3; private silent: N = false as N; public services: Service[]; - public timeout = 20; + public timeout = 2; constructor(options: URLManagerOptions) { Object.assign(this, options); @@ -32,14 +32,14 @@ export class ServiceUrlManager { * * @returns {Promise<{alive: boolean, error?: Error}>} - A promise that resolves with the liveness status of the service. */ - private async pingService(service: ILivenessChecker) { + private async pingService(service: ILivenessChecker, _timeout: number): Promise { try { const statusPromise = service.isAlive(); const timeoutPromise = new Promise((resolve, reject) => { setTimeout(() => { reject(new Error("Timeout")); - }, this.timeout * 1000); + }, _timeout * 1000); }); const result = await Promise.race([statusPromise, timeoutPromise]); @@ -49,9 +49,9 @@ export class ServiceUrlManager { if ("disconnect" in service) { await (service as IDisconnectHandler).disconnect(); } - return result; + return result as ServiceStatus; } catch (e) { - return this.handleErrorsOnSilentMode((e as Error).message); + return this.handleErrorsOnSilentMode((e as Error).message) as unknown as ServiceStatus; } } /** @@ -95,7 +95,7 @@ export class ServiceUrlManager { await service.update({ url: urls[i] }); monitorEvents.log(`${service.name}: pinging ${service.url}`, "gray"); for (let retry = 0; retry < this.retries; retry++) { - const status = await this.pingService(service); + const status = await this.pingService(service, this.timeout + retry * this.timeout); if (status?.alive) { monitorEvents.log(`${service.name} on ${service.url} Success!`, "green"); return service.url; From de7dde611ee68a9b53a9a5784f8e341b1ded389f Mon Sep 17 00:00:00 2001 From: kassem Date: Mon, 29 Jul 2024 14:37:32 +0300 Subject: [PATCH 34/58] fix: return status object instade of throwing error --- packages/monitoring/src/serviceMonitor/serviceURLManager.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index e308ebc035..9206a2c5ef 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -51,7 +51,10 @@ export class ServiceUrlManager { } return result as ServiceStatus; } catch (e) { - return this.handleErrorsOnSilentMode((e as Error).message) as unknown as ServiceStatus; + return { + alive: false, + error: e, + }; } } /** From d120598407824717a8154d2f842eb98d3bacf55a Mon Sep 17 00:00:00 2001 From: kassem Date: Mon, 29 Jul 2024 14:39:02 +0300 Subject: [PATCH 35/58] refactor: - rename constractor param - use replace instade of split --- packages/monitoring/src/serviceMonitor/rmb.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/rmb.ts b/packages/monitoring/src/serviceMonitor/rmb.ts index ef3b5a8b86..f126eae763 100644 --- a/packages/monitoring/src/serviceMonitor/rmb.ts +++ b/packages/monitoring/src/serviceMonitor/rmb.ts @@ -5,8 +5,8 @@ import { ILivenessChecker, ServiceStatus } from "../types"; export class RMBMonitor implements ILivenessChecker { private _name = "RMB"; private _url: string; - constructor(gridProxyUrl?: string) { - if (gridProxyUrl) this.url = gridProxyUrl; + constructor(RMBUrl?: string) { + if (RMBUrl) this.url = RMBUrl; } public get name() { return this._name; @@ -22,10 +22,9 @@ export class RMBMonitor implements ILivenessChecker { } async isAlive(): Promise { if (!this.url) throw new Error("Can't access before initialization"); - const splittedURL = this.url.split("relay"); - const url = splittedURL[1] ? "https://gridproxy" + splittedURL[1] : this.url; + const proxyUrl = this.url.replace("wss://relay", "https://gridproxy"); try { - const res = await fetch(url + "/health"); + const res = await fetch(proxyUrl + "/health"); if (!res?.ok) throw Error(`HTTP Response Code: ${res?.status}`); const rmb_conn = (await res.json())?.rmb_conn; if (rmb_conn === "ok") { From c167f87bc16042acb5dbbfd81c76d3d482158605 Mon Sep 17 00:00:00 2001 From: kassem Date: Mon, 29 Jul 2024 15:11:28 +0300 Subject: [PATCH 36/58] Refactor: use /health endpoint in tfchain monitor --- .../monitoring/src/serviceMonitor/tfChain.ts | 47 +++++-------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/tfChain.ts b/packages/monitoring/src/serviceMonitor/tfChain.ts index f4ebae62d6..2025d8ebc3 100644 --- a/packages/monitoring/src/serviceMonitor/tfChain.ts +++ b/packages/monitoring/src/serviceMonitor/tfChain.ts @@ -1,20 +1,11 @@ -import { QueryClient } from "@threefold/tfchain_client"; +import { resolveServiceStatus, sendRequest } from "../helpers/utils"; +import { ILivenessChecker, ServiceStatus } from "../types"; -import { IDisconnectHandler, ILivenessChecker, ServiceStatus } from "../types"; - -export class TFChainMonitor implements ILivenessChecker, IDisconnectHandler { +export class TFChainMonitor implements ILivenessChecker { private _name = "TFChain"; private _url: string; - private _tfClient: QueryClient; - constructor(tfChainUrl?: string) { - if (tfChainUrl) { - this._url = tfChainUrl; - this._tfClient = new QueryClient(this.url); - } - } - private async setUp() { - if (!this._tfClient) throw new Error("Can't setUp before initialization"); - await this._tfClient?.connect(); + constructor(tfchainUrl?: string) { + if (tfchainUrl) this.url = tfchainUrl; } public get name() { return this._name; @@ -26,25 +17,13 @@ export class TFChainMonitor implements ILivenessChecker, IDisconnectHandler { this._url = url; } public update(param: { url: string }): void { - if (this.url === param.url) return; - this.url = param.url; - this._tfClient = new QueryClient(this.url); - } - public async isAlive(): Promise { - if (!this.url) throw new Error("Can't access before initialization"); - try { - if (!this._tfClient.api) await this.setUp(); - return { - alive: true, - }; - } catch (error) { - return { - alive: false, - error, - }; - } - } - public async disconnect() { - await this._tfClient.disconnect(); + this._url = param.url; + } + async isAlive(url = this.url): Promise { + if (!url) throw new Error("Can't access before initialization"); + let _url = url.replace("wss", "https"); + _url = _url.replace("/ws", "/health"); + console.log("updated", _url); + return resolveServiceStatus(sendRequest(_url, { method: "Get" })); } } From 638fcde023d0ed76f0cb99633c0dd6cb5f0daf46 Mon Sep 17 00:00:00 2001 From: kassem Date: Mon, 29 Jul 2024 17:32:41 +0300 Subject: [PATCH 37/58] refactor: remove logs line --- packages/monitoring/src/serviceMonitor/tfChain.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/monitoring/src/serviceMonitor/tfChain.ts b/packages/monitoring/src/serviceMonitor/tfChain.ts index 2025d8ebc3..353f8b5b2e 100644 --- a/packages/monitoring/src/serviceMonitor/tfChain.ts +++ b/packages/monitoring/src/serviceMonitor/tfChain.ts @@ -23,7 +23,6 @@ export class TFChainMonitor implements ILivenessChecker { if (!url) throw new Error("Can't access before initialization"); let _url = url.replace("wss", "https"); _url = _url.replace("/ws", "/health"); - console.log("updated", _url); return resolveServiceStatus(sendRequest(_url, { method: "Get" })); } } From 2e7412932982be0a968a843aa3c09e55511f490c Mon Sep 17 00:00:00 2001 From: kassem Date: Tue, 30 Jul 2024 14:34:03 +0300 Subject: [PATCH 38/58] feat: isAlive can take new url --- packages/monitoring/src/serviceMonitor/activations.ts | 6 +++--- packages/monitoring/src/serviceMonitor/graphql.ts | 6 +++--- packages/monitoring/src/serviceMonitor/gridproxy.ts | 6 +++--- packages/monitoring/src/serviceMonitor/rmb.ts | 6 +++--- packages/monitoring/src/serviceMonitor/stats.ts | 6 +++--- packages/monitoring/src/types/index.ts | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/activations.ts b/packages/monitoring/src/serviceMonitor/activations.ts index 54325310c7..4133a74d9c 100644 --- a/packages/monitoring/src/serviceMonitor/activations.ts +++ b/packages/monitoring/src/serviceMonitor/activations.ts @@ -19,8 +19,8 @@ export class ActivationMonitor implements ILivenessChecker { public update(param: { url: string }): void { this.url = param.url; } - async isAlive(): Promise { - if (!this.url) throw new Error("Can't access before initialization"); - return resolveServiceStatus(sendRequest(this.url, { method: "Get" })); + async isAlive(url = this.url): Promise { + if (!url) throw new Error("Can't access before initialization"); + return resolveServiceStatus(sendRequest(url, { method: "Get" })); } } diff --git a/packages/monitoring/src/serviceMonitor/graphql.ts b/packages/monitoring/src/serviceMonitor/graphql.ts index 73e6e535e4..20246607ad 100644 --- a/packages/monitoring/src/serviceMonitor/graphql.ts +++ b/packages/monitoring/src/serviceMonitor/graphql.ts @@ -20,10 +20,10 @@ export class GraphQLMonitor implements ILivenessChecker { this._url = param.url; } - async isAlive(): Promise { - if (!this.url) throw new Error("Can't access before initialization"); + async isAlive(url = this.url): Promise { + if (!url) throw new Error("Can't access before initialization"); return resolveServiceStatus( - sendRequest(this.url, { + sendRequest(url, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ diff --git a/packages/monitoring/src/serviceMonitor/gridproxy.ts b/packages/monitoring/src/serviceMonitor/gridproxy.ts index f111b96236..0f27f1cc53 100644 --- a/packages/monitoring/src/serviceMonitor/gridproxy.ts +++ b/packages/monitoring/src/serviceMonitor/gridproxy.ts @@ -19,8 +19,8 @@ export class GridProxyMonitor implements ILivenessChecker { public update(param: { url: string }): void { this._url = param.url; } - async isAlive(): Promise { - if (!this.url) throw new Error("Can't access before initialization"); - return resolveServiceStatus(sendRequest(this.url, { method: "Get" })); + async isAlive(url = this.url): Promise { + if (!url) throw new Error("Can't access before initialization"); + return resolveServiceStatus(sendRequest(url, { method: "Get" })); } } diff --git a/packages/monitoring/src/serviceMonitor/rmb.ts b/packages/monitoring/src/serviceMonitor/rmb.ts index f126eae763..d6068d4381 100644 --- a/packages/monitoring/src/serviceMonitor/rmb.ts +++ b/packages/monitoring/src/serviceMonitor/rmb.ts @@ -20,9 +20,9 @@ export class RMBMonitor implements ILivenessChecker { public update(param: { url: string }): void { this._url = param.url; } - async isAlive(): Promise { - if (!this.url) throw new Error("Can't access before initialization"); - const proxyUrl = this.url.replace("wss://relay", "https://gridproxy"); + async isAlive(url = this.url): Promise { + if (!url) throw new Error("Can't access before initialization"); + const proxyUrl = url.replace("wss://relay", "https://gridproxy"); try { const res = await fetch(proxyUrl + "/health"); if (!res?.ok) throw Error(`HTTP Response Code: ${res?.status}`); diff --git a/packages/monitoring/src/serviceMonitor/stats.ts b/packages/monitoring/src/serviceMonitor/stats.ts index 9c3a4fa4df..8a7b558a8c 100644 --- a/packages/monitoring/src/serviceMonitor/stats.ts +++ b/packages/monitoring/src/serviceMonitor/stats.ts @@ -19,8 +19,8 @@ export class StatsMonitor implements ILivenessChecker { public update(param: { url: string }): void { this.url = param.url; } - async isAlive(): Promise { - if (!this.url) throw new Error("Can't access before initialization"); - return resolveServiceStatus(sendRequest(`${this.url}/api/stats-summary`, { method: "Get" })); + async isAlive(url = this.url): Promise { + if (!url) throw new Error("Can't access before initialization"); + return resolveServiceStatus(sendRequest(`${url}/api/stats-summary`, { method: "Get" })); } } diff --git a/packages/monitoring/src/types/index.ts b/packages/monitoring/src/types/index.ts index f2f2906b0f..0f447c0c38 100644 --- a/packages/monitoring/src/types/index.ts +++ b/packages/monitoring/src/types/index.ts @@ -41,7 +41,7 @@ export interface ILivenessChecker

    extends IServiceBase

    { * Checks if the service is alive. * @returns {Promise} A promise that resolves with the current status of the service. */ - isAlive: () => Promise; + isAlive: (url?: string) => Promise; } export type ServiceStatus = { From 5d6b6830fa3b27a3e228ac66d6d49fde6541b410 Mon Sep 17 00:00:00 2001 From: kassem Date: Tue, 30 Jul 2024 18:44:43 +0300 Subject: [PATCH 39/58] Feat: ping url in parallel --- .../src/serviceMonitor/serviceURLManager.ts | 64 ++++++++++++++----- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index 9206a2c5ef..0720db7eae 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -1,3 +1,5 @@ +import { ConnectionError } from "@threefold/types"; + import { monitorEvents } from "../helpers/events"; import { IDisconnectHandler, ILivenessChecker, Service, ServiceStatus, ServiceUrl, URLManagerOptions } from "../types"; @@ -29,12 +31,14 @@ export class ServiceUrlManager { * If the service supports disconnection (implements IDisconnectHandler), it calls the `disconnect` method after the liveness check. * * @param {ILivenessChecker} service - An instance of ILivenessChecker that provides methods to check the service's liveness. + * @param {number} _timeout - The timeout duration for the ping request. + * @param {string} url - The URL of the service to be pinged. * * @returns {Promise<{alive: boolean, error?: Error}>} - A promise that resolves with the liveness status of the service. */ - private async pingService(service: ILivenessChecker, _timeout: number): Promise { + private async pingService(service: ILivenessChecker, _timeout: number, url: string): Promise { try { - const statusPromise = service.isAlive(); + const statusPromise = service.isAlive(url); const timeoutPromise = new Promise((resolve, reject) => { setTimeout(() => { @@ -57,6 +61,7 @@ export class ServiceUrlManager { }; } } + /** * Handles errors based on the silent mode setting. * @@ -75,6 +80,31 @@ export class ServiceUrlManager { } throw new Error(errorMsg); } + /** + * Checks if a service is alive with retries. + * + * This method attempts to check the availability of a service at a given URL, retrying the check + * a specified number of times if the service is not reachable. If the service becomes reachable, + * the URL is returned. Otherwise, an error is thrown after the maximum number of retries. + * + * @param {string} url - The URL of the service to check. + * @param {ILivenessChecker} service - An instance of ILivenessChecker used to check the service's liveness. + * + * @returns {Promise} - A promise that resolves with the URL if the service is reachable within the retries. + * + * @throws {ConnectionError} - Throws an error if the service cannot be reached after the maximum number of retries. + */ + async isAliveWithRetries(url: string, service: ILivenessChecker): Promise { + for (let retry = 0; retry < this.retries; retry++) { + const status = await this.pingService(service, this.timeout + retry * this.timeout, url); + if (status?.alive) { + monitorEvents.log(`${service.name} on ${url} Success!`, "green"); + return url; + } + if (status?.error) monitorEvents.log(`${service.name}: ${url} ${status.error}`, "gray"); + } + throw new ConnectionError(`${service.name}: Can't reach ${url} after 3 retries`); + } /** * Attempts to find a reachable service URL from a list of provided URLs. @@ -93,26 +123,26 @@ export class ServiceUrlManager { * */ async getAvailableStack(urls: string[], service: ILivenessChecker): Promise> { - let error: Error | string = ""; + const requestPromises: Promise[] = []; for (let i = 0; i < urls.length; i++) { - await service.update({ url: urls[i] }); - monitorEvents.log(`${service.name}: pinging ${service.url}`, "gray"); - for (let retry = 0; retry < this.retries; retry++) { - const status = await this.pingService(service, this.timeout + retry * this.timeout); - if (status?.alive) { - monitorEvents.log(`${service.name} on ${service.url} Success!`, "green"); - return service.url; - } - error = status?.error ?? ""; - } - monitorEvents.log( - `${service.name}: failed to ping ${service.url} after ${this.retries} retries; ${error}`, - "red", - ); + requestPromises.push(this.isAliveWithRetries(urls[i], service)); } + const result = await Promise.allSettled(requestPromises); + for (const url of result) if (url.status == "fulfilled") return url.value; + return this.handleErrorsOnSilentMode(`Failed to reach ${service.name} on all provided stacks`) as ServiceUrl; } + /** + * Retrieves available service URLs for all configured services. + * + * This method iterates through the configured services, checks their availability, + * and returns an object containing the reachable service URLs. + * + * @returns {Promise<{ [key: string]: ServiceUrl }>} - A promise that resolves with an object where the keys are service names and the values are reachable service URLs. + * + */ + async getAvailableServicesStack(): Promise<{ [key: string]: ServiceUrl }> { const result: { [key: string]: Promise> } = {}; From 889190d255a1b5a762ca47a6c880e10f30e824a7 Mon Sep 17 00:00:00 2001 From: kassem Date: Tue, 30 Jul 2024 18:45:24 +0300 Subject: [PATCH 40/58] Docs: update example --- packages/monitoring/example/serviceURLManager.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/monitoring/example/serviceURLManager.ts b/packages/monitoring/example/serviceURLManager.ts index cc8c015e94..8d74677abd 100644 --- a/packages/monitoring/example/serviceURLManager.ts +++ b/packages/monitoring/example/serviceURLManager.ts @@ -11,7 +11,6 @@ const gridproxy = ["http://www.nonexistentgridProxy.com", "http://www.gooogleTF. const rmb = ["https://graphql.dev.grid.tf", "wss://relay.dev.grid.tf"]; const graphql = ["https://graphql.dev.grid.tf/graphql"]; const tfChain = ["wss://tfchain.dev.grid.tf/ws", "wss://www.nonExistentChain.com"]; -const mnemonics = ""; async function checkStacksAvailability(services: URLManagerOptions) { try { @@ -25,7 +24,7 @@ async function checkStacksAvailability(services: URLManagerOp checkStacksAvailability({ retries: 3, - silent: false, + silent: true, services: [ { service: new GridProxyMonitor(), URLs: gridproxy }, { service: new GraphQLMonitor(), URLs: graphql }, @@ -35,5 +34,5 @@ checkStacksAvailability({ }, { service: new TFChainMonitor(), URLs: tfChain }, ], - timeout: 10, + timeout: 2, }); From be4fbb2b12556fea40c9eeb55460125e1699ca86 Mon Sep 17 00:00:00 2001 From: kassem Date: Wed, 31 Jul 2024 13:28:34 +0300 Subject: [PATCH 41/58] Refactor: retry logic moved to all stacks, to avoid delay --- .../src/serviceMonitor/serviceURLManager.ts | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index 0720db7eae..4be014fabe 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -81,54 +81,56 @@ export class ServiceUrlManager { throw new Error(errorMsg); } /** - * Checks if a service is alive with retries. + * Checks if a service is alive with a specified timeout for each attempt. * - * This method attempts to check the availability of a service at a given URL, retrying the check - * a specified number of times if the service is not reachable. If the service becomes reachable, - * the URL is returned. Otherwise, an error is thrown after the maximum number of retries. + * This method attempts to check the availability of a service at a given URL, using the provided + * pingService with a specified timeout for each attempt. If the service becomes reachable, + * the URL is returned. Otherwise, an error is thrown if the service cannot be reached. * * @param {string} url - The URL of the service to check. * @param {ILivenessChecker} service - An instance of ILivenessChecker used to check the service's liveness. + * @param {number} timeout - The timeout in seconds for each liveness check attempt. * * @returns {Promise} - A promise that resolves with the URL if the service is reachable within the retries. * * @throws {ConnectionError} - Throws an error if the service cannot be reached after the maximum number of retries. */ - async isAliveWithRetries(url: string, service: ILivenessChecker): Promise { - for (let retry = 0; retry < this.retries; retry++) { - const status = await this.pingService(service, this.timeout + retry * this.timeout, url); - if (status?.alive) { - monitorEvents.log(`${service.name} on ${url} Success!`, "green"); - return url; - } - if (status?.error) monitorEvents.log(`${service.name}: ${url} ${status.error}`, "gray"); + async isAliveWithTimeout(url: string, service: ILivenessChecker, timeout: number): Promise { + const status = await this.pingService(service, timeout, url); + if (status?.alive) { + monitorEvents.log(`${service.name} on ${url} Success!`, "green"); + return url; } - throw new ConnectionError(`${service.name}: Can't reach ${url} after 3 retries`); + if (status?.error) monitorEvents.log(`${service.name}: ${url} ${status.error}`, "gray"); + throw new ConnectionError(`${service.name}: Can't reach ${url}`); } /** - * Attempts to find a reachable service URL from a list of provided URLs. + * Attempts to find a reachable service URL from a list of provided URLs with retries. * - * This method iterates through the list of URLs, repeatedly pinging the service to - * check if it is alive. If a reachable URL is found, it is returned. If all URLs - * are exhausted without finding a reachable service, an error is thrown. + * This method checks the availability of each URL in the provided list using the specified + * liveness checker. It retries the check a specified number of times, increasing the timeout + * with each retry. It returns the first available URL in the list. If no URL is reachable after + * the maximum number of retries, an error is handled in silent mode. * * @param {string[]} urls - An array of service URLs to check for reachability. - * @param {ILivenessChecker} service - An instance of ILivenessChecker that provides methods - * to check the service's liveness and manage service URLs. - * - * @returns {Promise} - A promise that resolves with the reachable service URL. + * @param {ILivenessChecker} service - An instance of ILivenessChecker used to check each service's liveness. * - * @throws {Error} - Throws an error if no reachable service URL is found after checking all URLs. + * @returns {Promise>} - A promise that resolves with the first available URL if any are reachable. * + * @throws {Error} - Throws an error if none of the URLs are reachable after the maximum number of retries. */ + async getAvailableStack(urls: string[], service: ILivenessChecker): Promise> { - const requestPromises: Promise[] = []; - for (let i = 0; i < urls.length; i++) { - requestPromises.push(this.isAliveWithRetries(urls[i], service)); + for (let retry = 0; retry < this.retries; retry++) { + const requestPromises: Promise[] = []; + const timeout = this.timeout + this.timeout * retry; + for (let i = 0; i < urls.length; i++) { + requestPromises.push(this.isAliveWithTimeout(urls[i], service, timeout)); + } + const result = await Promise.allSettled(requestPromises); + for (const url of result) if (url.status == "fulfilled") return url.value; } - const result = await Promise.allSettled(requestPromises); - for (const url of result) if (url.status == "fulfilled") return url.value; return this.handleErrorsOnSilentMode(`Failed to reach ${service.name} on all provided stacks`) as ServiceUrl; } From b5827ea1477bfd19510145a2d9e48b897713bcc1 Mon Sep 17 00:00:00 2001 From: kassem Date: Wed, 31 Jul 2024 17:17:32 +0300 Subject: [PATCH 42/58] refactor: enahnce logs --- .../monitoring/src/serviceMonitor/serviceURLManager.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index 4be014fabe..0b36caf292 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -98,7 +98,6 @@ export class ServiceUrlManager { async isAliveWithTimeout(url: string, service: ILivenessChecker, timeout: number): Promise { const status = await this.pingService(service, timeout, url); if (status?.alive) { - monitorEvents.log(`${service.name} on ${url} Success!`, "green"); return url; } if (status?.error) monitorEvents.log(`${service.name}: ${url} ${status.error}`, "gray"); @@ -129,7 +128,12 @@ export class ServiceUrlManager { requestPromises.push(this.isAliveWithTimeout(urls[i], service, timeout)); } const result = await Promise.allSettled(requestPromises); - for (const url of result) if (url.status == "fulfilled") return url.value; + for (const url of result) + if (url.status == "fulfilled") { + monitorEvents.log(`${service.name} on ${url.value} Success!`, "green"); + + return url.value; + } } return this.handleErrorsOnSilentMode(`Failed to reach ${service.name} on all provided stacks`) as ServiceUrl; From 43f10de31392e8f570fb7203d62ade9dd2124ace Mon Sep 17 00:00:00 2001 From: kassem Date: Wed, 31 Jul 2024 17:20:10 +0300 Subject: [PATCH 43/58] docs: update urls in example --- packages/monitoring/example/serviceURLManager.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/monitoring/example/serviceURLManager.ts b/packages/monitoring/example/serviceURLManager.ts index 8d74677abd..7919fbdaa4 100644 --- a/packages/monitoring/example/serviceURLManager.ts +++ b/packages/monitoring/example/serviceURLManager.ts @@ -7,10 +7,10 @@ import { URLManagerOptions, } from "../src"; -const gridproxy = ["http://www.nonexistentgridProxy.com", "http://www.gooogleTF.com", "https://gridproxy.dev.grid.tf/"]; -const rmb = ["https://graphql.dev.grid.tf", "wss://relay.dev.grid.tf"]; +const gridproxy = ["https://gridproxy.dev.grid.tf/"]; +const rmb = ["https://graphql.dev.grid.tf", "wss://relay.dev.grid.tf", "wss://relay.02.dev.grid.tf"]; const graphql = ["https://graphql.dev.grid.tf/graphql"]; -const tfChain = ["wss://tfchain.dev.grid.tf/ws", "wss://www.nonExistentChain.com"]; +const tfChain = ["wss://tfchain.dev.grid.tf/ws"]; async function checkStacksAvailability(services: URLManagerOptions) { try { From 47e0195d5768543412cd779d8c3c32c9d427a47a Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 1 Aug 2024 12:15:12 +0300 Subject: [PATCH 44/58] Chore: remove disconnect logic --- .../src/serviceMonitor/alivenessChecker.ts | 32 ++++++------------- .../src/serviceMonitor/serviceURLManager.ts | 6 +--- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/alivenessChecker.ts b/packages/monitoring/src/serviceMonitor/alivenessChecker.ts index 603c2818df..eb625f2ec2 100644 --- a/packages/monitoring/src/serviceMonitor/alivenessChecker.ts +++ b/packages/monitoring/src/serviceMonitor/alivenessChecker.ts @@ -1,5 +1,5 @@ import { monitorEvents } from "../helpers/events"; -import { IDisconnectHandler, ILivenessChecker } from "../types"; +import { ILivenessChecker } from "../types"; /** * Represents a service monitor that periodically checks the liveness of multiple services. @@ -36,22 +36,10 @@ export class ServiceMonitor { } /** - * Disconnects services that implement the `IDisconnectHandler` interface. - * @returns A promise that resolves when all services are disconnected. + * Monitors the services at a regular interval and returns a function to exit the monitoring. + * @returns An object with a function `exit` to stop the monitoring services. */ - public async disconnect(): Promise { - for (const service of this.services) { - if ("disconnect" in service) { - await (service as IDisconnectHandler).disconnect(); - } - } - } - - /** - * Monitors the services at a regular interval and returns a function to exit and disconnect the monitoring. - * @returns An object with a function `exitAndDisconnect` to stop the monitoring and disconnect services. - */ - public monitorService(): { exitAndDisconnect: () => Promise } { + public monitorService(): { exit: () => Promise } { if (this.services.length === 0) throw new Error("No services to monitor"); monitorEvents.log(`Checking services status...`); @@ -59,21 +47,19 @@ export class ServiceMonitor { const intervalId = setInterval(async () => await this.checkLivenessOnce(), this.interval * 60 * 1000); /** - * Stops the monitoring and disconnects the services. - * @returns A promise that resolves when the monitoring is stopped and services are disconnected. + * Stops the monitoring services. + * @returns A promise that resolves when the monitoring is stopped. */ - const exitAndDisconnect = async (): Promise => { + const exit = async (): Promise => { clearInterval(intervalId); - await this.disconnect(); }; - return { exitAndDisconnect }; + return { exit }; } /** - * Checks the liveness of each service once and disconnects the services. + * Checks the liveness of each service once. */ public async pingService(): Promise { await this.checkLivenessOnce(); - await this.disconnect(); } } diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index 0b36caf292..8f3a808dc7 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -1,7 +1,7 @@ import { ConnectionError } from "@threefold/types"; import { monitorEvents } from "../helpers/events"; -import { IDisconnectHandler, ILivenessChecker, Service, ServiceStatus, ServiceUrl, URLManagerOptions } from "../types"; +import { ILivenessChecker, Service, ServiceStatus, ServiceUrl, URLManagerOptions } from "../types"; /** * Manages service URLs, checking their availability and ensuring they are reachable. @@ -28,7 +28,6 @@ export class ServiceUrlManager { * Pings the given service to check if it is alive. * * This method checks the liveness of the provided service by calling its `isAlive` method. - * If the service supports disconnection (implements IDisconnectHandler), it calls the `disconnect` method after the liveness check. * * @param {ILivenessChecker} service - An instance of ILivenessChecker that provides methods to check the service's liveness. * @param {number} _timeout - The timeout duration for the ping request. @@ -50,9 +49,6 @@ export class ServiceUrlManager { if (result instanceof Error && result.message === "Timeout") { throw result; } - if ("disconnect" in service) { - await (service as IDisconnectHandler).disconnect(); - } return result as ServiceStatus; } catch (e) { return { From addcd1865d43e6e2937fc07538a7a968d06d6540 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 1 Aug 2024 12:16:49 +0300 Subject: [PATCH 45/58] chore: Remove IDisconnectHandler interface --- packages/monitoring/src/types/index.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/monitoring/src/types/index.ts b/packages/monitoring/src/types/index.ts index 0f447c0c38..f68af9316e 100644 --- a/packages/monitoring/src/types/index.ts +++ b/packages/monitoring/src/types/index.ts @@ -21,17 +21,6 @@ interface IServiceBase

    { update(param: P): void; } -/** - * Represents a handler for disconnecting a service. - */ -export interface IDisconnectHandler { - /** - * Performs the disconnection from the service. - * @returns {Promise} A promise that resolves when the disconnection is successful. - */ - disconnect: () => Promise; -} - /** * Represents a service with liveness checking capability. * @template P - The type of the parameter object used for the update method. Defaults to {url:string}. From 18b0a1b128a4957b31e1988d61f1f330d1ed68a1 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 1 Aug 2024 12:17:42 +0300 Subject: [PATCH 46/58] Chore: update monitoring example --- packages/monitoring/example/serviceMonitor.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/monitoring/example/serviceMonitor.ts b/packages/monitoring/example/serviceMonitor.ts index ca0716ff49..377e6af107 100644 --- a/packages/monitoring/example/serviceMonitor.ts +++ b/packages/monitoring/example/serviceMonitor.ts @@ -5,12 +5,7 @@ async function HealthCheck() { new GridProxyMonitor(""), new GraphQLMonitor("https://graphql.dev.grid.tf/graphql"), new TFChainMonitor("wss://tfchain.dev.grid.tf/ws"), - new RMBMonitor({ - relayUrl: "wss://relay.dev.grid.tf", - chainUrl: "wss://tfchain.dev.grid.tf/ws", - mnemonics: "mnemonic", - keypairType: "sr25519", - }), + new RMBMonitor("wss://relay.dev.grid.tf"), ]; const serviceMonitor = new ServiceMonitor(services); @@ -21,7 +16,7 @@ async function HealthCheck() { serviceMonitor.interval = 0.25; const monitor = serviceMonitor.monitorService(); await new Promise(resolve => setTimeout(resolve, 0.5 * 60 * 1000)); - await monitor.exitAndDisconnect(); + await monitor.exit(); process.exit(0); } catch (err) { console.log(err); From 9d4f08dcbc38f8b1886a125a554ff58acb85fbe4 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 1 Aug 2024 12:34:43 +0300 Subject: [PATCH 47/58] chore: Update the service url on success --- packages/monitoring/src/serviceMonitor/serviceURLManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index 8f3a808dc7..1385f7d090 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -127,7 +127,7 @@ export class ServiceUrlManager { for (const url of result) if (url.status == "fulfilled") { monitorEvents.log(`${service.name} on ${url.value} Success!`, "green"); - + service.update({ url: url.value }); return url.value; } } From d601138b75730e5956290f1431e398ed609447a1 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 8 Aug 2024 10:44:42 +0300 Subject: [PATCH 48/58] Refactor: remove update function and replace it with setter --- .../monitoring/src/serviceMonitor/activations.ts | 5 +---- packages/monitoring/src/serviceMonitor/graphql.ts | 5 +---- packages/monitoring/src/serviceMonitor/gridproxy.ts | 5 +---- packages/monitoring/src/serviceMonitor/rmb.ts | 5 +---- .../src/serviceMonitor/serviceURLManager.ts | 4 ++-- packages/monitoring/src/serviceMonitor/stats.ts | 5 +---- packages/monitoring/src/serviceMonitor/tfChain.ts | 5 +---- packages/monitoring/src/types/index.ts | 13 ++----------- 8 files changed, 10 insertions(+), 37 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/activations.ts b/packages/monitoring/src/serviceMonitor/activations.ts index 4133a74d9c..739695e386 100644 --- a/packages/monitoring/src/serviceMonitor/activations.ts +++ b/packages/monitoring/src/serviceMonitor/activations.ts @@ -13,12 +13,9 @@ export class ActivationMonitor implements ILivenessChecker { public get url() { return this._url ?? ""; } - private set url(url: string) { + public set url(url: string) { this._url = url; } - public update(param: { url: string }): void { - this.url = param.url; - } async isAlive(url = this.url): Promise { if (!url) throw new Error("Can't access before initialization"); return resolveServiceStatus(sendRequest(url, { method: "Get" })); diff --git a/packages/monitoring/src/serviceMonitor/graphql.ts b/packages/monitoring/src/serviceMonitor/graphql.ts index 20246607ad..df755f0a5c 100644 --- a/packages/monitoring/src/serviceMonitor/graphql.ts +++ b/packages/monitoring/src/serviceMonitor/graphql.ts @@ -13,12 +13,9 @@ export class GraphQLMonitor implements ILivenessChecker { public get url() { return this._url ?? ""; } - private set url(url: string) { + public set url(url: string) { this._url = url; } - public update(param: { url: string }) { - this._url = param.url; - } async isAlive(url = this.url): Promise { if (!url) throw new Error("Can't access before initialization"); diff --git a/packages/monitoring/src/serviceMonitor/gridproxy.ts b/packages/monitoring/src/serviceMonitor/gridproxy.ts index 0f27f1cc53..13603baaa4 100644 --- a/packages/monitoring/src/serviceMonitor/gridproxy.ts +++ b/packages/monitoring/src/serviceMonitor/gridproxy.ts @@ -13,12 +13,9 @@ export class GridProxyMonitor implements ILivenessChecker { public get url() { return this._url ?? ""; } - private set url(url: string) { + public set url(url: string) { this._url = url; } - public update(param: { url: string }): void { - this._url = param.url; - } async isAlive(url = this.url): Promise { if (!url) throw new Error("Can't access before initialization"); return resolveServiceStatus(sendRequest(url, { method: "Get" })); diff --git a/packages/monitoring/src/serviceMonitor/rmb.ts b/packages/monitoring/src/serviceMonitor/rmb.ts index d6068d4381..a5ac63d5da 100644 --- a/packages/monitoring/src/serviceMonitor/rmb.ts +++ b/packages/monitoring/src/serviceMonitor/rmb.ts @@ -14,12 +14,9 @@ export class RMBMonitor implements ILivenessChecker { public get url() { return this._url ?? ""; } - private set url(url: string) { + public set url(url: string) { this._url = url; } - public update(param: { url: string }): void { - this._url = param.url; - } async isAlive(url = this.url): Promise { if (!url) throw new Error("Can't access before initialization"); const proxyUrl = url.replace("wss://relay", "https://gridproxy"); diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index 1385f7d090..8423d7ab1c 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -127,8 +127,8 @@ export class ServiceUrlManager { for (const url of result) if (url.status == "fulfilled") { monitorEvents.log(`${service.name} on ${url.value} Success!`, "green"); - service.update({ url: url.value }); - return url.value; + service.url = url.value; + return service.url; } } diff --git a/packages/monitoring/src/serviceMonitor/stats.ts b/packages/monitoring/src/serviceMonitor/stats.ts index 8a7b558a8c..5bee0180ab 100644 --- a/packages/monitoring/src/serviceMonitor/stats.ts +++ b/packages/monitoring/src/serviceMonitor/stats.ts @@ -13,12 +13,9 @@ export class StatsMonitor implements ILivenessChecker { public get url() { return this._url ?? ""; } - private set url(url: string) { + public set url(url: string) { this._url = url; } - public update(param: { url: string }): void { - this.url = param.url; - } async isAlive(url = this.url): Promise { if (!url) throw new Error("Can't access before initialization"); return resolveServiceStatus(sendRequest(`${url}/api/stats-summary`, { method: "Get" })); diff --git a/packages/monitoring/src/serviceMonitor/tfChain.ts b/packages/monitoring/src/serviceMonitor/tfChain.ts index 353f8b5b2e..032b647e70 100644 --- a/packages/monitoring/src/serviceMonitor/tfChain.ts +++ b/packages/monitoring/src/serviceMonitor/tfChain.ts @@ -13,12 +13,9 @@ export class TFChainMonitor implements ILivenessChecker { public get url() { return this._url ?? ""; } - private set url(url: string) { + public set url(url: string) { this._url = url; } - public update(param: { url: string }): void { - this._url = param.url; - } async isAlive(url = this.url): Promise { if (!url) throw new Error("Can't access before initialization"); let _url = url.replace("wss", "https"); diff --git a/packages/monitoring/src/types/index.ts b/packages/monitoring/src/types/index.ts index f68af9316e..83c89c6403 100644 --- a/packages/monitoring/src/types/index.ts +++ b/packages/monitoring/src/types/index.ts @@ -1,9 +1,7 @@ -import { KeypairType } from "@polkadot/util-crypto/types"; /** * Represents a basic service interface. - * @template P - The type of the parameter object used for the update method. */ -interface IServiceBase

    { +interface IServiceBase { /** * The name of the service. */ @@ -13,19 +11,12 @@ interface IServiceBase

    { * The URL of the service. */ url: string; - - /** - * Updates the service with the provided parameters. - * @param {P} param - The parameter object with specific keys, should be specified on class. - */ - update(param: P): void; } /** * Represents a service with liveness checking capability. - * @template P - The type of the parameter object used for the update method. Defaults to {url:string}. */ -export interface ILivenessChecker

    extends IServiceBase

    { +export interface ILivenessChecker

    extends IServiceBase { /** * Checks if the service is alive. * @returns {Promise} A promise that resolves with the current status of the service. From acf62e21a923ab332166ef1f5747ad21a41222c9 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 8 Aug 2024 11:33:24 +0300 Subject: [PATCH 49/58] reslove some Codacy issues --- packages/playground/scripts/build-env.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/playground/scripts/build-env.sh b/packages/playground/scripts/build-env.sh index d166c9157a..3f298c9822 100644 --- a/packages/playground/scripts/build-env.sh +++ b/packages/playground/scripts/build-env.sh @@ -79,7 +79,7 @@ esac parss_array(){ local service_urls=$1 - toString=($(echo $service_urls | tr ',' "\n")) + toString=($(echo "$service_urls" | tr ',' "\n")) for item in "${toString[@]}"; do quoted_string+="'$item' " done @@ -95,11 +95,11 @@ parss_array(){ configs=" window.env = { NETWORK: '$MODE', - GRAPHQL_STACKS: "[$(parss_array $GRAPHQL_URL)]", - GRIDPROXY_STACKS: "[$(parss_array $GRIDPROXY_URL)]", - SUBSTRATE_STACKS: "[$(parss_array $SUBSTRATE_URL)]", - ACTIVATION_SERVICE_STACKS: "[$(parss_array $ACTIVATION_SERVICE_URL)]", - RELAY_STACKS: "[$(parss_array $RELAY_DOMAIN)]", + GRAPHQL_STACKS: "[$(parss_array "$GRAPHQL_URL")]", + GRIDPROXY_STACKS: "[$(parss_array "$GRIDPROXY_URL")]", + SUBSTRATE_STACKS: "[$(parss_array "$SUBSTRATE_URL")]", + ACTIVATION_SERVICE_STACKS: "[$(parss_array "$ACTIVATION_SERVICE_URL")]", + RELAY_STACKS: "[$(parss_array "$RELAY_DOMAIN")]", BRIDGE_TFT_ADDRESS: '$BRIDGE_TFT_ADDRESS', STELLAR_NETWORK: '$STELLAR_NETWORK', STELLAR_HORIZON_URL: '$STELLAR_HORIZON_URL', From 9987cccd075307ef47d8d39ebfbc556ce9f8578d Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 8 Aug 2024 12:18:02 +0300 Subject: [PATCH 50/58] WIP: reslove some Codacy issues --- packages/playground/scripts/build-env.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/playground/scripts/build-env.sh b/packages/playground/scripts/build-env.sh index 3f298c9822..e4770ca886 100644 --- a/packages/playground/scripts/build-env.sh +++ b/packages/playground/scripts/build-env.sh @@ -105,7 +105,7 @@ window.env = { STELLAR_HORIZON_URL: '$STELLAR_HORIZON_URL', TFT_ASSET_ISSUER: '$TFT_ASSET_ISSUER', MINTING_URL: '$MINTING_URL', - STATS_STACKS: "[$(parss_array $STATS_URL)]", + STATS_STACKS: "[$(parss_array "$STATS_URL")]", TIMEOUT: +'$TIMEOUT', PAGE_SIZE: +'$PAGE_SIZE', MANUAL_URL: '$MANUAL_URL' From d8218c9e24dbd4738883ff0ed9bec02e4aaad186 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 8 Aug 2024 12:26:55 +0300 Subject: [PATCH 51/58] WIP: reslove some Codacy issues --- packages/playground/public/loader/loader.css | 9 ++++++--- packages/playground/scripts/build-env.sh | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/playground/public/loader/loader.css b/packages/playground/public/loader/loader.css index 6804bfc581..8d90a1b793 100644 --- a/packages/playground/public/loader/loader.css +++ b/packages/playground/public/loader/loader.css @@ -32,14 +32,15 @@ border-radius: 5px; padding: 5px 25px; } + .app-monitor-container.active { display: flex; } + .app-monitor-status { text-align: left; color: #fff; margin: 0; - font-family: sans-serif; max-width: 500px; line-height: 1; @@ -48,7 +49,7 @@ .service-name { color: #fff; width: 100px; - padding: 2px 0px; + padding: 2px 0; font-family: sans-serif; line-height: 1; font-weight: bold; @@ -62,11 +63,12 @@ font-weight: bold; font-size: x-large; } + .service-reachable { color: rgb(var(--v-theme-success, "76, 175, 80")); } .service-unreachable { - color: rgb(207, 102, 121); + color: rgba(207, 102, 121, 1); } .app-loader-logo { max-width: 100%; @@ -142,6 +144,7 @@ line-height: 1; text-align: center; } + .app-monitor-msg { display: none; } diff --git a/packages/playground/scripts/build-env.sh b/packages/playground/scripts/build-env.sh index e4770ca886..aafd953b04 100644 --- a/packages/playground/scripts/build-env.sh +++ b/packages/playground/scripts/build-env.sh @@ -88,7 +88,7 @@ parss_array(){ quoted_string=${quoted_string// /,} # remove trailing comma - echo "$quoted_string" | sed 's/.$//' + echo "${quoted_string%?}" } From fc3a0038b699054902ecc8cded0891a27e5b5722 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 8 Aug 2024 15:05:47 +0300 Subject: [PATCH 52/58] Refactor: add ServiceBase class --- .../src/serviceMonitor/activations.ts | 19 +++++------------- .../monitoring/src/serviceMonitor/graphql.ts | 20 +++++-------------- .../src/serviceMonitor/gridproxy.ts | 19 +++++------------- packages/monitoring/src/serviceMonitor/rmb.ts | 20 +++++-------------- .../src/serviceMonitor/serviceBase.ts | 15 ++++++++++++++ .../monitoring/src/serviceMonitor/stats.ts | 20 +++++-------------- .../monitoring/src/serviceMonitor/tfChain.ts | 20 +++++-------------- packages/monitoring/src/types/index.ts | 4 ++-- 8 files changed, 47 insertions(+), 90 deletions(-) create mode 100644 packages/monitoring/src/serviceMonitor/serviceBase.ts diff --git a/packages/monitoring/src/serviceMonitor/activations.ts b/packages/monitoring/src/serviceMonitor/activations.ts index 739695e386..3c774cf32a 100644 --- a/packages/monitoring/src/serviceMonitor/activations.ts +++ b/packages/monitoring/src/serviceMonitor/activations.ts @@ -1,20 +1,11 @@ import { resolveServiceStatus, sendRequest } from "../helpers/utils"; import { ILivenessChecker, ServiceStatus } from "../types"; +import { ServiceBase } from "./serviceBase"; -export class ActivationMonitor implements ILivenessChecker { - private readonly _name = "Activation"; - private _url: string; - constructor(activationServiceUrl?: string) { - if (activationServiceUrl) this.url = activationServiceUrl; - } - public get name() { - return this._name; - } - public get url() { - return this._url ?? ""; - } - public set url(url: string) { - this._url = url; +export class ActivationMonitor extends ServiceBase implements ILivenessChecker { + constructor(ServiceUrl?: string) { + super("Activation"); + if (ServiceUrl) this.url = ServiceUrl; } async isAlive(url = this.url): Promise { if (!url) throw new Error("Can't access before initialization"); diff --git a/packages/monitoring/src/serviceMonitor/graphql.ts b/packages/monitoring/src/serviceMonitor/graphql.ts index df755f0a5c..dffe069b32 100644 --- a/packages/monitoring/src/serviceMonitor/graphql.ts +++ b/packages/monitoring/src/serviceMonitor/graphql.ts @@ -1,22 +1,12 @@ import { resolveServiceStatus, sendRequest } from "../helpers/utils"; import { ILivenessChecker, ServiceStatus } from "../types"; +import { ServiceBase } from "./serviceBase"; -export class GraphQLMonitor implements ILivenessChecker { - private readonly _name = "GraphQl"; - private _url: string; - constructor(graphQlUrl?: string) { - if (graphQlUrl) this._url = graphQlUrl; +export class GraphQLMonitor extends ServiceBase implements ILivenessChecker { + constructor(ServiceUrl?: string) { + super("GraphQl"); + if (ServiceUrl) this.url = ServiceUrl; } - public get name() { - return this._name; - } - public get url() { - return this._url ?? ""; - } - public set url(url: string) { - this._url = url; - } - async isAlive(url = this.url): Promise { if (!url) throw new Error("Can't access before initialization"); return resolveServiceStatus( diff --git a/packages/monitoring/src/serviceMonitor/gridproxy.ts b/packages/monitoring/src/serviceMonitor/gridproxy.ts index 13603baaa4..ab8eec9a95 100644 --- a/packages/monitoring/src/serviceMonitor/gridproxy.ts +++ b/packages/monitoring/src/serviceMonitor/gridproxy.ts @@ -1,20 +1,11 @@ import { resolveServiceStatus, sendRequest } from "../helpers/utils"; import { ILivenessChecker, ServiceStatus } from "../types"; +import { ServiceBase } from "./serviceBase"; -export class GridProxyMonitor implements ILivenessChecker { - private readonly _name = "GridProxy"; - private _url: string; - constructor(gridProxyUrl?: string) { - if (gridProxyUrl) this.url = gridProxyUrl; - } - public get name() { - return this._name; - } - public get url() { - return this._url ?? ""; - } - public set url(url: string) { - this._url = url; +export class GridProxyMonitor extends ServiceBase implements ILivenessChecker { + constructor(ServiceUrl?: string) { + super("GridProxy"); + if (ServiceUrl) this.url = ServiceUrl; } async isAlive(url = this.url): Promise { if (!url) throw new Error("Can't access before initialization"); diff --git a/packages/monitoring/src/serviceMonitor/rmb.ts b/packages/monitoring/src/serviceMonitor/rmb.ts index a5ac63d5da..1317e2d053 100644 --- a/packages/monitoring/src/serviceMonitor/rmb.ts +++ b/packages/monitoring/src/serviceMonitor/rmb.ts @@ -1,21 +1,11 @@ import { RequestError } from "@threefold/types"; import { ILivenessChecker, ServiceStatus } from "../types"; - -export class RMBMonitor implements ILivenessChecker { - private _name = "RMB"; - private _url: string; - constructor(RMBUrl?: string) { - if (RMBUrl) this.url = RMBUrl; - } - public get name() { - return this._name; - } - public get url() { - return this._url ?? ""; - } - public set url(url: string) { - this._url = url; +import { ServiceBase } from "./serviceBase"; +export class RMBMonitor extends ServiceBase implements ILivenessChecker { + constructor(ServiceUrl?: string) { + super("RMB"); + if (ServiceUrl) this.url = ServiceUrl; } async isAlive(url = this.url): Promise { if (!url) throw new Error("Can't access before initialization"); diff --git a/packages/monitoring/src/serviceMonitor/serviceBase.ts b/packages/monitoring/src/serviceMonitor/serviceBase.ts new file mode 100644 index 0000000000..ce357caf2d --- /dev/null +++ b/packages/monitoring/src/serviceMonitor/serviceBase.ts @@ -0,0 +1,15 @@ +import { IServiceBase } from "../types"; + +export class ServiceBase implements IServiceBase { + private _url?: string; + constructor(private readonly _name: string) {} + public get name() { + return this._name; + } + public get url() { + return this._url ?? ""; + } + public set url(url: string) { + this._url = url; + } +} diff --git a/packages/monitoring/src/serviceMonitor/stats.ts b/packages/monitoring/src/serviceMonitor/stats.ts index 5bee0180ab..845f76a45a 100644 --- a/packages/monitoring/src/serviceMonitor/stats.ts +++ b/packages/monitoring/src/serviceMonitor/stats.ts @@ -1,20 +1,10 @@ import { resolveServiceStatus, sendRequest } from "../helpers/utils"; import { ILivenessChecker, ServiceStatus } from "../types"; - -export class StatsMonitor implements ILivenessChecker { - private readonly _name = "Stats"; - private _url: string; - constructor(statusUrl?: string) { - if (statusUrl) this.url = statusUrl; - } - public get name() { - return this._name; - } - public get url() { - return this._url ?? ""; - } - public set url(url: string) { - this._url = url; +import { ServiceBase } from "./serviceBase"; +export class StatsMonitor extends ServiceBase implements ILivenessChecker { + constructor(ServiceUrl?: string) { + super("Stats"); + if (ServiceUrl) this.url = ServiceUrl; } async isAlive(url = this.url): Promise { if (!url) throw new Error("Can't access before initialization"); diff --git a/packages/monitoring/src/serviceMonitor/tfChain.ts b/packages/monitoring/src/serviceMonitor/tfChain.ts index 032b647e70..3db7913328 100644 --- a/packages/monitoring/src/serviceMonitor/tfChain.ts +++ b/packages/monitoring/src/serviceMonitor/tfChain.ts @@ -1,20 +1,10 @@ import { resolveServiceStatus, sendRequest } from "../helpers/utils"; import { ILivenessChecker, ServiceStatus } from "../types"; - -export class TFChainMonitor implements ILivenessChecker { - private _name = "TFChain"; - private _url: string; - constructor(tfchainUrl?: string) { - if (tfchainUrl) this.url = tfchainUrl; - } - public get name() { - return this._name; - } - public get url() { - return this._url ?? ""; - } - public set url(url: string) { - this._url = url; +import { ServiceBase } from "./serviceBase"; +export class TFChainMonitor extends ServiceBase implements ILivenessChecker { + constructor(ServiceUrl?: string) { + super("TFChain"); + if (ServiceUrl) this.url = ServiceUrl; } async isAlive(url = this.url): Promise { if (!url) throw new Error("Can't access before initialization"); diff --git a/packages/monitoring/src/types/index.ts b/packages/monitoring/src/types/index.ts index 83c89c6403..64d333cf55 100644 --- a/packages/monitoring/src/types/index.ts +++ b/packages/monitoring/src/types/index.ts @@ -1,7 +1,7 @@ /** * Represents a basic service interface. */ -interface IServiceBase { +export interface IServiceBase { /** * The name of the service. */ @@ -16,7 +16,7 @@ interface IServiceBase { /** * Represents a service with liveness checking capability. */ -export interface ILivenessChecker

    extends IServiceBase { +export interface ILivenessChecker extends IServiceBase { /** * Checks if the service is alive. * @returns {Promise} A promise that resolves with the current status of the service. From 5016e5094490b602bbff8f70fcf37f91c6354c27 Mon Sep 17 00:00:00 2001 From: kassem Date: Thu, 8 Aug 2024 15:13:49 +0300 Subject: [PATCH 53/58] chore: predefine the timeout error --- .../monitoring/src/serviceMonitor/serviceURLManager.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts index 8423d7ab1c..f8f27cb9a0 100644 --- a/packages/monitoring/src/serviceMonitor/serviceURLManager.ts +++ b/packages/monitoring/src/serviceMonitor/serviceURLManager.ts @@ -3,6 +3,8 @@ import { ConnectionError } from "@threefold/types"; import { monitorEvents } from "../helpers/events"; import { ILivenessChecker, Service, ServiceStatus, ServiceUrl, URLManagerOptions } from "../types"; +const TIME_OUT = "Timeout"; + /** * Manages service URLs, checking their availability and ensuring they are reachable. * @@ -39,14 +41,14 @@ export class ServiceUrlManager { try { const statusPromise = service.isAlive(url); - const timeoutPromise = new Promise((resolve, reject) => { + const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { - reject(new Error("Timeout")); + reject(new Error(TIME_OUT)); }, _timeout * 1000); }); const result = await Promise.race([statusPromise, timeoutPromise]); - if (result instanceof Error && result.message === "Timeout") { + if (result instanceof Error && result.message === TIME_OUT) { throw result; } return result as ServiceStatus; From 5d82ed99803fc147ccbfa40d5cc2136c2585aa23 Mon Sep 17 00:00:00 2001 From: kassem Date: Tue, 13 Aug 2024 18:39:27 +0300 Subject: [PATCH 54/58] Chore: add docstring and remove debug lines --- packages/playground/src/config.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/playground/src/config.ts b/packages/playground/src/config.ts index 71b0028489..6194406320 100644 --- a/packages/playground/src/config.ts +++ b/packages/playground/src/config.ts @@ -75,7 +75,16 @@ function defineGlobalProps(app: App) { app.config.globalProperties.validators = validators; app.config.globalProperties.MANUAL_URL = window.env.MANUAL_URL; } - +/** + * Configures global environment variables based on available service URLs. + * + * This asynchronous function initializes a `ServiceUrlManager` with a set of predefined services and their URLs. + * It then retrieves the available service stacks and updates the global `window.env` object with the URLs for each service. + * If any service URLs are not available, an error is displayed and the function returns `false`. + * If all service URLs are successfully retrieved and set, the function returns `true`. + * + * @returns A promise that resolves to `true` if all service URLs are successfully set, or `false` if any service URL is missing. + */ export async function setGlobalEnv() { const { GRIDPROXY_STACKS, GRAPHQL_STACKS, STATS_STACKS, RELAY_STACKS, SUBSTRATE_STACKS, ACTIVATION_SERVICE_STACKS } = window.env; @@ -104,8 +113,5 @@ export async function setGlobalEnv() { window.env.SUBSTRATE_URL = TFChain!; window.env.ACTIVATION_SERVICE_URL = Activation!; window.env.RELAY_DOMAIN = RMB!; - - await new Promise(r => setTimeout(r, 3000)); - return true; } From fad94be210bd45483a851ee48b023e36844a1453 Mon Sep 17 00:00:00 2001 From: kassem Date: Tue, 13 Aug 2024 18:55:08 +0300 Subject: [PATCH 55/58] Chore: fix codacy issue --- packages/playground/public/loader/loader.css | 4 +++- packages/playground/src/clients/index.ts | 2 +- packages/playground/src/components/logger.vue | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/playground/public/loader/loader.css b/packages/playground/public/loader/loader.css index 8d90a1b793..0972991e1b 100644 --- a/packages/playground/public/loader/loader.css +++ b/packages/playground/public/loader/loader.css @@ -67,8 +67,9 @@ .service-reachable { color: rgb(var(--v-theme-success, "76, 175, 80")); } + .service-unreachable { - color: rgba(207, 102, 121, 1); + color: rgb(207, 102, 121); } .app-loader-logo { max-width: 100%; @@ -148,6 +149,7 @@ .app-monitor-msg { display: none; } + .app-loader-msg.active, .app-monitor-msg.active { display: block; diff --git a/packages/playground/src/clients/index.ts b/packages/playground/src/clients/index.ts index a9b33d6a04..fff0d9ce63 100644 --- a/packages/playground/src/clients/index.ts +++ b/packages/playground/src/clients/index.ts @@ -1,7 +1,7 @@ import TFGridGqlClient from "@threefold/graphql_client"; import GridProxyClient from "@threefold/gridproxy_client"; import { QueryClient } from "@threefold/tfchain_client"; - +console.log("wtf") const gqlClient = new TFGridGqlClient(window.env.GRAPHQL_URL); const gridProxyClient = new GridProxyClient(window.env.GRIDPROXY_URL); const queryClient = new QueryClient(window.env.SUBSTRATE_URL); diff --git a/packages/playground/src/components/logger.vue b/packages/playground/src/components/logger.vue index 05b3ec8c73..f2e0928cee 100644 --- a/packages/playground/src/components/logger.vue +++ b/packages/playground/src/components/logger.vue @@ -203,7 +203,7 @@ export default { let _interceptorQueue: LI[] = []; async function interceptMessage(instance: LI) { - if (connectDB.value.error || !connectDB.value.data) { + if (connectDB.value.error ) { _interceptorQueue.push(instance); return; } From 4e502f400194c7335d73bc1e233353e14c7365b9 Mon Sep 17 00:00:00 2001 From: kassem Date: Wed, 14 Aug 2024 09:41:11 +0300 Subject: [PATCH 56/58] Chore: fix codacy issue --- packages/playground/public/loader/loader.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/playground/public/loader/loader.css b/packages/playground/public/loader/loader.css index 0972991e1b..49f52c77d3 100644 --- a/packages/playground/public/loader/loader.css +++ b/packages/playground/public/loader/loader.css @@ -69,7 +69,7 @@ } .service-unreachable { - color: rgb(207, 102, 121); + color: rgb(207 102 121); } .app-loader-logo { max-width: 100%; From faf59b13f65ddad18887d123cc89fa7634bf31c7 Mon Sep 17 00:00:00 2001 From: kassem Date: Sun, 18 Aug 2024 14:06:19 +0300 Subject: [PATCH 57/58] Docs: add stacks support docs --- packages/playground/docs/build.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/playground/docs/build.md b/packages/playground/docs/build.md index b252bdee85..88180c4ab2 100644 --- a/packages/playground/docs/build.md +++ b/packages/playground/docs/build.md @@ -30,6 +30,8 @@ bash ../scripts/build-env.sh - SENTRY_DSN - ENABLE_TELEMETRY +- The use can provide a single URL or multiple URLs separated with a comma, for `GRAPHQL_URL, GRIDPROXY_URL, SUBSTRATE_URL, ACTIVATION_SERVICE_URL, RELAY_DOMAIN, STATS_URL`, If the user provides multiple URLs, it will be considered as a priority list, which means the dashboard will try to connect over the first URL in the list; if it fails, it will move to the next one, and so on. + - The backend payments are done with stellar so you need to decide which network of stellar you want to connect to ```bash From c64d797476343d0ad06e30f4b6d05cb9952c949c Mon Sep 17 00:00:00 2001 From: kassem Date: Sun, 18 Aug 2024 14:11:06 +0300 Subject: [PATCH 58/58] chore: remove debug line --- packages/playground/src/clients/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/playground/src/clients/index.ts b/packages/playground/src/clients/index.ts index fff0d9ce63..71bfb8697f 100644 --- a/packages/playground/src/clients/index.ts +++ b/packages/playground/src/clients/index.ts @@ -1,7 +1,6 @@ import TFGridGqlClient from "@threefold/graphql_client"; import GridProxyClient from "@threefold/gridproxy_client"; import { QueryClient } from "@threefold/tfchain_client"; -console.log("wtf") const gqlClient = new TFGridGqlClient(window.env.GRAPHQL_URL); const gridProxyClient = new GridProxyClient(window.env.GRIDPROXY_URL); const queryClient = new QueryClient(window.env.SUBSTRATE_URL);