Skip to content

Commit 0f24159

Browse files
committed
add utils for signature and auth header + add keyed parameters to client
1 parent b737d50 commit 0f24159

File tree

12 files changed

+153
-251
lines changed

12 files changed

+153
-251
lines changed

packages/registrar_client/README.md

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,36 @@ This package provides a client for interacting with the TFGrid v4 Node Registrar
44

55
## Getting Started
66

7-
Set the `REGISTRAR_URL` in your environment variables to point to the TFGrid v4 Node Registrar.
7+
To initialize the Registrar Client, you need to provide the base url of registrar and your private key. Here is an example:
88

9-
```sh
10-
export REGISTRAR_URL=https://your-registrar-url
9+
```typescript
10+
const baseUrl = "https://registrar.dev4.grid.tf/v1";
11+
const privateKey = "your_private_key";
12+
const client = new RegistrarClient(baseURl, privateKey);
1113
```
1214

1315
## Usage
1416

1517
Here is an example of how to use the Registrar Client:
1618

1719
```typescript
18-
const privateKey = "your_private_key";
19-
const client = new RegistrarClient(privateKey);
20+
const client = new RegistrarClient(baseUrl, privateKey);
2021

22+
// Example: Create an account
2123
const accountRequest: CreateAccountRequest = {
2224
// your create account request data
2325
};
24-
client.account
25-
.createAccount(accountRequest)
26-
.then(account => {
27-
console.log("Account created:", account);
28-
})
29-
.catch(error => {
30-
console.error("Failed to create account:", error);
31-
});
26+
27+
const account = await client.account.createAccount(accountRequest);
28+
29+
// Example: Get account details
30+
const twinID = account.twin_id;
31+
const accountDetails = await client.account.getAccountByTwinId(twinID);
32+
33+
// Example: Update account information
34+
const updateAccountRequest: UpdateAccountRequest = {
35+
// your update account request data
36+
};
37+
38+
const updatedAccount = await client.account.updateAccount(twinID, updateAccountRequest);
3239
```

packages/registrar_client/jest.config.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@ module.exports = {
55
"^.+\\.(ts|tsx)$": ["ts-jest", { useESM: true }],
66
},
77
extensionsToTreatAsEsm: [".ts", ".tsx"],
8-
globals: {
9-
"ts-jest": {
10-
useESM: true,
11-
},
12-
},
138
testEnvironment: "node",
149
setupFiles: ["dotenv/config"],
1510
};

packages/registrar_client/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "registrar_client",
3-
"version": "1.0.0",
4-
"description": "Now I’m the model of a modern major general / The venerated Virginian veteran whose men are all / Lining up, to put me up on a pedestal / Writin’ letters to relatives / Embellishin’ my elegance and eloquence / But the elephant is in the room / The truth is in ya face when ya hear the British cannons go / BOOM",
3+
"version": "0.1.0",
4+
"description": "Registrar client for the ThreeFold Grid V4",
55
"keywords": [],
66
"author": "Salma Elsoly <salmaelsoly@gmail.com>",
77
"license": "ISC",
@@ -18,7 +18,9 @@
1818
"url": "git+https://github.com/threefoldtech/tfgridv4-sdk-ts.git"
1919
},
2020
"scripts": {
21-
"test": "node ./__tests__/registrar_client.test.js"
21+
"test": "jest",
22+
"format": "prettier --write .",
23+
"lint": "eslint src/**/*.ts"
2224
},
2325
"bugs": {
2426
"url": "https://github.com/threefoldtech/tfgridv4-sdk-ts/issues"

packages/registrar_client/src/client/client.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
22
import { Accounts } from "../modules/account/service";
3-
import { Config } from "../config";
43
import { Farms } from "../modules/farm/service";
54
import { Nodes } from "../modules/node/service";
65
import { Zos } from "../modules/zos/service";
76

87
export abstract class BaseClient {
98
private client: AxiosInstance;
109

11-
constructor() {
10+
constructor(baseURL: string) {
1211
this.client = axios.create({
13-
baseURL: Config.getInstance().registrarUrl,
12+
baseURL: baseURL,
1413
});
1514
}
1615

@@ -34,17 +33,27 @@ export abstract class BaseClient {
3433
return response.data;
3534
}
3635
}
36+
interface Config {
37+
baseURL: string;
38+
privateKey: string;
39+
}
3740

3841
export class RegistrarClient extends BaseClient {
39-
public readonly private_key: string;
42+
public readonly privateKey: string;
4043
accounts: Accounts;
4144
farms: Farms;
4245
nodes: Nodes;
4346
zos: Zos;
4447

45-
constructor(private_key: string) {
46-
super();
47-
this.private_key = private_key;
48+
constructor({ baseURL, privateKey }: Config) {
49+
if (!baseURL) {
50+
throw new Error("Base URL is required");
51+
}
52+
if (!privateKey) {
53+
throw new Error("Private key is required");
54+
}
55+
super(baseURL);
56+
this.privateKey = privateKey;
4857
this.accounts = new Accounts(this);
4958
this.farms = new Farms(this);
5059
this.nodes = new Nodes(this);

packages/registrar_client/src/config.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

packages/registrar_client/src/modules/account/service.ts

Lines changed: 25 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { RegistrarClient } from "../../client/client";
22
import { Account, CreateAccountRequest, UpdateAccountRequest } from "./types";
33
import * as tweetnacl from "tweetnacl";
44
import * as base64 from "base64-js";
5+
import { createSignatureWithPublicKey, createAuthHeader } from "../../utils";
56

67
export class Accounts {
78
private client: RegistrarClient;
@@ -15,81 +16,43 @@ export class Accounts {
1516
async createAccount(request: Partial<CreateAccountRequest>): Promise<Account | null> {
1617
const timestamp = Math.floor(Date.now() / 1000);
1718

18-
const privateKey = this.client.private_key;
19-
let publicKey;
20-
try {
21-
publicKey = base64.fromByteArray(tweetnacl.sign.keyPair.fromSecretKey(base64.toByteArray(privateKey)).publicKey);
22-
} catch (e) {
23-
console.error("Failed to generate public key: ", e);
24-
return null;
25-
}
19+
const privateKey = this.client.privateKey;
20+
const keyPair = tweetnacl.sign.keyPair.fromSecretKey(base64.toByteArray(privateKey));
2621

27-
const challenge = `${timestamp}:${publicKey}`;
28-
const signature = base64.fromByteArray(
29-
tweetnacl.sign.detached(Buffer.from(challenge, "utf-8"), base64.toByteArray(privateKey)),
30-
);
22+
const publicKey = base64.fromByteArray(keyPair.publicKey);
23+
const signature = createSignatureWithPublicKey(timestamp, publicKey, privateKey);
3124

3225
request.public_key = publicKey;
3326
request.signature = signature;
3427
request.timestamp = timestamp;
3528

36-
try {
37-
const data = await this.client.post<Account>(this.accountUri, request);
38-
return data;
39-
} catch (e) {
40-
console.error("Failed to create account: ", e);
41-
return null;
42-
}
29+
const data = await this.client.post<Account>(this.accountUri, request);
30+
return data;
4331
}
4432

45-
async getAccountByPublicKey(publicKey: string): Promise<Account | null> {
46-
try {
47-
const data = await this.client.get<Account>(this.accountUri, {
48-
params: {
49-
public_key: publicKey,
50-
},
51-
});
52-
return data;
53-
} catch (e) {
54-
console.error("Failed to get account: ", e);
55-
return null;
56-
}
33+
async getAccountByPublicKey(publicKey: string): Promise<Account> {
34+
const data = await this.client.get<Account>(this.accountUri, {
35+
params: {
36+
public_key: publicKey,
37+
},
38+
});
39+
return data;
5740
}
5841

59-
async getAccountByTwinId(twinId: number): Promise<Account | null> {
60-
try {
61-
const data = await this.client.get<Account>(this.accountUri, {
62-
params: {
63-
twin_id: twinId,
64-
},
65-
});
42+
async getAccountByTwinId(twinId: number): Promise<Account> {
43+
const data = await this.client.get<Account>(this.accountUri, {
44+
params: {
45+
twin_id: twinId,
46+
},
47+
});
6648

67-
return data;
68-
} catch (e) {
69-
console.error("Failed to get account: ", e);
70-
return null;
71-
}
49+
return data;
7250
}
7351

7452
async updateAccount(twinID: number, body: UpdateAccountRequest): Promise<any> {
75-
const timestamp = Math.floor(Date.now() / 1000);
76-
const challenge = `${timestamp}:${twinID}`;
77-
const privateKey = this.client.private_key;
78-
if (!privateKey) {
79-
throw new Error("Private key is not found");
80-
}
81-
const signature = tweetnacl.sign.detached(Buffer.from(challenge, "utf-8"), base64.toByteArray(privateKey));
82-
const config = {
83-
headers: {
84-
"X-Auth": `${Buffer.from(challenge).toString("base64")}:${base64.fromByteArray(signature)}`,
85-
},
86-
};
87-
try {
88-
const data = await this.client.patch<any>(`${this.accountUri}/${twinID}`, body, config);
89-
return data;
90-
} catch (e) {
91-
console.error("Failed to update account: ", e);
92-
return null;
93-
}
53+
const headers = createAuthHeader(twinID, this.client.privateKey);
54+
55+
const data = await this.client.patch<any>(`${this.accountUri}/${twinID}`, body, { headers });
56+
return data;
9457
}
9558
}
Lines changed: 15 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { RegistrarClient } from "../../client/client";
22
import { Farm, FarmsFilter } from "./types";
3-
import * as tweetnacl from "tweetnacl";
4-
import * as base64 from "base64-js";
3+
import { createAuthHeader } from "../../utils";
54

65
export class Farms {
76
private client: RegistrarClient;
@@ -11,7 +10,7 @@ export class Farms {
1110
this.client = client;
1211
}
1312

14-
async createFarm(farm: Partial<Farm>): Promise<number | null> {
13+
async createFarm(farm: Partial<Farm>): Promise<number> {
1514
const twinID = farm.twin_id;
1615
if (!twinID) {
1716
throw new Error("TwinID is not found");
@@ -20,70 +19,29 @@ export class Farms {
2019
if (!farmName || farmName.length < 1 || farmName.length > 40) {
2120
throw new Error("Farm name must be between 1 and 40 characters");
2221
}
23-
const timestamp = Math.floor(Date.now() / 1000);
24-
const challenge = `${timestamp}:${twinID}`;
25-
const privateKey = this.client.private_key;
26-
if (!privateKey) {
27-
throw new Error("Private key is not found");
28-
}
29-
const signature = tweetnacl.sign.detached(Buffer.from(challenge, "utf-8"), base64.toByteArray(privateKey));
30-
const config = {
31-
headers: {
32-
"X-Auth": `${Buffer.from(challenge).toString("base64")}:${base64.fromByteArray(signature)}`,
33-
},
34-
};
22+
const headers = createAuthHeader(twinID, this.client.privateKey);
3523

36-
try {
37-
const data = await this.client.post<number>(this.farmUri, farm, config);
38-
return data;
39-
} catch (e) {
40-
console.error("Failed to create farm: ", e);
41-
return null;
42-
}
24+
const data = await this.client.post<number>(this.farmUri, farm, { headers });
25+
return data;
4326
}
4427

45-
async listFarms(filter: FarmsFilter): Promise<Farm[] | null> {
46-
try {
47-
const data = await this.client.get<Farm[]>(this.farmUri, { params: filter });
48-
return data;
49-
} catch (e) {
50-
console.error("Failed to get farms: ", e);
51-
return null;
52-
}
28+
async listFarms(filter: FarmsFilter): Promise<Farm[]> {
29+
const data = await this.client.get<Farm[]>(this.farmUri, { params: filter });
30+
return data;
5331
}
5432

55-
async getFarm(farmID: number): Promise<Farm | null> {
56-
try {
57-
const data = await this.client.get<Farm>(`${this.farmUri}/${farmID}`);
58-
return data;
59-
} catch (e) {
60-
console.error("Failed to get farm: ", e);
61-
return null;
62-
}
33+
async getFarm(farmID: number): Promise<Farm> {
34+
const data = await this.client.get<Farm>(`${this.farmUri}/${farmID}`);
35+
return data;
6336
}
6437

6538
async updateFarm(farmID: number, twinID: number, name: string): Promise<any> {
6639
if (!name || name.length < 1 || name.length > 40) {
6740
throw new Error("Farm name must be between 1 and 40 characters");
6841
}
69-
const timestamp = Math.floor(Date.now() / 1000);
70-
const challenge = `${timestamp}:${twinID}`;
71-
const privateKey = this.client.private_key;
72-
if (!privateKey) {
73-
throw new Error("Private key is not found");
74-
}
75-
const signature = tweetnacl.sign.detached(Buffer.from(challenge, "utf-8"), base64.toByteArray(privateKey));
76-
const config = {
77-
headers: {
78-
"X-Auth": `${Buffer.from(challenge).toString("base64")}:${base64.fromByteArray(signature)}`,
79-
},
80-
};
81-
try {
82-
const data = await this.client.patch<any>(`${this.farmUri}/${farmID}`, { farm_name: name }, config);
83-
return data;
84-
} catch (e) {
85-
console.error("Failed to update farm: ", e);
86-
return null;
87-
}
42+
const headers = createAuthHeader(twinID, this.client.privateKey);
43+
44+
const data = await this.client.patch<any>(`${this.farmUri}/${farmID}`, { farm_name: name }, { headers });
45+
return data;
8846
}
8947
}

0 commit comments

Comments
 (0)