intgate 1.0.0
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/README.md +325 -0
- package/dist/client.d.ts +103 -0
- package/dist/client.js +252 -0
- package/dist/exceptions.d.ts +23 -0
- package/dist/exceptions.js +41 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +28 -0
- package/dist/types.d.ts +84 -0
- package/dist/types.js +5 -0
- package/package.json +32 -0
- package/src/client.ts +272 -0
- package/src/exceptions.ts +41 -0
- package/src/index.ts +8 -0
- package/src/types.ts +95 -0
- package/tsconfig.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# IntGate Node.js/TypeScript Client Library
|
|
2
|
+
|
|
3
|
+
Node.js and TypeScript client library for the [IntGate](https://license.intserver.com) license verification API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install intgate
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or with yarn:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
yarn add intgate
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
### TypeScript
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { IntGateClient } from 'intgate';
|
|
23
|
+
|
|
24
|
+
// Initialize the client with your team ID
|
|
25
|
+
const client = new IntGateClient('your-team-uuid-here');
|
|
26
|
+
|
|
27
|
+
// Verify a license
|
|
28
|
+
async function verifyLicense() {
|
|
29
|
+
try {
|
|
30
|
+
const result = await client.verifyLicense({
|
|
31
|
+
licenseKey: 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX',
|
|
32
|
+
hardwareIdentifier: 'unique-device-id',
|
|
33
|
+
version: '1.0.0'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (result.result.valid) {
|
|
37
|
+
console.log('License is valid!');
|
|
38
|
+
console.log('License data:', result.data);
|
|
39
|
+
} else {
|
|
40
|
+
console.log('License invalid:', result.result.details);
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error('Error:', error);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
verifyLicense();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### JavaScript
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
const { IntGateClient } = require('intgate');
|
|
54
|
+
|
|
55
|
+
// Initialize the client with your team ID
|
|
56
|
+
const client = new IntGateClient('your-team-uuid-here');
|
|
57
|
+
|
|
58
|
+
// Verify a license
|
|
59
|
+
async function verifyLicense() {
|
|
60
|
+
try {
|
|
61
|
+
const result = await client.verifyLicense({
|
|
62
|
+
licenseKey: 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX',
|
|
63
|
+
hardwareIdentifier: 'unique-device-id',
|
|
64
|
+
version: '1.0.0'
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (result.result.valid) {
|
|
68
|
+
console.log('License is valid!');
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error('Error:', error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## API Reference
|
|
77
|
+
|
|
78
|
+
### Initialize Client
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { IntGateClient } from 'intgate';
|
|
82
|
+
|
|
83
|
+
const client = new IntGateClient(
|
|
84
|
+
'your-team-uuid', // Required: Your team's UUID from IntGate dashboard
|
|
85
|
+
'https://license.intserver.com/api/v1' // Optional: Custom API base URL
|
|
86
|
+
);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Verify License
|
|
90
|
+
|
|
91
|
+
Validates a license key and returns license information, customer data, and product details.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const result = await client.verifyLicense({
|
|
95
|
+
licenseKey: 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX', // Required
|
|
96
|
+
customerId: 'customer-uuid', // Optional: Required if strict customers enabled
|
|
97
|
+
productId: 'product-uuid', // Optional: Required if strict products enabled
|
|
98
|
+
challenge: 'random-string', // Optional: For request signing
|
|
99
|
+
version: '1.0.0', // Optional: Software version (3-255 chars)
|
|
100
|
+
hardwareIdentifier: 'device-id', // Optional: Unique device ID (10-1000 chars)
|
|
101
|
+
branch: 'main' // Optional: Product branch (2-255 chars)
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### License Heartbeat
|
|
106
|
+
|
|
107
|
+
Send periodic heartbeats to determine if a device is still active.
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const result = await client.licenseHeartbeat({
|
|
111
|
+
licenseKey: 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX', // Required
|
|
112
|
+
hardwareIdentifier: 'device-id', // Required: Unique device ID (10-1000 chars)
|
|
113
|
+
customerId: 'customer-uuid', // Optional
|
|
114
|
+
productId: 'product-uuid', // Optional
|
|
115
|
+
challenge: 'random-string', // Optional
|
|
116
|
+
version: '1.0.0', // Optional: Software version (3-255 chars)
|
|
117
|
+
branch: 'main' // Optional: Product branch (2-255 chars)
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Download Release
|
|
122
|
+
|
|
123
|
+
Download an encrypted release file.
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const encryptedFile = await client.downloadRelease({
|
|
127
|
+
licenseKey: 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX', // Required
|
|
128
|
+
productId: 'product-uuid', // Required
|
|
129
|
+
sessionKey: 'encrypted-session-key', // Required: Encrypted with team's public key
|
|
130
|
+
hardwareIdentifier: 'device-id', // Required: Unique device ID
|
|
131
|
+
version: '1.0.0', // Required: Software version
|
|
132
|
+
customerId: 'customer-uuid', // Optional
|
|
133
|
+
branch: 'main' // Optional: Product branch
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Save to file:
|
|
137
|
+
import fs from 'fs';
|
|
138
|
+
fs.writeFileSync('release.encrypted', encryptedFile);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Automatic Heartbeat
|
|
142
|
+
|
|
143
|
+
Start automatic heartbeat in the background with event-based notifications.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { IntGateClient } from 'intgate';
|
|
147
|
+
|
|
148
|
+
const client = new IntGateClient('your-team-uuid');
|
|
149
|
+
|
|
150
|
+
// Listen for heartbeat events
|
|
151
|
+
client.on('heartbeat', (result) => {
|
|
152
|
+
console.log(`✓ Heartbeat OK at ${result.result.timestamp}`);
|
|
153
|
+
console.log(` License valid: ${result.result.valid}`);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
client.on('heartbeat-error', (error) => {
|
|
157
|
+
console.error('✗ Heartbeat failed:', error);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Start automatic heartbeat (runs with setTimeout)
|
|
161
|
+
client.startAutomaticHeartbeat(
|
|
162
|
+
{
|
|
163
|
+
licenseKey: 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX',
|
|
164
|
+
hardwareIdentifier: 'device-12345',
|
|
165
|
+
version: '1.0.0'
|
|
166
|
+
},
|
|
167
|
+
1800000 // 30 minutes in milliseconds
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
console.log('Application running with automatic heartbeat...');
|
|
171
|
+
|
|
172
|
+
// Get last heartbeat result anytime
|
|
173
|
+
const lastResult = client.getLastHeartbeatResult();
|
|
174
|
+
if (lastResult?.result.valid) {
|
|
175
|
+
console.log('License is active');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Check if heartbeat is running
|
|
179
|
+
if (client.isHeartbeatRunning()) {
|
|
180
|
+
console.log('Automatic heartbeat is active');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Stop when done
|
|
184
|
+
client.stopAutomaticHeartbeat();
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Error Handling
|
|
188
|
+
|
|
189
|
+
The library provides specific exception types:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import {
|
|
193
|
+
IntGateClient,
|
|
194
|
+
IntGateAPIError,
|
|
195
|
+
IntGateValidationError
|
|
196
|
+
} from 'intgate';
|
|
197
|
+
|
|
198
|
+
const client = new IntGateClient('your-team-uuid');
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const result = await client.verifyLicense({
|
|
202
|
+
licenseKey: 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX'
|
|
203
|
+
});
|
|
204
|
+
} catch (error) {
|
|
205
|
+
if (error instanceof IntGateValidationError) {
|
|
206
|
+
// Input validation failed
|
|
207
|
+
console.error('Validation error:', error.message);
|
|
208
|
+
} else if (error instanceof IntGateAPIError) {
|
|
209
|
+
// API request failed
|
|
210
|
+
console.error('API error:', error.message);
|
|
211
|
+
console.error('Status code:', error.statusCode);
|
|
212
|
+
console.error('Response data:', error.responseData);
|
|
213
|
+
} else {
|
|
214
|
+
// Other errors
|
|
215
|
+
console.error('Unexpected error:', error);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Complete Example
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { IntGateClient, IntGateAPIError } from 'intgate';
|
|
224
|
+
|
|
225
|
+
// Initialize client
|
|
226
|
+
const client = new IntGateClient('your-team-uuid-here');
|
|
227
|
+
|
|
228
|
+
async function main() {
|
|
229
|
+
try {
|
|
230
|
+
// Verify license on startup
|
|
231
|
+
console.log('Verifying license...');
|
|
232
|
+
const result = await client.verifyLicense({
|
|
233
|
+
licenseKey: 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX',
|
|
234
|
+
hardwareIdentifier: 'my-device-12345',
|
|
235
|
+
version: '1.0.0',
|
|
236
|
+
productId: 'your-product-uuid'
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (!result.result.valid) {
|
|
240
|
+
console.error('License validation failed:', result.result.details);
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
console.log('License verified successfully!');
|
|
245
|
+
console.log('Expires:', result.data.license.expirationDate || 'Never');
|
|
246
|
+
|
|
247
|
+
// Setup automatic heartbeat with event listeners
|
|
248
|
+
console.log('\nStarting automatic heartbeat...');
|
|
249
|
+
|
|
250
|
+
client.on('heartbeat', (result) => {
|
|
251
|
+
console.log('Heartbeat successful');
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
client.on('heartbeat-error', (error) => {
|
|
255
|
+
console.error('Heartbeat failed:', error);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
client.startAutomaticHeartbeat(
|
|
259
|
+
{
|
|
260
|
+
licenseKey: 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX',
|
|
261
|
+
hardwareIdentifier: 'my-device-12345',
|
|
262
|
+
version: '1.0.0'
|
|
263
|
+
},
|
|
264
|
+
1800000 // Every 30 minutes
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
console.log('Application running...');
|
|
268
|
+
|
|
269
|
+
} catch (error) {
|
|
270
|
+
if (error instanceof IntGateAPIError) {
|
|
271
|
+
console.error('API Error:', error.message);
|
|
272
|
+
if (error.statusCode === 404) {
|
|
273
|
+
console.error('License not found');
|
|
274
|
+
} else if (error.statusCode === 401) {
|
|
275
|
+
console.error('Unauthorized - check your team ID');
|
|
276
|
+
}
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
throw error;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
main();
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## TypeScript Support
|
|
287
|
+
|
|
288
|
+
This library is written in TypeScript and includes full type definitions:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
import {
|
|
292
|
+
IntGateClient,
|
|
293
|
+
VerifyLicenseOptions,
|
|
294
|
+
VerifyLicenseResponse,
|
|
295
|
+
LicenseHeartbeatOptions,
|
|
296
|
+
HeartbeatResponse,
|
|
297
|
+
DownloadReleaseOptions,
|
|
298
|
+
License,
|
|
299
|
+
Customer,
|
|
300
|
+
Product,
|
|
301
|
+
VerificationResult
|
|
302
|
+
} from 'intgate';
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Build from Source
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
# Install dependencies
|
|
309
|
+
npm install
|
|
310
|
+
|
|
311
|
+
# Build TypeScript to JavaScript
|
|
312
|
+
npm run build
|
|
313
|
+
|
|
314
|
+
# The built files will be in the dist/ directory
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## License
|
|
318
|
+
|
|
319
|
+
MIT License
|
|
320
|
+
|
|
321
|
+
## Support
|
|
322
|
+
|
|
323
|
+
For API documentation, visit: https://license.intserver.com
|
|
324
|
+
|
|
325
|
+
For examples, see: [AUTOMATIC_HEARTBEAT.md](../AUTOMATIC_HEARTBEAT.md)
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IntGate API Client implementation for Node.js/TypeScript.
|
|
3
|
+
*/
|
|
4
|
+
import { EventEmitter } from 'events';
|
|
5
|
+
import { VerifyLicenseOptions, LicenseHeartbeatOptions, DownloadReleaseOptions, VerifyLicenseResponse, HeartbeatResponse } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Client for interacting with the IntGate license verification API.
|
|
8
|
+
*/
|
|
9
|
+
export declare class IntGateClient extends EventEmitter {
|
|
10
|
+
private teamId;
|
|
11
|
+
private baseUrl;
|
|
12
|
+
private axiosInstance;
|
|
13
|
+
private heartbeatTimer;
|
|
14
|
+
private heartbeatRunning;
|
|
15
|
+
private lastHeartbeatResult;
|
|
16
|
+
/**
|
|
17
|
+
* Initialize the IntGate client.
|
|
18
|
+
* @param teamId Your team's UUID from the IntGate dashboard.
|
|
19
|
+
* @param baseUrl The base URL for the IntGate API (default: https://license.intserver.com/api/v1).
|
|
20
|
+
*/
|
|
21
|
+
constructor(teamId: string, baseUrl?: string);
|
|
22
|
+
/**
|
|
23
|
+
* Verify a license key with the IntGate backend.
|
|
24
|
+
*
|
|
25
|
+
* This endpoint validates a license key and returns license information,
|
|
26
|
+
* customer data, and product details. Typically called when software starts.
|
|
27
|
+
*
|
|
28
|
+
* @param options License verification options
|
|
29
|
+
* @returns Promise resolving to verification response
|
|
30
|
+
* @throws {IntGateValidationError} If required parameters are invalid
|
|
31
|
+
* @throws {IntGateAPIError} If the API request fails
|
|
32
|
+
*/
|
|
33
|
+
verifyLicense(options: VerifyLicenseOptions): Promise<VerifyLicenseResponse>;
|
|
34
|
+
/**
|
|
35
|
+
* Send a heartbeat to determine if a device is still active.
|
|
36
|
+
*
|
|
37
|
+
* This endpoint should be called at regular intervals (e.g., every 30 minutes).
|
|
38
|
+
* It validates the license key similar to verifyLicense.
|
|
39
|
+
*
|
|
40
|
+
* @param options Heartbeat options
|
|
41
|
+
* @returns Promise resolving to heartbeat response
|
|
42
|
+
* @throws {IntGateValidationError} If required parameters are invalid
|
|
43
|
+
* @throws {IntGateAPIError} If the API request fails
|
|
44
|
+
*/
|
|
45
|
+
licenseHeartbeat(options: LicenseHeartbeatOptions): Promise<HeartbeatResponse>;
|
|
46
|
+
/**
|
|
47
|
+
* Download an encrypted release file.
|
|
48
|
+
*
|
|
49
|
+
* This endpoint is primarily for languages that support loading code remotely
|
|
50
|
+
* (e.g., Java classloaders). The file is encrypted using the provided session key.
|
|
51
|
+
*
|
|
52
|
+
* @param options Download release options
|
|
53
|
+
* @returns Promise resolving to encrypted file content (Buffer)
|
|
54
|
+
* @throws {IntGateValidationError} If required parameters are invalid
|
|
55
|
+
* @throws {IntGateAPIError} If the API request fails
|
|
56
|
+
*/
|
|
57
|
+
downloadRelease(options: DownloadReleaseOptions): Promise<Buffer>;
|
|
58
|
+
/**
|
|
59
|
+
* Start automatic heartbeat in the background.
|
|
60
|
+
*
|
|
61
|
+
* The client will emit 'heartbeat' events with the result on success,
|
|
62
|
+
* and 'heartbeat-error' events on errors.
|
|
63
|
+
*
|
|
64
|
+
* @param options Heartbeat options
|
|
65
|
+
* @param interval Interval in milliseconds between heartbeats (default: 1800000 = 30 minutes)
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* client.startAutomaticHeartbeat({
|
|
69
|
+
* licenseKey: 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX',
|
|
70
|
+
* hardwareIdentifier: 'device-123'
|
|
71
|
+
* }, 60000); // Every 1 minute
|
|
72
|
+
*
|
|
73
|
+
* client.on('heartbeat', (result) => {
|
|
74
|
+
* console.log('Heartbeat successful:', result);
|
|
75
|
+
* });
|
|
76
|
+
*
|
|
77
|
+
* client.on('heartbeat-error', (error) => {
|
|
78
|
+
* console.error('Heartbeat failed:', error);
|
|
79
|
+
* });
|
|
80
|
+
*/
|
|
81
|
+
startAutomaticHeartbeat(options: LicenseHeartbeatOptions, interval?: number): void;
|
|
82
|
+
/**
|
|
83
|
+
* Stop the automatic heartbeat.
|
|
84
|
+
*/
|
|
85
|
+
stopAutomaticHeartbeat(): void;
|
|
86
|
+
/**
|
|
87
|
+
* Get the result of the last automatic heartbeat.
|
|
88
|
+
*
|
|
89
|
+
* @returns The last heartbeat result, or null if no heartbeat has run yet
|
|
90
|
+
*/
|
|
91
|
+
getLastHeartbeatResult(): HeartbeatResponse | null;
|
|
92
|
+
/**
|
|
93
|
+
* Check if automatic heartbeat is currently running.
|
|
94
|
+
*
|
|
95
|
+
* @returns True if heartbeat is running, false otherwise
|
|
96
|
+
*/
|
|
97
|
+
isHeartbeatRunning(): boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Handle errors from API requests.
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
private handleError;
|
|
103
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* IntGate API Client implementation for Node.js/TypeScript.
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.IntGateClient = void 0;
|
|
10
|
+
const axios_1 = __importDefault(require("axios"));
|
|
11
|
+
const events_1 = require("events");
|
|
12
|
+
const exceptions_1 = require("./exceptions");
|
|
13
|
+
/**
|
|
14
|
+
* Client for interacting with the IntGate license verification API.
|
|
15
|
+
*/
|
|
16
|
+
class IntGateClient extends events_1.EventEmitter {
|
|
17
|
+
/**
|
|
18
|
+
* Initialize the IntGate client.
|
|
19
|
+
* @param teamId Your team's UUID from the IntGate dashboard.
|
|
20
|
+
* @param baseUrl The base URL for the IntGate API (default: https://license.intserver.com/api/v1).
|
|
21
|
+
*/
|
|
22
|
+
constructor(teamId, baseUrl = 'https://license.intserver.com/api/v1') {
|
|
23
|
+
super();
|
|
24
|
+
this.heartbeatTimer = null;
|
|
25
|
+
this.heartbeatRunning = false;
|
|
26
|
+
this.lastHeartbeatResult = null;
|
|
27
|
+
if (!teamId) {
|
|
28
|
+
throw new exceptions_1.IntGateValidationError('teamId is required');
|
|
29
|
+
}
|
|
30
|
+
this.teamId = teamId;
|
|
31
|
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
32
|
+
this.axiosInstance = axios_1.default.create({
|
|
33
|
+
headers: {
|
|
34
|
+
'Content-Type': 'application/json',
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Verify a license key with the IntGate backend.
|
|
40
|
+
*
|
|
41
|
+
* This endpoint validates a license key and returns license information,
|
|
42
|
+
* customer data, and product details. Typically called when software starts.
|
|
43
|
+
*
|
|
44
|
+
* @param options License verification options
|
|
45
|
+
* @returns Promise resolving to verification response
|
|
46
|
+
* @throws {IntGateValidationError} If required parameters are invalid
|
|
47
|
+
* @throws {IntGateAPIError} If the API request fails
|
|
48
|
+
*/
|
|
49
|
+
async verifyLicense(options) {
|
|
50
|
+
if (!options.licenseKey) {
|
|
51
|
+
throw new exceptions_1.IntGateValidationError('licenseKey is required');
|
|
52
|
+
}
|
|
53
|
+
const payload = {
|
|
54
|
+
licenseKey: options.licenseKey,
|
|
55
|
+
};
|
|
56
|
+
if (options.customerId)
|
|
57
|
+
payload.customerId = options.customerId;
|
|
58
|
+
if (options.productId)
|
|
59
|
+
payload.productId = options.productId;
|
|
60
|
+
if (options.challenge)
|
|
61
|
+
payload.challenge = options.challenge;
|
|
62
|
+
if (options.version)
|
|
63
|
+
payload.version = options.version;
|
|
64
|
+
if (options.hardwareIdentifier)
|
|
65
|
+
payload.hardwareIdentifier = options.hardwareIdentifier;
|
|
66
|
+
if (options.branch)
|
|
67
|
+
payload.branch = options.branch;
|
|
68
|
+
const url = `${this.baseUrl}/client/teams/${this.teamId}/verification/verify`;
|
|
69
|
+
try {
|
|
70
|
+
const response = await this.axiosInstance.post(url, payload);
|
|
71
|
+
return response.data;
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
this.handleError(error);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Send a heartbeat to determine if a device is still active.
|
|
79
|
+
*
|
|
80
|
+
* This endpoint should be called at regular intervals (e.g., every 30 minutes).
|
|
81
|
+
* It validates the license key similar to verifyLicense.
|
|
82
|
+
*
|
|
83
|
+
* @param options Heartbeat options
|
|
84
|
+
* @returns Promise resolving to heartbeat response
|
|
85
|
+
* @throws {IntGateValidationError} If required parameters are invalid
|
|
86
|
+
* @throws {IntGateAPIError} If the API request fails
|
|
87
|
+
*/
|
|
88
|
+
async licenseHeartbeat(options) {
|
|
89
|
+
if (!options.licenseKey) {
|
|
90
|
+
throw new exceptions_1.IntGateValidationError('licenseKey is required');
|
|
91
|
+
}
|
|
92
|
+
if (!options.hardwareIdentifier) {
|
|
93
|
+
throw new exceptions_1.IntGateValidationError('hardwareIdentifier is required');
|
|
94
|
+
}
|
|
95
|
+
const payload = {
|
|
96
|
+
licenseKey: options.licenseKey,
|
|
97
|
+
hardwareIdentifier: options.hardwareIdentifier,
|
|
98
|
+
};
|
|
99
|
+
if (options.customerId)
|
|
100
|
+
payload.customerId = options.customerId;
|
|
101
|
+
if (options.productId)
|
|
102
|
+
payload.productId = options.productId;
|
|
103
|
+
if (options.challenge)
|
|
104
|
+
payload.challenge = options.challenge;
|
|
105
|
+
if (options.version)
|
|
106
|
+
payload.version = options.version;
|
|
107
|
+
if (options.branch)
|
|
108
|
+
payload.branch = options.branch;
|
|
109
|
+
const url = `${this.baseUrl}/client/teams/${this.teamId}/verification/heartbeat`;
|
|
110
|
+
try {
|
|
111
|
+
const response = await this.axiosInstance.post(url, payload);
|
|
112
|
+
return response.data;
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
this.handleError(error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Download an encrypted release file.
|
|
120
|
+
*
|
|
121
|
+
* This endpoint is primarily for languages that support loading code remotely
|
|
122
|
+
* (e.g., Java classloaders). The file is encrypted using the provided session key.
|
|
123
|
+
*
|
|
124
|
+
* @param options Download release options
|
|
125
|
+
* @returns Promise resolving to encrypted file content (Buffer)
|
|
126
|
+
* @throws {IntGateValidationError} If required parameters are invalid
|
|
127
|
+
* @throws {IntGateAPIError} If the API request fails
|
|
128
|
+
*/
|
|
129
|
+
async downloadRelease(options) {
|
|
130
|
+
if (!options.licenseKey) {
|
|
131
|
+
throw new exceptions_1.IntGateValidationError('licenseKey is required');
|
|
132
|
+
}
|
|
133
|
+
if (!options.productId) {
|
|
134
|
+
throw new exceptions_1.IntGateValidationError('productId is required');
|
|
135
|
+
}
|
|
136
|
+
if (!options.sessionKey) {
|
|
137
|
+
throw new exceptions_1.IntGateValidationError('sessionKey is required');
|
|
138
|
+
}
|
|
139
|
+
if (!options.hardwareIdentifier) {
|
|
140
|
+
throw new exceptions_1.IntGateValidationError('hardwareIdentifier is required');
|
|
141
|
+
}
|
|
142
|
+
if (!options.version) {
|
|
143
|
+
throw new exceptions_1.IntGateValidationError('version is required');
|
|
144
|
+
}
|
|
145
|
+
const params = {
|
|
146
|
+
licenseKey: options.licenseKey,
|
|
147
|
+
productId: options.productId,
|
|
148
|
+
sessionKey: options.sessionKey,
|
|
149
|
+
hardwareIdentifier: options.hardwareIdentifier,
|
|
150
|
+
version: options.version,
|
|
151
|
+
};
|
|
152
|
+
if (options.customerId)
|
|
153
|
+
params.customerId = options.customerId;
|
|
154
|
+
if (options.branch)
|
|
155
|
+
params.branch = options.branch;
|
|
156
|
+
const url = `${this.baseUrl}/client/teams/${this.teamId}/verification/classloader`;
|
|
157
|
+
try {
|
|
158
|
+
const response = await this.axiosInstance.get(url, {
|
|
159
|
+
params,
|
|
160
|
+
responseType: 'arraybuffer',
|
|
161
|
+
});
|
|
162
|
+
return Buffer.from(response.data);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
this.handleError(error);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Start automatic heartbeat in the background.
|
|
170
|
+
*
|
|
171
|
+
* The client will emit 'heartbeat' events with the result on success,
|
|
172
|
+
* and 'heartbeat-error' events on errors.
|
|
173
|
+
*
|
|
174
|
+
* @param options Heartbeat options
|
|
175
|
+
* @param interval Interval in milliseconds between heartbeats (default: 1800000 = 30 minutes)
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* client.startAutomaticHeartbeat({
|
|
179
|
+
* licenseKey: 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX',
|
|
180
|
+
* hardwareIdentifier: 'device-123'
|
|
181
|
+
* }, 60000); // Every 1 minute
|
|
182
|
+
*
|
|
183
|
+
* client.on('heartbeat', (result) => {
|
|
184
|
+
* console.log('Heartbeat successful:', result);
|
|
185
|
+
* });
|
|
186
|
+
*
|
|
187
|
+
* client.on('heartbeat-error', (error) => {
|
|
188
|
+
* console.error('Heartbeat failed:', error);
|
|
189
|
+
* });
|
|
190
|
+
*/
|
|
191
|
+
startAutomaticHeartbeat(options, interval = 1800000) {
|
|
192
|
+
if (this.heartbeatRunning) {
|
|
193
|
+
throw new exceptions_1.IntGateValidationError('Automatic heartbeat is already running');
|
|
194
|
+
}
|
|
195
|
+
this.heartbeatRunning = true;
|
|
196
|
+
const runHeartbeat = async () => {
|
|
197
|
+
try {
|
|
198
|
+
const result = await this.licenseHeartbeat(options);
|
|
199
|
+
this.lastHeartbeatResult = result;
|
|
200
|
+
this.emit('heartbeat', result);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
this.emit('heartbeat-error', error);
|
|
204
|
+
}
|
|
205
|
+
if (this.heartbeatRunning) {
|
|
206
|
+
this.heartbeatTimer = setTimeout(runHeartbeat, interval);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
// Run immediately, then schedule
|
|
210
|
+
runHeartbeat();
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Stop the automatic heartbeat.
|
|
214
|
+
*/
|
|
215
|
+
stopAutomaticHeartbeat() {
|
|
216
|
+
this.heartbeatRunning = false;
|
|
217
|
+
if (this.heartbeatTimer) {
|
|
218
|
+
clearTimeout(this.heartbeatTimer);
|
|
219
|
+
this.heartbeatTimer = null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Get the result of the last automatic heartbeat.
|
|
224
|
+
*
|
|
225
|
+
* @returns The last heartbeat result, or null if no heartbeat has run yet
|
|
226
|
+
*/
|
|
227
|
+
getLastHeartbeatResult() {
|
|
228
|
+
return this.lastHeartbeatResult;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Check if automatic heartbeat is currently running.
|
|
232
|
+
*
|
|
233
|
+
* @returns True if heartbeat is running, false otherwise
|
|
234
|
+
*/
|
|
235
|
+
isHeartbeatRunning() {
|
|
236
|
+
return this.heartbeatRunning;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Handle errors from API requests.
|
|
240
|
+
* @private
|
|
241
|
+
*/
|
|
242
|
+
handleError(error) {
|
|
243
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
244
|
+
const axiosError = error;
|
|
245
|
+
const statusCode = axiosError.response?.status;
|
|
246
|
+
const responseData = axiosError.response?.data;
|
|
247
|
+
throw new exceptions_1.IntGateAPIError(`API request failed: ${axiosError.message}`, statusCode, responseData);
|
|
248
|
+
}
|
|
249
|
+
throw new exceptions_1.IntGateAPIError(`Network error: ${error.message}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
exports.IntGateClient = IntGateClient;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error classes for the IntGate API client.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Base error class for all IntGate errors.
|
|
6
|
+
*/
|
|
7
|
+
export declare class IntGateError extends Error {
|
|
8
|
+
constructor(message: string);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Error thrown when the API returns an error response.
|
|
12
|
+
*/
|
|
13
|
+
export declare class IntGateAPIError extends IntGateError {
|
|
14
|
+
statusCode?: number;
|
|
15
|
+
responseData?: any;
|
|
16
|
+
constructor(message: string, statusCode?: number, responseData?: any);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Error thrown when input validation fails.
|
|
20
|
+
*/
|
|
21
|
+
export declare class IntGateValidationError extends IntGateError {
|
|
22
|
+
constructor(message: string);
|
|
23
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Custom error classes for the IntGate API client.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.IntGateValidationError = exports.IntGateAPIError = exports.IntGateError = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Base error class for all IntGate errors.
|
|
9
|
+
*/
|
|
10
|
+
class IntGateError extends Error {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'IntGateError';
|
|
14
|
+
Object.setPrototypeOf(this, IntGateError.prototype);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.IntGateError = IntGateError;
|
|
18
|
+
/**
|
|
19
|
+
* Error thrown when the API returns an error response.
|
|
20
|
+
*/
|
|
21
|
+
class IntGateAPIError extends IntGateError {
|
|
22
|
+
constructor(message, statusCode, responseData) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = 'IntGateAPIError';
|
|
25
|
+
this.statusCode = statusCode;
|
|
26
|
+
this.responseData = responseData;
|
|
27
|
+
Object.setPrototypeOf(this, IntGateAPIError.prototype);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.IntGateAPIError = IntGateAPIError;
|
|
31
|
+
/**
|
|
32
|
+
* Error thrown when input validation fails.
|
|
33
|
+
*/
|
|
34
|
+
class IntGateValidationError extends IntGateError {
|
|
35
|
+
constructor(message) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = 'IntGateValidationError';
|
|
38
|
+
Object.setPrototypeOf(this, IntGateValidationError.prototype);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.IntGateValidationError = IntGateValidationError;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IntGate API Client Library for Node.js/TypeScript
|
|
3
|
+
* A Node.js/TypeScript client library for the IntGate license verification API.
|
|
4
|
+
*/
|
|
5
|
+
export { IntGateClient } from './client';
|
|
6
|
+
export { IntGateError, IntGateAPIError, IntGateValidationError } from './exceptions';
|
|
7
|
+
export * from './types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* IntGate API Client Library for Node.js/TypeScript
|
|
4
|
+
* A Node.js/TypeScript client library for the IntGate license verification API.
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
18
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.IntGateValidationError = exports.IntGateAPIError = exports.IntGateError = exports.IntGateClient = void 0;
|
|
22
|
+
var client_1 = require("./client");
|
|
23
|
+
Object.defineProperty(exports, "IntGateClient", { enumerable: true, get: function () { return client_1.IntGateClient; } });
|
|
24
|
+
var exceptions_1 = require("./exceptions");
|
|
25
|
+
Object.defineProperty(exports, "IntGateError", { enumerable: true, get: function () { return exceptions_1.IntGateError; } });
|
|
26
|
+
Object.defineProperty(exports, "IntGateAPIError", { enumerable: true, get: function () { return exceptions_1.IntGateAPIError; } });
|
|
27
|
+
Object.defineProperty(exports, "IntGateValidationError", { enumerable: true, get: function () { return exceptions_1.IntGateValidationError; } });
|
|
28
|
+
__exportStar(require("./types"), exports);
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the Lukittu API client.
|
|
3
|
+
*/
|
|
4
|
+
export interface LicenseMetadata {
|
|
5
|
+
key: string;
|
|
6
|
+
value: string;
|
|
7
|
+
}
|
|
8
|
+
export interface License {
|
|
9
|
+
ipLimit: number;
|
|
10
|
+
hwidLimit: number;
|
|
11
|
+
expirationType: 'NEVER' | 'DATE' | 'DAYS';
|
|
12
|
+
expirationStart?: 'CREATION' | 'ACTIVATION';
|
|
13
|
+
expirationDate?: string;
|
|
14
|
+
expirationDays?: number;
|
|
15
|
+
metadata?: LicenseMetadata[];
|
|
16
|
+
}
|
|
17
|
+
export interface Customer {
|
|
18
|
+
email: string;
|
|
19
|
+
fullName: string;
|
|
20
|
+
username: string;
|
|
21
|
+
metadata?: LicenseMetadata[];
|
|
22
|
+
}
|
|
23
|
+
export interface LatestRelease {
|
|
24
|
+
version: string;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
}
|
|
27
|
+
export interface Product {
|
|
28
|
+
name: string;
|
|
29
|
+
url: string;
|
|
30
|
+
latestRelease?: LatestRelease;
|
|
31
|
+
metadata?: LicenseMetadata[];
|
|
32
|
+
}
|
|
33
|
+
export interface VerificationResult {
|
|
34
|
+
timestamp: string;
|
|
35
|
+
valid: boolean;
|
|
36
|
+
details: string;
|
|
37
|
+
code: string;
|
|
38
|
+
challengeResponse?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface VerifyLicenseResponse {
|
|
41
|
+
data: {
|
|
42
|
+
license: License;
|
|
43
|
+
customers: Customer[];
|
|
44
|
+
products: Product[];
|
|
45
|
+
};
|
|
46
|
+
result: VerificationResult;
|
|
47
|
+
}
|
|
48
|
+
export interface HeartbeatResponse {
|
|
49
|
+
data: {
|
|
50
|
+
license: {
|
|
51
|
+
ipLimit: number;
|
|
52
|
+
hwidLimit: number;
|
|
53
|
+
expirationType: string;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
result: VerificationResult;
|
|
57
|
+
}
|
|
58
|
+
export interface VerifyLicenseOptions {
|
|
59
|
+
licenseKey: string;
|
|
60
|
+
customerId?: string;
|
|
61
|
+
productId?: string;
|
|
62
|
+
challenge?: string;
|
|
63
|
+
version?: string;
|
|
64
|
+
hardwareIdentifier?: string;
|
|
65
|
+
branch?: string;
|
|
66
|
+
}
|
|
67
|
+
export interface LicenseHeartbeatOptions {
|
|
68
|
+
licenseKey: string;
|
|
69
|
+
hardwareIdentifier: string;
|
|
70
|
+
customerId?: string;
|
|
71
|
+
productId?: string;
|
|
72
|
+
challenge?: string;
|
|
73
|
+
version?: string;
|
|
74
|
+
branch?: string;
|
|
75
|
+
}
|
|
76
|
+
export interface DownloadReleaseOptions {
|
|
77
|
+
licenseKey: string;
|
|
78
|
+
productId: string;
|
|
79
|
+
sessionKey: string;
|
|
80
|
+
hardwareIdentifier: string;
|
|
81
|
+
version: string;
|
|
82
|
+
customerId?: string;
|
|
83
|
+
branch?: string;
|
|
84
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "intgate",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Node.js client library for the IntGate license verification API",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"prepare": "npm run build"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"intgate",
|
|
13
|
+
"license",
|
|
14
|
+
"verification",
|
|
15
|
+
"api",
|
|
16
|
+
"client"
|
|
17
|
+
],
|
|
18
|
+
"author": "IntGate Client Library",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"axios": "^1.6.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^20.0.0",
|
|
25
|
+
"typescript": "^5.0.0"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/intgate/intgate-nodejs"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://license.intserver.com"
|
|
32
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IntGate API Client implementation for Node.js/TypeScript.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import axios, { AxiosInstance, AxiosError } from 'axios';
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
import { IntGateAPIError, IntGateValidationError } from './exceptions';
|
|
8
|
+
import {
|
|
9
|
+
VerifyLicenseOptions,
|
|
10
|
+
LicenseHeartbeatOptions,
|
|
11
|
+
DownloadReleaseOptions,
|
|
12
|
+
VerifyLicenseResponse,
|
|
13
|
+
HeartbeatResponse,
|
|
14
|
+
} from './types';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Client for interacting with the IntGate license verification API.
|
|
18
|
+
*/
|
|
19
|
+
export class IntGateClient extends EventEmitter {
|
|
20
|
+
private teamId: string;
|
|
21
|
+
private baseUrl: string;
|
|
22
|
+
private axiosInstance: AxiosInstance;
|
|
23
|
+
private heartbeatTimer: NodeJS.Timeout | null = null;
|
|
24
|
+
private heartbeatRunning: boolean = false;
|
|
25
|
+
private lastHeartbeatResult: HeartbeatResponse | null = null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Initialize the IntGate client.
|
|
29
|
+
* @param teamId Your team's UUID from the IntGate dashboard.
|
|
30
|
+
* @param baseUrl The base URL for the IntGate API (default: https://license.intserver.com/api/v1).
|
|
31
|
+
*/
|
|
32
|
+
constructor(teamId: string, baseUrl: string = 'https://license.intserver.com/api/v1') {
|
|
33
|
+
super();
|
|
34
|
+
|
|
35
|
+
if (!teamId) {
|
|
36
|
+
throw new IntGateValidationError('teamId is required');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.teamId = teamId;
|
|
40
|
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
41
|
+
this.axiosInstance = axios.create({
|
|
42
|
+
headers: {
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Verify a license key with the IntGate backend.
|
|
50
|
+
*
|
|
51
|
+
* This endpoint validates a license key and returns license information,
|
|
52
|
+
* customer data, and product details. Typically called when software starts.
|
|
53
|
+
*
|
|
54
|
+
* @param options License verification options
|
|
55
|
+
* @returns Promise resolving to verification response
|
|
56
|
+
* @throws {IntGateValidationError} If required parameters are invalid
|
|
57
|
+
* @throws {IntGateAPIError} If the API request fails
|
|
58
|
+
*/
|
|
59
|
+
async verifyLicense(options: VerifyLicenseOptions): Promise<VerifyLicenseResponse> {
|
|
60
|
+
if (!options.licenseKey) {
|
|
61
|
+
throw new IntGateValidationError('licenseKey is required');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const payload: any = {
|
|
65
|
+
licenseKey: options.licenseKey,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (options.customerId) payload.customerId = options.customerId;
|
|
69
|
+
if (options.productId) payload.productId = options.productId;
|
|
70
|
+
if (options.challenge) payload.challenge = options.challenge;
|
|
71
|
+
if (options.version) payload.version = options.version;
|
|
72
|
+
if (options.hardwareIdentifier) payload.hardwareIdentifier = options.hardwareIdentifier;
|
|
73
|
+
if (options.branch) payload.branch = options.branch;
|
|
74
|
+
|
|
75
|
+
const url = `${this.baseUrl}/client/teams/${this.teamId}/verification/verify`;
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const response = await this.axiosInstance.post<VerifyLicenseResponse>(url, payload);
|
|
79
|
+
return response.data;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
this.handleError(error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Send a heartbeat to determine if a device is still active.
|
|
87
|
+
*
|
|
88
|
+
* This endpoint should be called at regular intervals (e.g., every 30 minutes).
|
|
89
|
+
* It validates the license key similar to verifyLicense.
|
|
90
|
+
*
|
|
91
|
+
* @param options Heartbeat options
|
|
92
|
+
* @returns Promise resolving to heartbeat response
|
|
93
|
+
* @throws {IntGateValidationError} If required parameters are invalid
|
|
94
|
+
* @throws {IntGateAPIError} If the API request fails
|
|
95
|
+
*/
|
|
96
|
+
async licenseHeartbeat(options: LicenseHeartbeatOptions): Promise<HeartbeatResponse> {
|
|
97
|
+
if (!options.licenseKey) {
|
|
98
|
+
throw new IntGateValidationError('licenseKey is required');
|
|
99
|
+
}
|
|
100
|
+
if (!options.hardwareIdentifier) {
|
|
101
|
+
throw new IntGateValidationError('hardwareIdentifier is required');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const payload: any = {
|
|
105
|
+
licenseKey: options.licenseKey,
|
|
106
|
+
hardwareIdentifier: options.hardwareIdentifier,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
if (options.customerId) payload.customerId = options.customerId;
|
|
110
|
+
if (options.productId) payload.productId = options.productId;
|
|
111
|
+
if (options.challenge) payload.challenge = options.challenge;
|
|
112
|
+
if (options.version) payload.version = options.version;
|
|
113
|
+
if (options.branch) payload.branch = options.branch;
|
|
114
|
+
|
|
115
|
+
const url = `${this.baseUrl}/client/teams/${this.teamId}/verification/heartbeat`;
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const response = await this.axiosInstance.post<HeartbeatResponse>(url, payload);
|
|
119
|
+
return response.data;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
this.handleError(error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Download an encrypted release file.
|
|
127
|
+
*
|
|
128
|
+
* This endpoint is primarily for languages that support loading code remotely
|
|
129
|
+
* (e.g., Java classloaders). The file is encrypted using the provided session key.
|
|
130
|
+
*
|
|
131
|
+
* @param options Download release options
|
|
132
|
+
* @returns Promise resolving to encrypted file content (Buffer)
|
|
133
|
+
* @throws {IntGateValidationError} If required parameters are invalid
|
|
134
|
+
* @throws {IntGateAPIError} If the API request fails
|
|
135
|
+
*/
|
|
136
|
+
async downloadRelease(options: DownloadReleaseOptions): Promise<Buffer> {
|
|
137
|
+
if (!options.licenseKey) {
|
|
138
|
+
throw new IntGateValidationError('licenseKey is required');
|
|
139
|
+
}
|
|
140
|
+
if (!options.productId) {
|
|
141
|
+
throw new IntGateValidationError('productId is required');
|
|
142
|
+
}
|
|
143
|
+
if (!options.sessionKey) {
|
|
144
|
+
throw new IntGateValidationError('sessionKey is required');
|
|
145
|
+
}
|
|
146
|
+
if (!options.hardwareIdentifier) {
|
|
147
|
+
throw new IntGateValidationError('hardwareIdentifier is required');
|
|
148
|
+
}
|
|
149
|
+
if (!options.version) {
|
|
150
|
+
throw new IntGateValidationError('version is required');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const params: any = {
|
|
154
|
+
licenseKey: options.licenseKey,
|
|
155
|
+
productId: options.productId,
|
|
156
|
+
sessionKey: options.sessionKey,
|
|
157
|
+
hardwareIdentifier: options.hardwareIdentifier,
|
|
158
|
+
version: options.version,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
if (options.customerId) params.customerId = options.customerId;
|
|
162
|
+
if (options.branch) params.branch = options.branch;
|
|
163
|
+
|
|
164
|
+
const url = `${this.baseUrl}/client/teams/${this.teamId}/verification/classloader`;
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const response = await this.axiosInstance.get(url, {
|
|
168
|
+
params,
|
|
169
|
+
responseType: 'arraybuffer',
|
|
170
|
+
});
|
|
171
|
+
return Buffer.from(response.data);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
this.handleError(error);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Start automatic heartbeat in the background.
|
|
179
|
+
*
|
|
180
|
+
* The client will emit 'heartbeat' events with the result on success,
|
|
181
|
+
* and 'heartbeat-error' events on errors.
|
|
182
|
+
*
|
|
183
|
+
* @param options Heartbeat options
|
|
184
|
+
* @param interval Interval in milliseconds between heartbeats (default: 1800000 = 30 minutes)
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* client.startAutomaticHeartbeat({
|
|
188
|
+
* licenseKey: 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX',
|
|
189
|
+
* hardwareIdentifier: 'device-123'
|
|
190
|
+
* }, 60000); // Every 1 minute
|
|
191
|
+
*
|
|
192
|
+
* client.on('heartbeat', (result) => {
|
|
193
|
+
* console.log('Heartbeat successful:', result);
|
|
194
|
+
* });
|
|
195
|
+
*
|
|
196
|
+
* client.on('heartbeat-error', (error) => {
|
|
197
|
+
* console.error('Heartbeat failed:', error);
|
|
198
|
+
* });
|
|
199
|
+
*/
|
|
200
|
+
startAutomaticHeartbeat(options: LicenseHeartbeatOptions, interval: number = 1800000): void {
|
|
201
|
+
if (this.heartbeatRunning) {
|
|
202
|
+
throw new IntGateValidationError('Automatic heartbeat is already running');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this.heartbeatRunning = true;
|
|
206
|
+
|
|
207
|
+
const runHeartbeat = async () => {
|
|
208
|
+
try {
|
|
209
|
+
const result = await this.licenseHeartbeat(options);
|
|
210
|
+
this.lastHeartbeatResult = result;
|
|
211
|
+
this.emit('heartbeat', result);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
this.emit('heartbeat-error', error);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (this.heartbeatRunning) {
|
|
217
|
+
this.heartbeatTimer = setTimeout(runHeartbeat, interval);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// Run immediately, then schedule
|
|
222
|
+
runHeartbeat();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Stop the automatic heartbeat.
|
|
227
|
+
*/
|
|
228
|
+
stopAutomaticHeartbeat(): void {
|
|
229
|
+
this.heartbeatRunning = false;
|
|
230
|
+
if (this.heartbeatTimer) {
|
|
231
|
+
clearTimeout(this.heartbeatTimer);
|
|
232
|
+
this.heartbeatTimer = null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get the result of the last automatic heartbeat.
|
|
238
|
+
*
|
|
239
|
+
* @returns The last heartbeat result, or null if no heartbeat has run yet
|
|
240
|
+
*/
|
|
241
|
+
getLastHeartbeatResult(): HeartbeatResponse | null {
|
|
242
|
+
return this.lastHeartbeatResult;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Check if automatic heartbeat is currently running.
|
|
247
|
+
*
|
|
248
|
+
* @returns True if heartbeat is running, false otherwise
|
|
249
|
+
*/
|
|
250
|
+
isHeartbeatRunning(): boolean {
|
|
251
|
+
return this.heartbeatRunning;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Handle errors from API requests.
|
|
256
|
+
* @private
|
|
257
|
+
*/
|
|
258
|
+
private handleError(error: any): never {
|
|
259
|
+
if (axios.isAxiosError(error)) {
|
|
260
|
+
const axiosError = error as AxiosError;
|
|
261
|
+
const statusCode = axiosError.response?.status;
|
|
262
|
+
const responseData = axiosError.response?.data;
|
|
263
|
+
|
|
264
|
+
throw new IntGateAPIError(
|
|
265
|
+
`API request failed: ${axiosError.message}`,
|
|
266
|
+
statusCode,
|
|
267
|
+
responseData
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
throw new IntGateAPIError(`Network error: ${error.message}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error classes for the IntGate API client.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base error class for all IntGate errors.
|
|
7
|
+
*/
|
|
8
|
+
export class IntGateError extends Error {
|
|
9
|
+
constructor(message: string) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'IntGateError';
|
|
12
|
+
Object.setPrototypeOf(this, IntGateError.prototype);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Error thrown when the API returns an error response.
|
|
18
|
+
*/
|
|
19
|
+
export class IntGateAPIError extends IntGateError {
|
|
20
|
+
statusCode?: number;
|
|
21
|
+
responseData?: any;
|
|
22
|
+
|
|
23
|
+
constructor(message: string, statusCode?: number, responseData?: any) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = 'IntGateAPIError';
|
|
26
|
+
this.statusCode = statusCode;
|
|
27
|
+
this.responseData = responseData;
|
|
28
|
+
Object.setPrototypeOf(this, IntGateAPIError.prototype);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Error thrown when input validation fails.
|
|
34
|
+
*/
|
|
35
|
+
export class IntGateValidationError extends IntGateError {
|
|
36
|
+
constructor(message: string) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = 'IntGateValidationError';
|
|
39
|
+
Object.setPrototypeOf(this, IntGateValidationError.prototype);
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IntGate API Client Library for Node.js/TypeScript
|
|
3
|
+
* A Node.js/TypeScript client library for the IntGate license verification API.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { IntGateClient } from './client';
|
|
7
|
+
export { IntGateError, IntGateAPIError, IntGateValidationError } from './exceptions';
|
|
8
|
+
export * from './types';
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the Lukittu API client.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface LicenseMetadata {
|
|
6
|
+
key: string;
|
|
7
|
+
value: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface License {
|
|
11
|
+
ipLimit: number;
|
|
12
|
+
hwidLimit: number;
|
|
13
|
+
expirationType: 'NEVER' | 'DATE' | 'DAYS';
|
|
14
|
+
expirationStart?: 'CREATION' | 'ACTIVATION';
|
|
15
|
+
expirationDate?: string;
|
|
16
|
+
expirationDays?: number;
|
|
17
|
+
metadata?: LicenseMetadata[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Customer {
|
|
21
|
+
email: string;
|
|
22
|
+
fullName: string;
|
|
23
|
+
username: string;
|
|
24
|
+
metadata?: LicenseMetadata[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface LatestRelease {
|
|
28
|
+
version: string;
|
|
29
|
+
createdAt: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface Product {
|
|
33
|
+
name: string;
|
|
34
|
+
url: string;
|
|
35
|
+
latestRelease?: LatestRelease;
|
|
36
|
+
metadata?: LicenseMetadata[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface VerificationResult {
|
|
40
|
+
timestamp: string;
|
|
41
|
+
valid: boolean;
|
|
42
|
+
details: string;
|
|
43
|
+
code: string;
|
|
44
|
+
challengeResponse?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface VerifyLicenseResponse {
|
|
48
|
+
data: {
|
|
49
|
+
license: License;
|
|
50
|
+
customers: Customer[];
|
|
51
|
+
products: Product[];
|
|
52
|
+
};
|
|
53
|
+
result: VerificationResult;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface HeartbeatResponse {
|
|
57
|
+
data: {
|
|
58
|
+
license: {
|
|
59
|
+
ipLimit: number;
|
|
60
|
+
hwidLimit: number;
|
|
61
|
+
expirationType: string;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
result: VerificationResult;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface VerifyLicenseOptions {
|
|
68
|
+
licenseKey: string;
|
|
69
|
+
customerId?: string;
|
|
70
|
+
productId?: string;
|
|
71
|
+
challenge?: string;
|
|
72
|
+
version?: string;
|
|
73
|
+
hardwareIdentifier?: string;
|
|
74
|
+
branch?: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface LicenseHeartbeatOptions {
|
|
78
|
+
licenseKey: string;
|
|
79
|
+
hardwareIdentifier: string;
|
|
80
|
+
customerId?: string;
|
|
81
|
+
productId?: string;
|
|
82
|
+
challenge?: string;
|
|
83
|
+
version?: string;
|
|
84
|
+
branch?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface DownloadReleaseOptions {
|
|
88
|
+
licenseKey: string;
|
|
89
|
+
productId: string;
|
|
90
|
+
sessionKey: string;
|
|
91
|
+
hardwareIdentifier: string;
|
|
92
|
+
version: string;
|
|
93
|
+
customerId?: string;
|
|
94
|
+
branch?: string;
|
|
95
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": [
|
|
6
|
+
"ES2020"
|
|
7
|
+
],
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"outDir": "./dist",
|
|
10
|
+
"rootDir": "./src",
|
|
11
|
+
"strict": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"moduleResolution": "node",
|
|
16
|
+
"resolveJsonModule": true
|
|
17
|
+
},
|
|
18
|
+
"include": [
|
|
19
|
+
"src/**/*"
|
|
20
|
+
],
|
|
21
|
+
"exclude": [
|
|
22
|
+
"node_modules",
|
|
23
|
+
"dist"
|
|
24
|
+
]
|
|
25
|
+
}
|