diff --git a/README.md b/README.md index 19a55fc111..85b7f5af3f 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ This repo contains the typescript clients and projects for Threefold grid. - [Playground](./packages/playground/README.md) - [graphql_client](./packages/graphql_client/README.md) - [gridproxy_client](./packages/gridproxy_client/README.md) +- [UI](./packages/UI/README.md) ## Requirements diff --git a/packages/UI/docs/pdf_viewer.md b/packages/UI/docs/pdf_viewer.md index 2d20e1d3e2..ddbc0f536b 100644 --- a/packages/UI/docs/pdf_viewer.md +++ b/packages/UI/docs/pdf_viewer.md @@ -28,11 +28,13 @@ To use the PDF Signer Web Component, follow these steps: 2. Navigate to the `repository/packages/UI` directory. -3. Run `yarn build` to generate the required distribution files. +3. Choose which provider you are going to use [see providers section](#using-providers-and-extensions) -4. Locate the `dist` folder created in the previous step. +4. Run `yarn build` to generate the required distribution files. -5. Copy the `dist/threefold-ui.umd.js` file and include it in your project's HTML files. +5. Locate the `dist` folder created in the previous step. + +6. Copy the `dist/threefold-ui.umd.js` file and include it in your project's HTML files. ```html @@ -101,6 +103,8 @@ Here's an example of how to use the PDF Signer Web Component in your HTML file: In the example above, replace `` and `` with the actual URLs for your PDF document and the destination where signed documents should be sent. Also, for the ``, use one of the following network options: `[main, test, qa, dev]`. +PS: Please make sure that you have a `PDF URL` with `CORS-ORIGIN` enabled. + Feel free to customize the HTML structure and styles to match your application's design and requirements. **Now you can serve your HTML file on any live-server plugin.** diff --git a/packages/UI/docs/script_editor.md b/packages/UI/docs/script_editor.md index d536334a58..6b339cdf3b 100644 --- a/packages/UI/docs/script_editor.md +++ b/packages/UI/docs/script_editor.md @@ -27,11 +27,13 @@ To create an instance of the Script Editor, follow these steps: 2. Navigate to the `repository/packages/UI` directory. -3. Run `yarn build` to generate the required distribution files. +3. Choose which provider you are going to use [see providers section](#using-providers-and-extensions) -4. Locate the `dist` folder created in the previous step. +4. Run `yarn build` to generate the required distribution files. -5. Copy the `dist/threefold-ui.umd.js` file and include it in your project's HTML files. +5. Locate the `dist` folder created in the previous step. + +6. Copy the `dist/threefold-ui.umd.js` file and include it in your project's HTML files. ```html diff --git a/packages/UI/examples/server-example/src/server.ts b/packages/UI/examples/server-example/src/server.ts index 679dde433b..c39d1b1bee 100644 --- a/packages/UI/examples/server-example/src/server.ts +++ b/packages/UI/examples/server-example/src/server.ts @@ -48,16 +48,12 @@ const verify = async (payload: Payload) => { app.post("/api/verify", async (req: Request, res: Response) => { const payload: Payload = req.body; - let content: Uint8Array = new Uint8Array(); try { if (payload.pdfUrl) { const response = await axios.get(payload.pdfUrl, { responseType: "arraybuffer" }); - content = Uint8Array.from(Buffer.from(response.data, "base64")); - } else { - content = Uint8Array.from(Buffer.from(payload.content || "", "base64")); + payload.content = Uint8Array.from(Buffer.from(response.data, "base64")).toString(); } - payload.content = content.toString(); const verified = await verify(payload); if (verified) { diff --git a/packages/UI/src/components/PDFSignerViewComponent.vue b/packages/UI/src/components/PDFSignerViewComponent.vue index f4c308d184..680791384b 100644 --- a/packages/UI/src/components/PDFSignerViewComponent.vue +++ b/packages/UI/src/components/PDFSignerViewComponent.vue @@ -111,7 +111,10 @@ export default { pdfData.value = data.toString(); numOfPages.value = pdf.numPages; } catch (error: any) { - showError({ isError: true, errorMessage: error.message }); + showError({ + isError: true, + errorMessage: "Please make sure that you have provided a PDF URL with CORS enabled.", + }); } finally { loadingPdf.value = false; } diff --git a/packages/grid_client/src/high_level/twinDeploymentHandler.ts b/packages/grid_client/src/high_level/twinDeploymentHandler.ts index 27c72a39d7..0a00f9cd07 100644 --- a/packages/grid_client/src/high_level/twinDeploymentHandler.ts +++ b/packages/grid_client/src/high_level/twinDeploymentHandler.ts @@ -265,6 +265,43 @@ class TwinDeploymentHandler { return deployments; } + async checkFarmIps(twinDeployments: TwinDeployment[]) { + const farmIPs: Map = new Map(); + + for (const twinDeployment of twinDeployments) { + if (twinDeployment.operation !== Operations.deploy) { + continue; + } + + if (twinDeployment.publicIps === 0) { + continue; + } + + const node = await this.nodes.getNode(twinDeployment.nodeId); + if (!node) { + continue; + } + if (!farmIPs.has(node.farmId)) { + farmIPs.set(node.farmId, twinDeployment.publicIps); + } else { + farmIPs.set(node.farmId, farmIPs.get(node.farmId)! + twinDeployment.publicIps); + } + } + + for (const farmId of farmIPs.keys()) { + const _farm = await this.tfclient.farms.get({ id: farmId }); + const freeIps = _farm.publicIps.filter(res => res.contractId === 0).length; + + if (freeIps < farmIPs.get(farmId)!) { + throw Error( + `Farm ${farmId} doesn't have enough public IPs: requested IPs=${farmIPs.get( + farmId, + )}, available IPs=${freeIps}`, + ); + } + } + } + async checkNodesCapacity(twinDeployments: TwinDeployment[]) { for (const twinDeployment of twinDeployments) { let workloads: Workload[] = []; @@ -439,6 +476,8 @@ class TwinDeploymentHandler { twinDeployments = await this.merge(twinDeployments); await this.validate(twinDeployments); await this.checkNodesCapacity(twinDeployments); + await this.checkFarmIps(twinDeployments); + const contracts = { created: [], updated: [], deleted: [] }; const resultContracts = { created: [], updated: [], deleted: [] }; let nodeExtrinsics: ExtrinsicResult[] = []; diff --git a/packages/grid_client/src/modules/models.ts b/packages/grid_client/src/modules/models.ts index 309b7cbe1a..ac4d31ec99 100644 --- a/packages/grid_client/src/modules/models.ts +++ b/packages/grid_client/src/modules/models.ts @@ -608,6 +608,7 @@ class FarmFilterOptions { @Expose() @IsOptional() @IsInt() page?: number; @Expose() @IsOptional() @IsInt() size?: number; @Expose() @IsOptional() @IsInt() ownedBy?: number; + @Expose() @IsOptional() @IsInt() farmId?: number; } class CalculatorModel { diff --git a/packages/grid_client/src/primitives/nodes.ts b/packages/grid_client/src/primitives/nodes.ts index 3eeb934dfa..7acfda6a64 100644 --- a/packages/grid_client/src/primitives/nodes.ts +++ b/packages/grid_client/src/primitives/nodes.ts @@ -23,7 +23,7 @@ interface FarmInfo { interface PublicIps { id: string; ip: string; - contractId: number; + contract_id: number; // Added to match the one in the farm interface || TODO: Should we replace the whole http requests to be done with the gridProxy. gateway: string; } @@ -176,7 +176,7 @@ class Nodes { farms = await this.getAllFarms(url); } return farms - .filter(farm => farm.publicIps.filter(ip => ip.contractId === 0).length > 0) + .filter(farm => farm.publicIps.filter(ip => ip.contract_id === 0).length > 0) .map(farm => farm.farmId) .includes(farmId); } @@ -383,6 +383,7 @@ class Nodes { node_has_gpu: options.nodeHasGPU, node_rented_by: options.nodeRentedBy, node_certified: options.nodeCertified, + farm_id: options.farmId, }; return Object.entries(params) .map(param => param.join("="))