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("="))