diff --git a/packages/grid_client/scripts/applications/nostr.ts b/packages/grid_client/scripts/applications/nostr.ts new file mode 100644 index 0000000000..16b8089a95 --- /dev/null +++ b/packages/grid_client/scripts/applications/nostr.ts @@ -0,0 +1,139 @@ +import { Features, FilterOptions, GatewayNameModel, GridClient, MachinesModel, NodeInfo } from "../../src"; +import { config, getClient } from "../client_loader"; +import { log, pingNodes } from "../utils"; + +async function deploy(client: GridClient, vms: MachinesModel, subdomain: string, gatewayNode: NodeInfo) { + // Deploy VM + const resultVM = await client.machines.deploy(vms); + log("================= Deploying VM ================="); + log(resultVM); + log("================= Deploying VM ================="); + + // Get WG interface details + const wgnet = (await client.machines.getObj(vms.name))[0].interfaces[0]; + + // Deploy Gateway + const gateway: GatewayNameModel = { + name: subdomain, + network: wgnet.network, + node_id: gatewayNode.nodeId, + tls_passthrough: false, + backends: [`http://${wgnet.ip}:8080`], + }; + + const resultGateway = await client.gateway.deploy_name(gateway); + log("================= Deploying Gateway ================="); + log(resultGateway); + log("================= Deploying Gateway ================="); +} + +async function getDeployment(client: GridClient, name: string, subdomain: string) { + // Get VM deployment + const resultVM = await client.machines.getObj(name); + log("================= Getting VM Deployment ================="); + log(resultVM); + log("================= Getting VM Deployment ================="); + + // Get Gateway deployment + const resultGateway = await client.gateway.getObj(subdomain); + log("================= Getting Gateway Deployment ================="); + log(resultGateway); + log(`https://${resultGateway[0].domain}`); + log("================= Getting Gateway Deployment ================="); +} + +async function cancel(client: GridClient, name: string, subdomain: string) { + // Cancel VM deployment + const resultVM = await client.machines.delete({ name }); + log("================= Canceling VM Deployment ================="); + log(resultVM); + log("================= Canceling VM Deployment ================="); + + // Cancel Gateway deployment + const resultGateway = await client.gateway.delete_name({ name: subdomain }); + log("================= Canceling Gateway Deployment ================="); + log(resultGateway); + log("================= Canceling Gateway Deployment ================="); +} + +async function main() { + const name = "newnostr1"; + const grid3 = await getClient(`nostr/${name}`); + const subdomain = `ntt${grid3.twinId}${name}`; + const instanceCapacity = { cru: 2, mru: 4, sru: 50 }; + + // VM Query Options + const vmQueryOptions: FilterOptions = { + features: [Features.wireguard, Features.mycelium], + cru: instanceCapacity.cru, + mru: instanceCapacity.mru, + sru: instanceCapacity.sru, + availableFor: grid3.twinId, + farmId: 1, + }; + + // Gateway Query Options + const gatewayQueryOptions: FilterOptions = { + features: [Features.wireguard, Features.mycelium], + gateway: true, + availableFor: grid3.twinId, + }; + + const gatewayNodes = await grid3.capacity.filterNodes(gatewayQueryOptions); + const gatewayNodeId = await pingNodes(grid3, gatewayNodes); + const gatewayNode = gatewayNodes.find(node => node.nodeId == gatewayNodeId); + const nodes = await grid3.capacity.filterNodes(vmQueryOptions); + const vmNode = await pingNodes(grid3, nodes); + const domain = `${subdomain}.${gatewayNode!.publicConfig.domain}`; + + const vms: MachinesModel = { + name, + network: { + name: "nostrnet", + ip_range: "10.252.0.0/16", + addAccess: true, + accessNodeId: gatewayNode!.nodeId, + }, + machines: [ + { + name: "nostr", + node_id: vmNode, + disks: [ + { + name: "nsDisk", + size: instanceCapacity.sru, + mountpoint: "/mnt/data", + }, + ], + planetary: true, + public_ip: false, + public_ip6: false, + mycelium: true, + cpu: instanceCapacity.cru, + memory: 1024 * instanceCapacity.mru, + rootfs_size: 0, + flist: "https://hub.grid.tf/tf-official-apps/nostr_relay-mycelium.flist", + entrypoint: "/sbin/zinit init", + env: { + SSH_KEY: config.ssh_key, + NOSTR_HOSTNAME: domain, + }, + }, + ], + metadata: "", + description: "Deploying Nostr instance via TS Grid3 client", + }; + + // Deploy VM and Gateway + await deploy(grid3, vms, subdomain, gatewayNode!); + + // Get the deployment details + await getDeployment(grid3, name, subdomain); + + // Uncomment the line below to cancel the deployment + // await cancel(grid3, name, subdomain); + + await grid3.disconnect(); +} + +main(); diff --git a/packages/grid_client/src/clients/tf-grid/contracts.ts b/packages/grid_client/src/clients/tf-grid/contracts.ts index 715eaa07af..3d599e3773 100644 --- a/packages/grid_client/src/clients/tf-grid/contracts.ts +++ b/packages/grid_client/src/clients/tf-grid/contracts.ts @@ -109,6 +109,10 @@ export interface GetConsumptionOptions { graphqlURL: string; id: number; } +export interface Consumption { + amountBilled: number; + discountReceived: DiscountLevel; +} export interface GetDiscountPackageOptions { graphqlURL: string; @@ -268,7 +272,7 @@ class TFContracts extends Contracts { const gqlClient = new Graphql(options.graphqlURL); const body = `query getConsumption($contractId: BigInt!){ - contractBillReports(where: {contractID_eq: $contractId} , orderBy: timestamp_DESC) { + contractBillReports(where: {contractID_eq: $contractId} , orderBy: timestamp_DESC, limit:1) { discountReceived } @@ -282,7 +286,7 @@ class TFContracts extends Contracts { if (billReports.length === 0) { return "None"; } else { - const discountPackage = billReports[billReports.length - 1].discountReceived; + const discountPackage = billReports[0].discountReceived; return discountPackage; } } catch (err) { @@ -291,17 +295,19 @@ class TFContracts extends Contracts { } } /** - * Get contract consumption per hour in TFT. + * Get the contract consumption details per hour in TFT. * * @param {GetConsumptionOptions} options - * @returns {Promise} + * @returns {Promise} A promise resolving to the consumption details, + * including the amount billed and the discount received. */ - async getConsumption(options: GetConsumptionOptions): Promise { + async getConsumption(options: GetConsumptionOptions): Promise { const gqlClient = new Graphql(options.graphqlURL); const body = `query getConsumption($contractId: BigInt!){ contractBillReports(where: {contractID_eq: $contractId}, limit: 2 , orderBy: timestamp_DESC) { amountBilled timestamp + discountReceived } nodeContracts(where: {contractID_eq: $contractId}) { createdAt @@ -318,7 +324,10 @@ class TFContracts extends Contracts { const gqlConsumption: GqlConsumption = response["data"] as GqlConsumption; const billReports = gqlConsumption.contractBillReports; if (billReports.length === 0) { - return 0; + return { + amountBilled: 0, + discountReceived: "None", + }; } else { let duration = 1; const amountBilled = new Decimal(billReports[0].amountBilled); @@ -338,10 +347,13 @@ class TFContracts extends Contracts { } } } - return amountBilled - .div(duration || 1) - .div(10 ** 7) - .toNumber(); + return { + amountBilled: amountBilled + .div(duration || 1) + .div(10 ** 7) + .toNumber(), + discountReceived: billReports[0].discountReceived, + }; } } catch (err) { (err as Error).message = formatErrorMessage(`Error getting consumption for contract ${options.id}.`, err); diff --git a/packages/grid_client/src/modules/contracts.ts b/packages/grid_client/src/modules/contracts.ts index 1dca479e2f..2728b55e7b 100644 --- a/packages/grid_client/src/modules/contracts.ts +++ b/packages/grid_client/src/modules/contracts.ts @@ -10,6 +10,7 @@ import { GridClientError } from "@threefold/types"; import * as PATH from "path"; import { + Consumption, ContractsOverdue, type DiscountLevel, GqlContracts, @@ -539,17 +540,18 @@ class Contracts { return this.client.contracts.getDiscountPackage({ id: options.id, graphqlURL: this.config.graphqlURL }); } /** - * Get contract consumption per hour in TFT. + * Get the contract consumption details per hour in TFT. * - * @param {ContractConsumption} options - * @returns {Promise} + * @param {ContractConsumption} options - The contract consumption parameters. + * @returns {Promise} A promise resolving to the consumption details, + * including the amount billed and the discount received. * @decorators * - `@expose`: Exposes the method for external use. * - `@validateInput`: Validates the input options. */ @expose @validateInput - async getConsumption(options: ContractConsumption): Promise { + async getConsumption(options: ContractConsumption): Promise { return this.client.contracts.getConsumption({ id: options.id, graphqlURL: this.config.graphqlURL }); } diff --git a/packages/grid_client/src/modules/models.ts b/packages/grid_client/src/modules/models.ts index 5be3172bb1..9f4f6c7525 100644 --- a/packages/grid_client/src/modules/models.ts +++ b/packages/grid_client/src/modules/models.ts @@ -624,6 +624,7 @@ class FilterOptions { @Expose() @IsOptional() @IsInt({ each: true }) @Min(1, { each: true }) nodeExclude?: number[]; @Expose() @IsOptional() @IsInt({ each: true }) @Min(1, { each: true }) farmIds?: number[]; @Expose() @IsOptional() @IsInt() @Min(1) farmId?: number; + @Expose() @IsOptional() @IsInt() @Min(1) nodeId?: number; @Expose() @IsOptional() @IsString() farmName?: string; @Expose() @IsOptional() @IsString() country?: string; @Expose() @IsOptional() @IsString() city?: string; diff --git a/packages/grid_client/src/primitives/nodes.ts b/packages/grid_client/src/primitives/nodes.ts index ebc4c4ead8..7efbb1cf95 100644 --- a/packages/grid_client/src/primitives/nodes.ts +++ b/packages/grid_client/src/primitives/nodes.ts @@ -466,6 +466,7 @@ class Nodes { healthy: options.healthy, sort_by: SortBy.FreeCRU, sort_order: SortOrder.Desc, + node_id: options.nodeId, rentable_or_rented_by: options.rentableOrRentedBy, features: options.features, }; diff --git a/packages/playground/src/components/manage_gateway_dialog.vue b/packages/playground/src/components/manage_gateway_dialog.vue index b4eac015bf..193a59d245 100644 --- a/packages/playground/src/components/manage_gateway_dialog.vue +++ b/packages/playground/src/components/manage_gateway_dialog.vue @@ -89,41 +89,6 @@
- - - - - - -
- -
- - - - - -
- - - -
- - - - - + + + + + + + + + + + + + +
+ +
+ + + + +
@@ -187,12 +192,12 @@