@machhub-dev/sdk-ts 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/classes/auth.d.ts +7 -1
- package/dist/cjs/classes/auth.js +30 -5
- package/dist/cjs/classes/collection.d.ts +6 -1
- package/dist/cjs/classes/collection.js +22 -1
- package/dist/cjs/sdk-ts.d.ts +8 -0
- package/dist/cjs/sdk-ts.js +42 -26
- package/dist/cjs/services/http.service.js +7 -2
- package/dist/classes/auth.d.ts +7 -1
- package/dist/classes/auth.js +30 -5
- package/dist/classes/collection.d.ts +6 -1
- package/dist/classes/collection.js +22 -1
- package/dist/sdk-ts.d.ts +8 -0
- package/dist/sdk-ts.js +42 -26
- package/dist/services/http.service.js +7 -2
- package/package.json +2 -1
- package/src/classes/auth.ts +38 -6
- package/src/classes/collection.ts +25 -1
- package/src/sdk-ts.ts +45 -26
- package/src/services/http.service.ts +6 -2
|
@@ -2,10 +2,16 @@ import { HTTPService } from "../services/http.service.js";
|
|
|
2
2
|
import { Action, ActionResponse, Feature, Group, LoginResponse, User, ValidateJWTResponse } from "../types/auth.models.js";
|
|
3
3
|
export declare class Auth {
|
|
4
4
|
private httpService;
|
|
5
|
-
|
|
5
|
+
private applicationID;
|
|
6
|
+
private readonly AUTH_TOKEN_KEY_PREFIX;
|
|
7
|
+
constructor(httpService: HTTPService, applicationID: string);
|
|
8
|
+
private getStorageKey;
|
|
6
9
|
login(username: string, password: string): Promise<LoginResponse | undefined>;
|
|
7
10
|
validateJWT(token: string): Promise<ValidateJWTResponse>;
|
|
8
11
|
logout(): Promise<void>;
|
|
12
|
+
getJWTData(): Promise<any>;
|
|
13
|
+
getCurrentUser(): Promise<User>;
|
|
14
|
+
validateCurrentUser(): Promise<ValidateJWTResponse>;
|
|
9
15
|
checkAction(feature: string, scope: string): Promise<ActionResponse>;
|
|
10
16
|
checkPermission(feature: string, scope: string, action: Action): Promise<ActionResponse>;
|
|
11
17
|
getUsers(): Promise<User[]>;
|
package/dist/cjs/classes/auth.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Auth = void 0;
|
|
4
|
+
const jwt_decode_1 = require("jwt-decode");
|
|
4
5
|
class Auth {
|
|
5
|
-
constructor(httpService) {
|
|
6
|
+
constructor(httpService, applicationID) {
|
|
7
|
+
this.AUTH_TOKEN_KEY_PREFIX = "x-machhub-auth-tkn";
|
|
6
8
|
this.httpService = httpService;
|
|
9
|
+
this.applicationID = applicationID;
|
|
10
|
+
}
|
|
11
|
+
getStorageKey() {
|
|
12
|
+
return this.applicationID
|
|
13
|
+
? `${this.AUTH_TOKEN_KEY_PREFIX}-${this.applicationID}`
|
|
14
|
+
: this.AUTH_TOKEN_KEY_PREFIX;
|
|
7
15
|
}
|
|
8
16
|
async login(username, password) {
|
|
9
17
|
let res;
|
|
@@ -13,7 +21,8 @@ class Auth {
|
|
|
13
21
|
password: password,
|
|
14
22
|
}).post("/auth/login");
|
|
15
23
|
if (localStorage) {
|
|
16
|
-
|
|
24
|
+
// console.log("storage key:", this.getStorageKey());
|
|
25
|
+
localStorage.setItem(this.getStorageKey(), res.tkn); // Set User JWT
|
|
17
26
|
}
|
|
18
27
|
else {
|
|
19
28
|
console.error("localStorage is not available. The program needs to be in a browser environment.");
|
|
@@ -28,11 +37,27 @@ class Auth {
|
|
|
28
37
|
}
|
|
29
38
|
}
|
|
30
39
|
async validateJWT(token) {
|
|
31
|
-
|
|
32
|
-
return res;
|
|
40
|
+
return await this.httpService.request.withJSON({ token }).post("/auth/jwt/validate");
|
|
33
41
|
}
|
|
34
42
|
async logout() {
|
|
35
|
-
localStorage.removeItem(
|
|
43
|
+
localStorage.removeItem(this.getStorageKey());
|
|
44
|
+
}
|
|
45
|
+
async getJWTData() {
|
|
46
|
+
const token = localStorage.getItem(this.getStorageKey());
|
|
47
|
+
if (!token) {
|
|
48
|
+
throw new Error("No JWT token found in localStorage.");
|
|
49
|
+
}
|
|
50
|
+
return (0, jwt_decode_1.jwtDecode)(token);
|
|
51
|
+
}
|
|
52
|
+
async getCurrentUser() {
|
|
53
|
+
return await this.httpService.request.get("/auth/me");
|
|
54
|
+
}
|
|
55
|
+
async validateCurrentUser() {
|
|
56
|
+
const token = localStorage.getItem(this.getStorageKey());
|
|
57
|
+
if (!token) {
|
|
58
|
+
throw new Error("No JWT token found in localStorage.");
|
|
59
|
+
}
|
|
60
|
+
return await this.validateJWT(token);
|
|
36
61
|
}
|
|
37
62
|
async checkAction(feature, scope) {
|
|
38
63
|
try {
|
|
@@ -16,7 +16,12 @@ export declare class Collection {
|
|
|
16
16
|
sort(field: string, direction?: "asc" | "desc"): Collection;
|
|
17
17
|
limit(limit: number): Collection;
|
|
18
18
|
offset(offset: number): Collection;
|
|
19
|
-
|
|
19
|
+
expand(fields: string | string[]): Collection;
|
|
20
|
+
private applyOptions;
|
|
21
|
+
first(): Promise<any>;
|
|
22
|
+
getAll(options?: {
|
|
23
|
+
expand?: string | string[];
|
|
24
|
+
}): Promise<any[]>;
|
|
20
25
|
getOne(id: string): Promise<any>;
|
|
21
26
|
create(data: Record<string, any>): Promise<any>;
|
|
22
27
|
update(id: string, data: Record<string, any>): Promise<any>;
|
|
@@ -34,8 +34,29 @@ class Collection {
|
|
|
34
34
|
this.queryParams.offset = offset;
|
|
35
35
|
return this;
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
expand(fields) {
|
|
38
|
+
this.queryParams.expand = Array.isArray(fields) ? fields.join(",") : fields;
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
applyOptions(options) {
|
|
42
|
+
if (!options)
|
|
43
|
+
return;
|
|
44
|
+
for (const [key, value] of Object.entries(options)) {
|
|
45
|
+
if (value !== undefined && value !== null) {
|
|
46
|
+
this.queryParams[key] = value;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async first() {
|
|
51
|
+
const results = await this.limit(1).getAll();
|
|
52
|
+
return results[0] ?? null;
|
|
53
|
+
}
|
|
54
|
+
async getAll(options) {
|
|
38
55
|
try {
|
|
56
|
+
this.applyOptions(options);
|
|
57
|
+
if (options?.expand) {
|
|
58
|
+
this.queryParams.expand = Array.isArray(options.expand) ? options.expand.join() : options.expand;
|
|
59
|
+
}
|
|
39
60
|
return await this.httpService.request.get(this.collectionName + "/all", this.queryParams);
|
|
40
61
|
}
|
|
41
62
|
catch (error) {
|
package/dist/cjs/sdk-ts.d.ts
CHANGED
|
@@ -20,6 +20,14 @@ export declare class SDK {
|
|
|
20
20
|
private _function;
|
|
21
21
|
private _flow;
|
|
22
22
|
private _auth;
|
|
23
|
+
private applicationID;
|
|
24
|
+
/**
|
|
25
|
+
* Extracts the application ID from a runtime ID.
|
|
26
|
+
* Runtime ID format: {applicationID}XmchX{randomID}
|
|
27
|
+
* @param runtimeID The runtime ID to extract from
|
|
28
|
+
* @returns The extracted application ID, or empty string if invalid format
|
|
29
|
+
*/
|
|
30
|
+
private extractApplicationIDFromRuntimeID;
|
|
23
31
|
/**
|
|
24
32
|
* Initializes the SDK with the required clients.
|
|
25
33
|
*
|
package/dist/cjs/sdk-ts.js
CHANGED
|
@@ -113,6 +113,19 @@ class SDK {
|
|
|
113
113
|
this._function = null;
|
|
114
114
|
this._flow = null;
|
|
115
115
|
this._auth = null;
|
|
116
|
+
this.applicationID = "";
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Extracts the application ID from a runtime ID.
|
|
120
|
+
* Runtime ID format: {applicationID}XmchX{randomID}
|
|
121
|
+
* @param runtimeID The runtime ID to extract from
|
|
122
|
+
* @returns The extracted application ID, or empty string if invalid format
|
|
123
|
+
*/
|
|
124
|
+
extractApplicationIDFromRuntimeID(runtimeID) {
|
|
125
|
+
if (!runtimeID)
|
|
126
|
+
return "";
|
|
127
|
+
const parts = runtimeID.split('XmchX');
|
|
128
|
+
return parts.length > 0 ? parts[0] : "";
|
|
116
129
|
}
|
|
117
130
|
/**
|
|
118
131
|
* Initializes the SDK with the required clients.
|
|
@@ -146,9 +159,11 @@ class SDK {
|
|
|
146
159
|
config = { application_id: "" };
|
|
147
160
|
if (!config.application_id)
|
|
148
161
|
config = { application_id: "" };
|
|
149
|
-
// console.log("Using application_id:", config.application_id);
|
|
150
162
|
const envCfg = await getEnvConfig();
|
|
151
|
-
//
|
|
163
|
+
// Extract application_id from runtimeID if not provided in config
|
|
164
|
+
const application_id = config.application_id ||
|
|
165
|
+
this.extractApplicationIDFromRuntimeID(envCfg.runtimeID);
|
|
166
|
+
this.applicationID = application_id;
|
|
152
167
|
// Determine the hostname - use window.location.hostname in browser, otherwise fallback to localhost
|
|
153
168
|
const hostname = typeof window !== 'undefined' ? window.location.hostname : 'localhost';
|
|
154
169
|
const secured = typeof window !== 'undefined' ? window.location.protocol === 'https:' : false;
|
|
@@ -168,16 +183,16 @@ class SDK {
|
|
|
168
183
|
if (!config.natsUrl) {
|
|
169
184
|
config.natsUrl = `${secured ? 'wss' : 'ws'}://${host}/nats`;
|
|
170
185
|
}
|
|
171
|
-
const {
|
|
172
|
-
console.log("SDK Config:", { application_id, httpUrl, mqttUrl, natsUrl, developer_key: config.developer_key?.split('').map((_, i) => i < config.developer_key.length - 4 ? '*' : config.developer_key[i]).join('') });
|
|
173
|
-
this.http = new HTTPClient(
|
|
174
|
-
this.mqtt = await MQTTClient.getInstance(
|
|
175
|
-
this.nats = await NATSClient.getInstance(
|
|
186
|
+
const { httpUrl, mqttUrl, natsUrl } = config;
|
|
187
|
+
console.log("SDK Config:", { application_id: this.applicationID, httpUrl, mqttUrl, natsUrl, developer_key: config.developer_key?.split('').map((_, i) => i < config.developer_key.length - 4 ? '*' : config.developer_key[i]).join('') });
|
|
188
|
+
this.http = new HTTPClient(this.applicationID, httpUrl, config.developer_key, envCfg.runtimeID);
|
|
189
|
+
this.mqtt = await MQTTClient.getInstance(this.applicationID, mqttUrl, config.developer_key);
|
|
190
|
+
this.nats = await NATSClient.getInstance(this.applicationID, natsUrl);
|
|
176
191
|
this._historian = new historian_js_1.Historian(this.http["httpService"], this.mqtt["mqttService"]);
|
|
177
192
|
this._tag = new tag_js_1.Tag(this.http["httpService"], this.mqtt["mqttService"]);
|
|
178
193
|
this._function = new function_js_1.Function(this.http["httpService"], this.nats["natsService"]);
|
|
179
194
|
this._flow = new flow_js_1.Flow(this.http["httpService"]);
|
|
180
|
-
this._auth = new auth_js_1.Auth(this.http["httpService"]);
|
|
195
|
+
this._auth = new auth_js_1.Auth(this.http["httpService"], this.applicationID);
|
|
181
196
|
}
|
|
182
197
|
catch (error) {
|
|
183
198
|
console.error("Failed to initialize:", error);
|
|
@@ -286,26 +301,27 @@ async function findConfigEndpoint() {
|
|
|
286
301
|
headers: {
|
|
287
302
|
'Accept': 'application/json',
|
|
288
303
|
},
|
|
289
|
-
signal: AbortSignal.timeout(2000)
|
|
290
|
-
});
|
|
291
|
-
if (testResponse.ok) {
|
|
292
|
-
//
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
catch (jsonError) {
|
|
305
|
-
// Not valid JSON, continue to next candidate
|
|
306
|
-
continue;
|
|
304
|
+
signal: AbortSignal.timeout(2000)
|
|
305
|
+
}).catch(() => { console.log("ERR"); return null; }); // Catch fetch errors silently
|
|
306
|
+
if (!testResponse || !testResponse.ok) {
|
|
307
|
+
continue; // Skip to next candidate
|
|
308
|
+
}
|
|
309
|
+
// Validate that the response is JSON and contains the expected 'port' field
|
|
310
|
+
const contentType = testResponse.headers.get('content-type');
|
|
311
|
+
if (contentType && contentType.includes('application/json')) {
|
|
312
|
+
try {
|
|
313
|
+
const testData = await testResponse.json();
|
|
314
|
+
// Check if the response has the expected structure with a 'port' field
|
|
315
|
+
// TODO: Allow checks for path based hosting as well
|
|
316
|
+
if (testData && typeof testData === 'object' && 'port' in testData) {
|
|
317
|
+
// console.log(`Found config endpoint at: ${configUrl}`);
|
|
318
|
+
return configUrl;
|
|
307
319
|
}
|
|
308
320
|
}
|
|
321
|
+
catch (jsonError) {
|
|
322
|
+
// Not valid JSON, continue to next candidate
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
309
325
|
}
|
|
310
326
|
}
|
|
311
327
|
catch (error) {
|
|
@@ -117,8 +117,13 @@ class RequestParameters {
|
|
|
117
117
|
return this;
|
|
118
118
|
}
|
|
119
119
|
withAccessToken() {
|
|
120
|
-
const
|
|
121
|
-
|
|
120
|
+
const rawAppID = this.applicationID.replace("domains:", "");
|
|
121
|
+
const storageKey = rawAppID
|
|
122
|
+
? `x-machhub-auth-tkn-${rawAppID}`
|
|
123
|
+
: `x-machhub-auth-tkn`;
|
|
124
|
+
const tkn = localStorage.getItem(storageKey);
|
|
125
|
+
if (tkn)
|
|
126
|
+
this.setHeader("Authorization", `Bearer ${tkn}`);
|
|
122
127
|
return this;
|
|
123
128
|
}
|
|
124
129
|
withRuntimeID() {
|
package/dist/classes/auth.d.ts
CHANGED
|
@@ -2,10 +2,16 @@ import { HTTPService } from "../services/http.service.js";
|
|
|
2
2
|
import { Action, ActionResponse, Feature, Group, LoginResponse, User, ValidateJWTResponse } from "../types/auth.models.js";
|
|
3
3
|
export declare class Auth {
|
|
4
4
|
private httpService;
|
|
5
|
-
|
|
5
|
+
private applicationID;
|
|
6
|
+
private readonly AUTH_TOKEN_KEY_PREFIX;
|
|
7
|
+
constructor(httpService: HTTPService, applicationID: string);
|
|
8
|
+
private getStorageKey;
|
|
6
9
|
login(username: string, password: string): Promise<LoginResponse | undefined>;
|
|
7
10
|
validateJWT(token: string): Promise<ValidateJWTResponse>;
|
|
8
11
|
logout(): Promise<void>;
|
|
12
|
+
getJWTData(): Promise<any>;
|
|
13
|
+
getCurrentUser(): Promise<User>;
|
|
14
|
+
validateCurrentUser(): Promise<ValidateJWTResponse>;
|
|
9
15
|
checkAction(feature: string, scope: string): Promise<ActionResponse>;
|
|
10
16
|
checkPermission(feature: string, scope: string, action: Action): Promise<ActionResponse>;
|
|
11
17
|
getUsers(): Promise<User[]>;
|
package/dist/classes/auth.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
import { jwtDecode } from "jwt-decode";
|
|
1
2
|
export class Auth {
|
|
2
|
-
constructor(httpService) {
|
|
3
|
+
constructor(httpService, applicationID) {
|
|
4
|
+
this.AUTH_TOKEN_KEY_PREFIX = "x-machhub-auth-tkn";
|
|
3
5
|
this.httpService = httpService;
|
|
6
|
+
this.applicationID = applicationID;
|
|
7
|
+
}
|
|
8
|
+
getStorageKey() {
|
|
9
|
+
return this.applicationID
|
|
10
|
+
? `${this.AUTH_TOKEN_KEY_PREFIX}-${this.applicationID}`
|
|
11
|
+
: this.AUTH_TOKEN_KEY_PREFIX;
|
|
4
12
|
}
|
|
5
13
|
async login(username, password) {
|
|
6
14
|
let res;
|
|
@@ -10,7 +18,8 @@ export class Auth {
|
|
|
10
18
|
password: password,
|
|
11
19
|
}).post("/auth/login");
|
|
12
20
|
if (localStorage) {
|
|
13
|
-
|
|
21
|
+
// console.log("storage key:", this.getStorageKey());
|
|
22
|
+
localStorage.setItem(this.getStorageKey(), res.tkn); // Set User JWT
|
|
14
23
|
}
|
|
15
24
|
else {
|
|
16
25
|
console.error("localStorage is not available. The program needs to be in a browser environment.");
|
|
@@ -25,11 +34,27 @@ export class Auth {
|
|
|
25
34
|
}
|
|
26
35
|
}
|
|
27
36
|
async validateJWT(token) {
|
|
28
|
-
|
|
29
|
-
return res;
|
|
37
|
+
return await this.httpService.request.withJSON({ token }).post("/auth/jwt/validate");
|
|
30
38
|
}
|
|
31
39
|
async logout() {
|
|
32
|
-
localStorage.removeItem(
|
|
40
|
+
localStorage.removeItem(this.getStorageKey());
|
|
41
|
+
}
|
|
42
|
+
async getJWTData() {
|
|
43
|
+
const token = localStorage.getItem(this.getStorageKey());
|
|
44
|
+
if (!token) {
|
|
45
|
+
throw new Error("No JWT token found in localStorage.");
|
|
46
|
+
}
|
|
47
|
+
return jwtDecode(token);
|
|
48
|
+
}
|
|
49
|
+
async getCurrentUser() {
|
|
50
|
+
return await this.httpService.request.get("/auth/me");
|
|
51
|
+
}
|
|
52
|
+
async validateCurrentUser() {
|
|
53
|
+
const token = localStorage.getItem(this.getStorageKey());
|
|
54
|
+
if (!token) {
|
|
55
|
+
throw new Error("No JWT token found in localStorage.");
|
|
56
|
+
}
|
|
57
|
+
return await this.validateJWT(token);
|
|
33
58
|
}
|
|
34
59
|
async checkAction(feature, scope) {
|
|
35
60
|
try {
|
|
@@ -16,7 +16,12 @@ export declare class Collection {
|
|
|
16
16
|
sort(field: string, direction?: "asc" | "desc"): Collection;
|
|
17
17
|
limit(limit: number): Collection;
|
|
18
18
|
offset(offset: number): Collection;
|
|
19
|
-
|
|
19
|
+
expand(fields: string | string[]): Collection;
|
|
20
|
+
private applyOptions;
|
|
21
|
+
first(): Promise<any>;
|
|
22
|
+
getAll(options?: {
|
|
23
|
+
expand?: string | string[];
|
|
24
|
+
}): Promise<any[]>;
|
|
20
25
|
getOne(id: string): Promise<any>;
|
|
21
26
|
create(data: Record<string, any>): Promise<any>;
|
|
22
27
|
update(id: string, data: Record<string, any>): Promise<any>;
|
|
@@ -30,8 +30,29 @@ export class Collection {
|
|
|
30
30
|
this.queryParams.offset = offset;
|
|
31
31
|
return this;
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
expand(fields) {
|
|
34
|
+
this.queryParams.expand = Array.isArray(fields) ? fields.join(",") : fields;
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
applyOptions(options) {
|
|
38
|
+
if (!options)
|
|
39
|
+
return;
|
|
40
|
+
for (const [key, value] of Object.entries(options)) {
|
|
41
|
+
if (value !== undefined && value !== null) {
|
|
42
|
+
this.queryParams[key] = value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async first() {
|
|
47
|
+
const results = await this.limit(1).getAll();
|
|
48
|
+
return results[0] ?? null;
|
|
49
|
+
}
|
|
50
|
+
async getAll(options) {
|
|
34
51
|
try {
|
|
52
|
+
this.applyOptions(options);
|
|
53
|
+
if (options?.expand) {
|
|
54
|
+
this.queryParams.expand = Array.isArray(options.expand) ? options.expand.join() : options.expand;
|
|
55
|
+
}
|
|
35
56
|
return await this.httpService.request.get(this.collectionName + "/all", this.queryParams);
|
|
36
57
|
}
|
|
37
58
|
catch (error) {
|
package/dist/sdk-ts.d.ts
CHANGED
|
@@ -20,6 +20,14 @@ export declare class SDK {
|
|
|
20
20
|
private _function;
|
|
21
21
|
private _flow;
|
|
22
22
|
private _auth;
|
|
23
|
+
private applicationID;
|
|
24
|
+
/**
|
|
25
|
+
* Extracts the application ID from a runtime ID.
|
|
26
|
+
* Runtime ID format: {applicationID}XmchX{randomID}
|
|
27
|
+
* @param runtimeID The runtime ID to extract from
|
|
28
|
+
* @returns The extracted application ID, or empty string if invalid format
|
|
29
|
+
*/
|
|
30
|
+
private extractApplicationIDFromRuntimeID;
|
|
23
31
|
/**
|
|
24
32
|
* Initializes the SDK with the required clients.
|
|
25
33
|
*
|
package/dist/sdk-ts.js
CHANGED
|
@@ -110,6 +110,19 @@ export class SDK {
|
|
|
110
110
|
this._function = null;
|
|
111
111
|
this._flow = null;
|
|
112
112
|
this._auth = null;
|
|
113
|
+
this.applicationID = "";
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Extracts the application ID from a runtime ID.
|
|
117
|
+
* Runtime ID format: {applicationID}XmchX{randomID}
|
|
118
|
+
* @param runtimeID The runtime ID to extract from
|
|
119
|
+
* @returns The extracted application ID, or empty string if invalid format
|
|
120
|
+
*/
|
|
121
|
+
extractApplicationIDFromRuntimeID(runtimeID) {
|
|
122
|
+
if (!runtimeID)
|
|
123
|
+
return "";
|
|
124
|
+
const parts = runtimeID.split('XmchX');
|
|
125
|
+
return parts.length > 0 ? parts[0] : "";
|
|
113
126
|
}
|
|
114
127
|
/**
|
|
115
128
|
* Initializes the SDK with the required clients.
|
|
@@ -143,9 +156,11 @@ export class SDK {
|
|
|
143
156
|
config = { application_id: "" };
|
|
144
157
|
if (!config.application_id)
|
|
145
158
|
config = { application_id: "" };
|
|
146
|
-
// console.log("Using application_id:", config.application_id);
|
|
147
159
|
const envCfg = await getEnvConfig();
|
|
148
|
-
//
|
|
160
|
+
// Extract application_id from runtimeID if not provided in config
|
|
161
|
+
const application_id = config.application_id ||
|
|
162
|
+
this.extractApplicationIDFromRuntimeID(envCfg.runtimeID);
|
|
163
|
+
this.applicationID = application_id;
|
|
149
164
|
// Determine the hostname - use window.location.hostname in browser, otherwise fallback to localhost
|
|
150
165
|
const hostname = typeof window !== 'undefined' ? window.location.hostname : 'localhost';
|
|
151
166
|
const secured = typeof window !== 'undefined' ? window.location.protocol === 'https:' : false;
|
|
@@ -165,16 +180,16 @@ export class SDK {
|
|
|
165
180
|
if (!config.natsUrl) {
|
|
166
181
|
config.natsUrl = `${secured ? 'wss' : 'ws'}://${host}/nats`;
|
|
167
182
|
}
|
|
168
|
-
const {
|
|
169
|
-
console.log("SDK Config:", { application_id, httpUrl, mqttUrl, natsUrl, developer_key: config.developer_key?.split('').map((_, i) => i < config.developer_key.length - 4 ? '*' : config.developer_key[i]).join('') });
|
|
170
|
-
this.http = new HTTPClient(
|
|
171
|
-
this.mqtt = await MQTTClient.getInstance(
|
|
172
|
-
this.nats = await NATSClient.getInstance(
|
|
183
|
+
const { httpUrl, mqttUrl, natsUrl } = config;
|
|
184
|
+
console.log("SDK Config:", { application_id: this.applicationID, httpUrl, mqttUrl, natsUrl, developer_key: config.developer_key?.split('').map((_, i) => i < config.developer_key.length - 4 ? '*' : config.developer_key[i]).join('') });
|
|
185
|
+
this.http = new HTTPClient(this.applicationID, httpUrl, config.developer_key, envCfg.runtimeID);
|
|
186
|
+
this.mqtt = await MQTTClient.getInstance(this.applicationID, mqttUrl, config.developer_key);
|
|
187
|
+
this.nats = await NATSClient.getInstance(this.applicationID, natsUrl);
|
|
173
188
|
this._historian = new Historian(this.http["httpService"], this.mqtt["mqttService"]);
|
|
174
189
|
this._tag = new Tag(this.http["httpService"], this.mqtt["mqttService"]);
|
|
175
190
|
this._function = new Function(this.http["httpService"], this.nats["natsService"]);
|
|
176
191
|
this._flow = new Flow(this.http["httpService"]);
|
|
177
|
-
this._auth = new Auth(this.http["httpService"]);
|
|
192
|
+
this._auth = new Auth(this.http["httpService"], this.applicationID);
|
|
178
193
|
}
|
|
179
194
|
catch (error) {
|
|
180
195
|
console.error("Failed to initialize:", error);
|
|
@@ -282,26 +297,27 @@ async function findConfigEndpoint() {
|
|
|
282
297
|
headers: {
|
|
283
298
|
'Accept': 'application/json',
|
|
284
299
|
},
|
|
285
|
-
signal: AbortSignal.timeout(2000)
|
|
286
|
-
});
|
|
287
|
-
if (testResponse.ok) {
|
|
288
|
-
//
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
catch (jsonError) {
|
|
301
|
-
// Not valid JSON, continue to next candidate
|
|
302
|
-
continue;
|
|
300
|
+
signal: AbortSignal.timeout(2000)
|
|
301
|
+
}).catch(() => { console.log("ERR"); return null; }); // Catch fetch errors silently
|
|
302
|
+
if (!testResponse || !testResponse.ok) {
|
|
303
|
+
continue; // Skip to next candidate
|
|
304
|
+
}
|
|
305
|
+
// Validate that the response is JSON and contains the expected 'port' field
|
|
306
|
+
const contentType = testResponse.headers.get('content-type');
|
|
307
|
+
if (contentType && contentType.includes('application/json')) {
|
|
308
|
+
try {
|
|
309
|
+
const testData = await testResponse.json();
|
|
310
|
+
// Check if the response has the expected structure with a 'port' field
|
|
311
|
+
// TODO: Allow checks for path based hosting as well
|
|
312
|
+
if (testData && typeof testData === 'object' && 'port' in testData) {
|
|
313
|
+
// console.log(`Found config endpoint at: ${configUrl}`);
|
|
314
|
+
return configUrl;
|
|
303
315
|
}
|
|
304
316
|
}
|
|
317
|
+
catch (jsonError) {
|
|
318
|
+
// Not valid JSON, continue to next candidate
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
305
321
|
}
|
|
306
322
|
}
|
|
307
323
|
catch (error) {
|
|
@@ -112,8 +112,13 @@ class RequestParameters {
|
|
|
112
112
|
return this;
|
|
113
113
|
}
|
|
114
114
|
withAccessToken() {
|
|
115
|
-
const
|
|
116
|
-
|
|
115
|
+
const rawAppID = this.applicationID.replace("domains:", "");
|
|
116
|
+
const storageKey = rawAppID
|
|
117
|
+
? `x-machhub-auth-tkn-${rawAppID}`
|
|
118
|
+
: `x-machhub-auth-tkn`;
|
|
119
|
+
const tkn = localStorage.getItem(storageKey);
|
|
120
|
+
if (tkn)
|
|
121
|
+
this.setHeader("Authorization", `Bearer ${tkn}`);
|
|
117
122
|
return this;
|
|
118
123
|
}
|
|
119
124
|
withRuntimeID() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@machhub-dev/sdk-ts",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"description": "MACHHUB TYPESCRIPT SDK",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"machhub",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@nats-io/nats-core": "^3.0.2",
|
|
24
24
|
"@nats-io/transport-node": "^3.0.2",
|
|
25
|
+
"jwt-decode": "^4.0.0",
|
|
25
26
|
"mqtt": "^5.10.4",
|
|
26
27
|
"safe-buffer": "^5.2.1",
|
|
27
28
|
"typescript": "^5.8.3",
|
package/src/classes/auth.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { HTTPService } from "../services/http.service.js";
|
|
2
|
+
import { jwtDecode } from "jwt-decode";
|
|
2
3
|
import { Action, ActionResponse, Feature, Group, LoginResponse, User, ValidateJWTResponse } from "../types/auth.models.js";
|
|
3
4
|
|
|
4
5
|
export class Auth {
|
|
5
6
|
private httpService: HTTPService;
|
|
7
|
+
private applicationID: string;
|
|
8
|
+
private readonly AUTH_TOKEN_KEY_PREFIX = "x-machhub-auth-tkn";
|
|
6
9
|
|
|
7
|
-
constructor(httpService: HTTPService) {
|
|
10
|
+
constructor(httpService: HTTPService, applicationID: string) {
|
|
8
11
|
this.httpService = httpService;
|
|
12
|
+
this.applicationID = applicationID;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private getStorageKey(): string {
|
|
16
|
+
return this.applicationID
|
|
17
|
+
? `${this.AUTH_TOKEN_KEY_PREFIX}-${this.applicationID}`
|
|
18
|
+
: this.AUTH_TOKEN_KEY_PREFIX;
|
|
9
19
|
}
|
|
10
20
|
|
|
11
21
|
public async login(username: string, password: string): Promise<LoginResponse | undefined> {
|
|
@@ -17,7 +27,8 @@ export class Auth {
|
|
|
17
27
|
}).post("/auth/login");
|
|
18
28
|
|
|
19
29
|
if (localStorage) {
|
|
20
|
-
|
|
30
|
+
// console.log("storage key:", this.getStorageKey());
|
|
31
|
+
localStorage.setItem(this.getStorageKey(), res.tkn); // Set User JWT
|
|
21
32
|
} else {
|
|
22
33
|
console.error("localStorage is not available. The program needs to be in a browser environment.");
|
|
23
34
|
}
|
|
@@ -31,13 +42,34 @@ export class Auth {
|
|
|
31
42
|
}
|
|
32
43
|
}
|
|
33
44
|
|
|
34
|
-
public async validateJWT(token: string): Promise<ValidateJWTResponse>{
|
|
35
|
-
|
|
36
|
-
return res
|
|
45
|
+
public async validateJWT(token: string): Promise<ValidateJWTResponse> {
|
|
46
|
+
return await this.httpService.request.withJSON({ token }).post("/auth/jwt/validate");
|
|
37
47
|
}
|
|
38
48
|
|
|
39
49
|
public async logout() {
|
|
40
|
-
localStorage.removeItem(
|
|
50
|
+
localStorage.removeItem(this.getStorageKey());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public async getJWTData(): Promise<any> {
|
|
54
|
+
const token = localStorage.getItem(this.getStorageKey());
|
|
55
|
+
if (!token) {
|
|
56
|
+
throw new Error("No JWT token found in localStorage.");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return jwtDecode(token);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public async getCurrentUser(): Promise<User> {
|
|
63
|
+
return await this.httpService.request.get("/auth/me");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public async validateCurrentUser(): Promise<ValidateJWTResponse> {
|
|
67
|
+
const token = localStorage.getItem(this.getStorageKey());
|
|
68
|
+
if (!token) {
|
|
69
|
+
throw new Error("No JWT token found in localStorage.");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return await this.validateJWT(token);
|
|
41
73
|
}
|
|
42
74
|
|
|
43
75
|
public async checkAction(feature: string, scope: string): Promise<ActionResponse> {
|
|
@@ -48,8 +48,32 @@ export class Collection {
|
|
|
48
48
|
return this;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
expand(fields: string | string[]): Collection {
|
|
52
|
+
this.queryParams.expand = Array.isArray(fields) ? fields.join(",") : fields;
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private applyOptions(options?: Record<string, any>) {
|
|
57
|
+
if (!options) return;
|
|
58
|
+
|
|
59
|
+
for (const [key, value] of Object.entries(options)) {
|
|
60
|
+
if (value !== undefined && value !== null) {
|
|
61
|
+
this.queryParams[key] = value;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async first(): Promise<any> {
|
|
67
|
+
const results = await this.limit(1).getAll();
|
|
68
|
+
return results[0] ?? null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async getAll(options?: { expand?: string | string[] }): Promise<any[]> {
|
|
52
72
|
try {
|
|
73
|
+
this.applyOptions(options)
|
|
74
|
+
if (options?.expand) {
|
|
75
|
+
this.queryParams.expand = Array.isArray(options.expand) ? options.expand.join() : options.expand
|
|
76
|
+
}
|
|
53
77
|
return await this.httpService.request.get(this.collectionName + "/all", this.queryParams);
|
|
54
78
|
} catch (error) {
|
|
55
79
|
throw new CollectionError('getAll', this.collectionName, error as Error);
|
package/src/sdk-ts.ts
CHANGED
|
@@ -137,6 +137,19 @@ export class SDK {
|
|
|
137
137
|
private _function: Function | null = null;
|
|
138
138
|
private _flow: Flow | null = null;
|
|
139
139
|
private _auth: Auth | null = null;
|
|
140
|
+
private applicationID: string = "";
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Extracts the application ID from a runtime ID.
|
|
144
|
+
* Runtime ID format: {applicationID}XmchX{randomID}
|
|
145
|
+
* @param runtimeID The runtime ID to extract from
|
|
146
|
+
* @returns The extracted application ID, or empty string if invalid format
|
|
147
|
+
*/
|
|
148
|
+
private extractApplicationIDFromRuntimeID(runtimeID: string): string {
|
|
149
|
+
if (!runtimeID) return "";
|
|
150
|
+
const parts = runtimeID.split('XmchX');
|
|
151
|
+
return parts.length > 0 ? parts[0] : "";
|
|
152
|
+
}
|
|
140
153
|
|
|
141
154
|
/**
|
|
142
155
|
* Initializes the SDK with the required clients.
|
|
@@ -167,13 +180,16 @@ export class SDK {
|
|
|
167
180
|
// 1. Via application_id + URLs + developer key passed in config parameter
|
|
168
181
|
// 2. Via development server - Set via Extension/
|
|
169
182
|
// API to get Config (All SDK URLs default to localhost with port from querying current window or 61888)
|
|
170
|
-
|
|
171
183
|
if (config === undefined) config = { application_id: "" }
|
|
172
184
|
if (!config.application_id) config = { application_id: "" }
|
|
173
|
-
// console.log("Using application_id:", config.application_id);
|
|
174
185
|
|
|
175
186
|
const envCfg = await getEnvConfig();
|
|
176
|
-
|
|
187
|
+
|
|
188
|
+
// Extract application_id from runtimeID if not provided in config
|
|
189
|
+
const application_id = config.application_id ||
|
|
190
|
+
this.extractApplicationIDFromRuntimeID(envCfg.runtimeID);
|
|
191
|
+
|
|
192
|
+
this.applicationID = application_id;
|
|
177
193
|
|
|
178
194
|
// Determine the hostname - use window.location.hostname in browser, otherwise fallback to localhost
|
|
179
195
|
const hostname = typeof window !== 'undefined' ? window.location.hostname : 'localhost';
|
|
@@ -197,19 +213,20 @@ export class SDK {
|
|
|
197
213
|
if (!config.natsUrl) {
|
|
198
214
|
config.natsUrl = `${secured ? 'wss' : 'ws'}://${host}/nats`;
|
|
199
215
|
}
|
|
200
|
-
|
|
216
|
+
|
|
217
|
+
const { httpUrl, mqttUrl, natsUrl } = config;
|
|
201
218
|
|
|
202
|
-
console.log("SDK Config:", { application_id, httpUrl, mqttUrl, natsUrl, developer_key: config.developer_key?.split('').map((_, i) => i < config!.developer_key!.length - 4 ? '*' : config!.developer_key![i]).join('') });
|
|
219
|
+
console.log("SDK Config:", { application_id: this.applicationID, httpUrl, mqttUrl, natsUrl, developer_key: config.developer_key?.split('').map((_, i) => i < config!.developer_key!.length - 4 ? '*' : config!.developer_key![i]).join('') });
|
|
203
220
|
|
|
204
|
-
this.http = new HTTPClient(
|
|
205
|
-
this.mqtt = await MQTTClient.getInstance(
|
|
206
|
-
this.nats = await NATSClient.getInstance(
|
|
221
|
+
this.http = new HTTPClient(this.applicationID, httpUrl, config.developer_key, envCfg.runtimeID);
|
|
222
|
+
this.mqtt = await MQTTClient.getInstance(this.applicationID, mqttUrl, config.developer_key);
|
|
223
|
+
this.nats = await NATSClient.getInstance(this.applicationID, natsUrl);
|
|
207
224
|
|
|
208
225
|
this._historian = new Historian(this.http["httpService"], this.mqtt["mqttService"]);
|
|
209
226
|
this._tag = new Tag(this.http["httpService"], this.mqtt["mqttService"]);
|
|
210
227
|
this._function = new Function(this.http["httpService"], this.nats["natsService"]);
|
|
211
228
|
this._flow = new Flow(this.http["httpService"]);
|
|
212
|
-
this._auth = new Auth(this.http["httpService"]);
|
|
229
|
+
this._auth = new Auth(this.http["httpService"], this.applicationID);
|
|
213
230
|
} catch (error: any) {
|
|
214
231
|
console.error("Failed to initialize:", error);
|
|
215
232
|
return false;
|
|
@@ -338,24 +355,26 @@ async function findConfigEndpoint(): Promise<string> {
|
|
|
338
355
|
'Accept': 'application/json',
|
|
339
356
|
},
|
|
340
357
|
signal: AbortSignal.timeout(2000)
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
if (testResponse.ok) {
|
|
344
|
-
//
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
continue;
|
|
358
|
+
}).catch(() => {console.log("ERR"); return null}); // Catch fetch errors silently
|
|
359
|
+
|
|
360
|
+
if (!testResponse || !testResponse.ok) {
|
|
361
|
+
continue; // Skip to next candidate
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Validate that the response is JSON and contains the expected 'port' field
|
|
365
|
+
const contentType = testResponse.headers.get('content-type');
|
|
366
|
+
if (contentType && contentType.includes('application/json')) {
|
|
367
|
+
try {
|
|
368
|
+
const testData = await testResponse.json();
|
|
369
|
+
// Check if the response has the expected structure with a 'port' field
|
|
370
|
+
// TODO: Allow checks for path based hosting as well
|
|
371
|
+
if (testData && typeof testData === 'object' && 'port' in testData) {
|
|
372
|
+
// console.log(`Found config endpoint at: ${configUrl}`);
|
|
373
|
+
return configUrl;
|
|
358
374
|
}
|
|
375
|
+
} catch (jsonError) {
|
|
376
|
+
// Not valid JSON, continue to next candidate
|
|
377
|
+
continue;
|
|
359
378
|
}
|
|
360
379
|
}
|
|
361
380
|
} catch (error) {
|
|
@@ -147,8 +147,12 @@ class RequestParameters {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
public withAccessToken(): RequestParameters {
|
|
150
|
-
const
|
|
151
|
-
|
|
150
|
+
const rawAppID = this.applicationID.replace("domains:", "");
|
|
151
|
+
const storageKey = rawAppID
|
|
152
|
+
? `x-machhub-auth-tkn-${rawAppID}`
|
|
153
|
+
: `x-machhub-auth-tkn`;
|
|
154
|
+
const tkn = localStorage.getItem(storageKey);
|
|
155
|
+
if (tkn) this.setHeader("Authorization", `Bearer ${tkn}`);
|
|
152
156
|
return this;
|
|
153
157
|
}
|
|
154
158
|
|