@corelicense/node 0.5.0 → 0.5.1
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/LICENSE +21 -0
- package/README.md +52 -162
- package/dist/core-license.js +1 -1
- package/docs/api-reference.md +210 -0
- package/docs/installation.md +188 -0
- package/docs/integration.md +789 -0
- package/package.json +5 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CoreLicense
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,193 +1,83 @@
|
|
|
1
1
|
# @corelicense/node
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Runtime license và policy layer cho ứng dụng Node.js — không chỉ middleware HTTP, mà là lớp kiểm soát **bootstrap**, **module registry**, **DI**, **worker/CLI**, **quota** và **feature gate** theo policy đã ký.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Phiên bản:** `0.5.1`
|
|
6
|
+
**License:** [MIT](./LICENSE)
|
|
7
|
+
**Repository:** [github.com/trailanglc/corelicense](https://github.com/trailanglc/corelicense)
|
|
8
|
+
**Docs trực tuyến:** [corelicense.net/docs](https://corelicense.net/docs/nodejs-sdk/quick-start)
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
---
|
|
8
11
|
|
|
9
|
-
CoreLicense
|
|
12
|
+
## CoreLicense là gì?
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
CoreLicense gồm **License Server** (API Go) và **Node.js SDK** (package này). Admin cấp license key + policy (features, limits, quotas). SDK:
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install express # Express middleware
|
|
21
|
-
npm install fastify # Fastify plugin
|
|
22
|
-
npm install @nestjs/common # NestJS module/guard
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Quick start
|
|
26
|
-
|
|
27
|
-
Chỉ cần **2 biến môi trường**. SDK mặc định kết nối `https://api.corelicense.net` — không cần cấu hình thêm cho production.
|
|
28
|
-
|
|
29
|
-
License key mới (CL1.*) vẫn ưu tiên URL nhúng trong key nếu server generate với `SDK_PUBLIC_URL` khác.
|
|
30
|
-
|
|
31
|
-
```env
|
|
32
|
-
CORELICENSE_KEY=CL1.xxxxx.CL-AAAAA-BBBBB-...
|
|
33
|
-
CORELICENSE_PRODUCT_ID=demo
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
`CORELICENSE_PUBLIC_KEY` **optional** — pin public key (enterprise / air-gapped).
|
|
37
|
-
`CORELICENSE_API_URL` **optional** — override thủ công (dev local hoặc legacy key).
|
|
38
|
-
|
|
39
|
-
Dev local với server Go chạy localhost:
|
|
40
|
-
|
|
41
|
-
```env
|
|
42
|
-
CORELICENSE_API_URL=http://localhost:8080
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
import express from 'express';
|
|
47
|
-
import { CoreLicense, loadOptionsFromEnv } from '@corelicense/node';
|
|
16
|
+
1. Kích hoạt / kiểm tra license với server
|
|
17
|
+
2. Verify **policy token** bằng public key (EdDSA)
|
|
18
|
+
3. Cache policy local, refresh định kỳ
|
|
19
|
+
4. Chặn runtime khi revoke / suspend / expired
|
|
20
|
+
5. (Tùy chọn) Chỉ bind module & service được cấp trong policy
|
|
48
21
|
|
|
49
|
-
|
|
22
|
+
---
|
|
50
23
|
|
|
51
|
-
|
|
52
|
-
cachePath: './storage/corelicense',
|
|
53
|
-
instance: {
|
|
54
|
-
strategy: 'install-id-domain-hash',
|
|
55
|
-
domain: process.env.APP_DOMAIN,
|
|
56
|
-
},
|
|
57
|
-
}));
|
|
24
|
+
## Tài liệu
|
|
58
25
|
|
|
59
|
-
|
|
60
|
-
|
|
26
|
+
| Tài liệu | Nội dung |
|
|
27
|
+
|----------|----------|
|
|
28
|
+
| [Cài đặt](./docs/installation.md) | `npm install`, biến môi trường, boot lần đầu, dev/prod |
|
|
29
|
+
| [Tích hợp](./docs/integration.md) | **Hướng dẫn chi tiết** — Express, Fastify, NestJS, registry/DI, worker, quota, production |
|
|
30
|
+
| [API Reference](./docs/api-reference.md) | Public API, options, error codes |
|
|
31
|
+
| [LICENSE](./LICENSE) | MIT |
|
|
61
32
|
|
|
62
|
-
|
|
63
|
-
allowPaths: ['/health', '/license/status'],
|
|
64
|
-
}));
|
|
33
|
+
> Dành cho agent / CI: đọc [docs/integration.md](./docs/integration.md) trước khi generate code tích hợp.
|
|
65
34
|
|
|
66
|
-
|
|
67
|
-
res.json(await license.getStatus());
|
|
68
|
-
});
|
|
35
|
+
---
|
|
69
36
|
|
|
70
|
-
|
|
71
|
-
}
|
|
37
|
+
## Quick links
|
|
72
38
|
|
|
73
|
-
|
|
39
|
+
```bash
|
|
40
|
+
npm install @corelicense/node
|
|
74
41
|
```
|
|
75
42
|
|
|
76
|
-
|
|
43
|
+
Production mặc định: `https://api.corelicense.net` — chỉ cần `CORELICENSE_KEY` + `CORELICENSE_PRODUCT_ID`.
|
|
77
44
|
|
|
78
45
|
```ts
|
|
79
|
-
|
|
80
|
-
publicKey: process.env.CORELICENSE_PUBLIC_KEY, // pinned mode
|
|
81
|
-
}));
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
Khi pin, SDK không fetch keys từ server. Nếu verify fail sau key rotation, cập nhật `CORELICENSE_PUBLIC_KEY` thủ công.
|
|
85
|
-
|
|
86
|
-
## Public API
|
|
87
|
-
|
|
88
|
-
| Method | Mô tả |
|
|
89
|
-
|--------|-------|
|
|
90
|
-
| `boot()` | Khởi tạo SDK, fetch/cache public keys, load cache, verify token |
|
|
91
|
-
| `refresh()` | Refresh policy token từ server |
|
|
92
|
-
| `requireActive()` | Yêu cầu license active |
|
|
93
|
-
| `requireFeature(name)` | Kiểm tra feature flag |
|
|
94
|
-
| `checkLimit(name, value)` | Kiểm tra limit |
|
|
95
|
-
| `consumeQuota(name, amount?)` | Tiêu thụ quota local |
|
|
96
|
-
| `getStatus()` | Trạng thái license hiện tại |
|
|
97
|
-
| `getPolicy()` | Policy đang active |
|
|
98
|
-
| `getRuntimeConfig()` | Runtime config cho app bootstrap |
|
|
99
|
-
| `express(options?)` | Express middleware |
|
|
100
|
-
| `fastifyPlugin(options?)` | Fastify plugin |
|
|
101
|
-
| `guardWorker(name)` | Guard worker |
|
|
102
|
-
| `guardCommand(name)` | Guard CLI/cron command |
|
|
103
|
-
| `createLicensedRegistry()` | Module registry gắn license |
|
|
104
|
-
| `createLicensedContainer()` | DI container gắn license |
|
|
105
|
-
| `bootstrap(options?)` | Bootstrap modules + services theo policy |
|
|
106
|
-
|
|
107
|
-
## Tích hợp sâu (v0.4)
|
|
46
|
+
import { CoreLicense, loadOptionsFromEnv } from '@corelicense/node';
|
|
108
47
|
|
|
109
|
-
|
|
48
|
+
const license = new CoreLicense(loadOptionsFromEnv({ cachePath: './storage/corelicense' }));
|
|
110
49
|
await license.boot();
|
|
111
|
-
|
|
112
|
-
const { modules, services, skipped } = await license.bootstrap({
|
|
113
|
-
modules: [
|
|
114
|
-
{ name: 'automation', feature: 'automation', factory: ({ license }) => new AutomationModule(license) },
|
|
115
|
-
],
|
|
116
|
-
services: [
|
|
117
|
-
{ name: 'ExportService', feature: 'export', factory: ({ license }) => new ExportService(license) },
|
|
118
|
-
],
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const automation = await modules.get<AutomationModule>('automation');
|
|
122
|
-
const exportService = await services.resolve<ExportService>('ExportService');
|
|
123
|
-
|
|
124
|
-
import { registerLicensedRoutes } from '@corelicense/node';
|
|
125
|
-
registerLicensedRoutes(app, modules, [
|
|
126
|
-
{ path: '/automation', module: 'automation', router: automationRouter },
|
|
127
|
-
]);
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
`get()` và `resolve()` gọi `requireFeature` mỗi lần truy cập — revoke/suspend chặn ngay. Đổi feature trong policy vẫn cần **restart** để bind/unbind module mới.
|
|
131
|
-
|
|
132
|
-
Xem [Tích hợp sâu](https://corelicense.net/docs/nodejs-sdk/deep-integration) trên docs site.
|
|
133
|
-
|
|
134
|
-
## Xử lý lỗi
|
|
135
|
-
|
|
136
|
-
Mọi lỗi SDK đều có `code` (mã định vị) và `source` (module SDK phát sinh):
|
|
137
|
-
|
|
138
|
-
```ts
|
|
139
|
-
import { isLicenseError, formatLicenseError, LicenseErrorCode } from '@corelicense/node';
|
|
140
|
-
|
|
141
|
-
try {
|
|
142
|
-
await license.boot();
|
|
143
|
-
} catch (error) {
|
|
144
|
-
if (isLicenseError(error)) {
|
|
145
|
-
console.error(error.code, error.source); // e.g. CONFIG_MISSING_ENV, load-options-from-env
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
50
|
```
|
|
149
51
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
## Production deploy
|
|
52
|
+
Chi tiết: [docs/installation.md](./docs/installation.md) → [docs/integration.md](./docs/integration.md).
|
|
153
53
|
|
|
154
|
-
|
|
54
|
+
---
|
|
155
55
|
|
|
156
|
-
|
|
56
|
+
## Subpath exports
|
|
157
57
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
errors/ # Error hierarchy
|
|
165
|
-
crypto/ # Policy token verification + public key resolver
|
|
166
|
-
client/ # License API + public keys client
|
|
167
|
-
cache/ # File/memory cache
|
|
168
|
-
instance/ # Instance ID resolution
|
|
169
|
-
guards/ # Express, Fastify, NestJS, worker, CLI
|
|
170
|
-
runtime/ # Runtime config, module registry, DI container
|
|
171
|
-
audit/ # Local audit log
|
|
172
|
-
events/ # Event bus
|
|
173
|
-
```
|
|
58
|
+
| Import | Mục đích |
|
|
59
|
+
|--------|----------|
|
|
60
|
+
| `@corelicense/node` | Core API |
|
|
61
|
+
| `@corelicense/node/express` | Express middleware |
|
|
62
|
+
| `@corelicense/node/fastify` | Fastify plugin |
|
|
63
|
+
| `@corelicense/node/nestjs` | NestJS module, guards, filter |
|
|
174
64
|
|
|
175
|
-
|
|
65
|
+
---
|
|
176
66
|
|
|
177
|
-
|
|
67
|
+
## Yêu cầu
|
|
178
68
|
|
|
179
|
-
|
|
69
|
+
- Node.js **≥ 18**
|
|
70
|
+
- License Server reachable (hoặc cache + offline grace hợp lệ)
|
|
180
71
|
|
|
181
|
-
|
|
182
|
-
- **v0.2** — Auto-fetch public keys, key rotation retry, worker/CLI guard, offline grace
|
|
183
|
-
- **v0.3** — Production default URL, activate flow, revoke handling, server contract sync
|
|
184
|
-
- **v0.4** — Licensed module registry, DI container, Express routes, NestJS `@RequireFeature`
|
|
185
|
-
- **v0.4.1** — Runtime re-check license trên `get()` / `resolve()`, fix `ModuleNotLicensedError` status
|
|
186
|
-
- **v0.4.2** — Mã lỗi (`code`) + `source` trên mọi `LicenseError`, `LicenseErrorCode`, `formatLicenseError()`
|
|
187
|
-
- **v0.4.3** — Re-check registry/DI không ghi audit; middleware 403 trả `source` qua `toClientPayload()`
|
|
188
|
-
- **v0.5.0** — Fail-closed offline boot, quota/limit gates, apiUrl pin, Nest 403 filter, sync mutex
|
|
189
|
-
- **v1.0** — Stable API, production-ready, security review
|
|
72
|
+
---
|
|
190
73
|
|
|
191
|
-
##
|
|
74
|
+
## Changelog (tóm tắt)
|
|
192
75
|
|
|
193
|
-
|
|
76
|
+
| Version | Highlights |
|
|
77
|
+
|---------|------------|
|
|
78
|
+
| **0.5.1** | Tách tài liệu (installation, integration, api-reference), sửa repository URL |
|
|
79
|
+
| **0.5.0** | Fail-closed offline boot, quota/limit gates, apiUrl pin, sync mutex, Nest 403 filter |
|
|
80
|
+
| **0.4.x** | Deep integration (registry/DI), error `code` + `source`, runtime re-check |
|
|
81
|
+
| **0.3.x** | Production default URL, activate/revoke sync |
|
|
82
|
+
| **0.2.x** | Auto-fetch public keys, worker/CLI guard |
|
|
83
|
+
| **0.1.x** | Boot, express middleware, file cache |
|
package/dist/core-license.js
CHANGED
|
@@ -25,7 +25,7 @@ import { PolicySignatureError } from './errors/policy-error.js';
|
|
|
25
25
|
import { throwForTerminalStatus } from './errors/throw-for-status.js';
|
|
26
26
|
import { resolveApiUrl, parseEmbeddedApiUrl } from './env/resolve-api-url.js';
|
|
27
27
|
const SDK_NAME = '@corelicense/node';
|
|
28
|
-
const SDK_VERSION = '0.5.
|
|
28
|
+
const SDK_VERSION = '0.5.1';
|
|
29
29
|
const DEFAULT_CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
30
30
|
function hashPolicyToken(token) {
|
|
31
31
|
return createHash('sha256').update(token).digest('hex');
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
Public API `@corelicense/node` v0.5.1.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## CoreLicense
|
|
8
|
+
|
|
9
|
+
### Constructor
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
new CoreLicense(options: CoreLicenseOptions)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Lifecycle
|
|
16
|
+
|
|
17
|
+
| Method | Returns | Mô tả |
|
|
18
|
+
|--------|---------|--------|
|
|
19
|
+
| `boot()` | `Promise<void>` | Khởi tạo, sync server, fail-closed |
|
|
20
|
+
| `refresh()` | `Promise<LicenseStatus>` | Force refresh policy |
|
|
21
|
+
| `startBackgroundCheck()` | `void` | Bật interval sync |
|
|
22
|
+
| `stopBackgroundCheck()` | `void` | Tắt interval |
|
|
23
|
+
|
|
24
|
+
### License gates
|
|
25
|
+
|
|
26
|
+
| Method | Mô tả |
|
|
27
|
+
|--------|--------|
|
|
28
|
+
| `requireActive()` | Throw nếu không active |
|
|
29
|
+
| `requireFeature(feature)` | Throw + audit nếu feature off |
|
|
30
|
+
| `checkLimit(name, current)` | Throw nếu vượt limit |
|
|
31
|
+
| `consumeQuota(name, amount?)` | Tiêu thụ quota local |
|
|
32
|
+
| `getStatus()` | Trạng thái hiện tại |
|
|
33
|
+
| `getPolicy()` | Policy đã verify (null nếu chưa có) |
|
|
34
|
+
| `getRuntimeConfig()` | Config derived từ policy |
|
|
35
|
+
|
|
36
|
+
### HTTP integrations
|
|
37
|
+
|
|
38
|
+
| Method | Mô tả |
|
|
39
|
+
|--------|--------|
|
|
40
|
+
| `express(options?)` | Express `RequestHandler` |
|
|
41
|
+
| `fastifyPlugin(options?)` | Fastify plugin |
|
|
42
|
+
|
|
43
|
+
### Worker / CLI
|
|
44
|
+
|
|
45
|
+
| Method | Mô tả |
|
|
46
|
+
|--------|--------|
|
|
47
|
+
| `guardWorker(name)` | Guard worker process |
|
|
48
|
+
| `guardCommand(name)` | Guard CLI/cron command |
|
|
49
|
+
|
|
50
|
+
### Deep integration
|
|
51
|
+
|
|
52
|
+
| Method | Mô tả |
|
|
53
|
+
|--------|--------|
|
|
54
|
+
| `bootstrap(options)` | Register modules + services |
|
|
55
|
+
| `createLicensedRegistry()` | `LicensedModuleRegistry` riêng |
|
|
56
|
+
| `createLicensedContainer()` | `LicensedServiceContainer` riêng |
|
|
57
|
+
|
|
58
|
+
### Events
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
license.on(event, handler)
|
|
62
|
+
license.off(event, handler)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Events: `statusChanged` | `policyUpdated` | `licenseRevoked` | `licenseSuspended` | `offlineMode`
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## LicensedModuleRegistry
|
|
70
|
+
|
|
71
|
+
| Method | Mô tả |
|
|
72
|
+
|--------|--------|
|
|
73
|
+
| `bootstrap(definitions, ctx)` | Register hàng loạt |
|
|
74
|
+
| `registerLicensed(def, ctx)` | Register một module |
|
|
75
|
+
| `has(name)` | Module đã bind? |
|
|
76
|
+
| `get<T>(name)` | Lấy module + re-check license |
|
|
77
|
+
| `list()` | Tên modules đã register |
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## LicensedServiceContainer
|
|
82
|
+
|
|
83
|
+
| Method | Mô tả |
|
|
84
|
+
|--------|--------|
|
|
85
|
+
| `bootstrap(definitions, ctx)` | Bind services |
|
|
86
|
+
| `resolve<T>(name)` | Resolve + re-check license |
|
|
87
|
+
| `has(name)` | Service đã bind? |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Helpers
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
loadOptionsFromEnv(overrides?: Partial<CoreLicenseOptions>): CoreLicenseOptions
|
|
95
|
+
resolveApiUrl(options, cachedApiUrl?): string
|
|
96
|
+
parseEmbeddedApiUrl(licenseKey): string | null
|
|
97
|
+
registerLicensedRoutes(app, registry, routes): string[]
|
|
98
|
+
isLicenseError(error): boolean
|
|
99
|
+
formatLicenseError(error): string
|
|
100
|
+
isFeatureEnabled(policy, feature): boolean
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Subpath exports
|
|
106
|
+
|
|
107
|
+
### `@corelicense/node/express`
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
createExpressMiddleware(license, options?): RequestHandler
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### `@corelicense/node/fastify`
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
createFastifyPlugin(license, options?): FastifyPluginAsync
|
|
117
|
+
coreLicenseFastifyPlugin
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `@corelicense/node/nestjs`
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
CoreLicenseModule.forRoot(options): DynamicModule
|
|
124
|
+
CoreLicenseGuard
|
|
125
|
+
CoreLicenseFeatureGuard
|
|
126
|
+
CoreLicenseExceptionFilter
|
|
127
|
+
RequireFeature(feature) // decorator
|
|
128
|
+
REQUIRE_FEATURE_KEY
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Types
|
|
134
|
+
|
|
135
|
+
### `CoreLicenseOptions`
|
|
136
|
+
|
|
137
|
+
Xem [installation.md](./installation.md#4-corelicenseoptions-constructor).
|
|
138
|
+
|
|
139
|
+
### `ExpressGuardOptions` / `FastifyGuardOptions`
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
{
|
|
143
|
+
allowPaths?: string[];
|
|
144
|
+
mode?: 'block' | 'limited';
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### `BootstrapResult`
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
{
|
|
152
|
+
modules: LicensedModuleRegistry;
|
|
153
|
+
services: LicensedServiceContainer;
|
|
154
|
+
registeredModules: string[];
|
|
155
|
+
registeredServices: string[];
|
|
156
|
+
skipped: { name: string; feature: string; reason: 'feature_disabled' }[];
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### `LicenseStatus`
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
{
|
|
164
|
+
status: 'active' | 'suspended' | 'revoked' | 'expired' | 'invalid' | 'blocked';
|
|
165
|
+
policyVersion?: string;
|
|
166
|
+
expiresAt?: string;
|
|
167
|
+
// ...
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Error codes
|
|
174
|
+
|
|
175
|
+
Mọi `LicenseError` có `code`, `source`, `message`, `toClientPayload()`, `toJSON()`.
|
|
176
|
+
|
|
177
|
+
| Code | HTTP (middleware) |
|
|
178
|
+
|------|-------------------|
|
|
179
|
+
| `LICENSE_REVOKED` | 403 |
|
|
180
|
+
| `LICENSE_SUSPENDED` | 403 |
|
|
181
|
+
| `LICENSE_EXPIRED` | 403 |
|
|
182
|
+
| `LICENSE_INVALID` | 403 |
|
|
183
|
+
| `OFFLINE_GRACE_EXPIRED` | 403 |
|
|
184
|
+
| `FEATURE_DISABLED` | 403 |
|
|
185
|
+
| `MODULE_NOT_LICENSED` | 403 |
|
|
186
|
+
| `QUOTA_EXCEEDED` | 403 |
|
|
187
|
+
| `MAX_INSTANCES` | 403 |
|
|
188
|
+
| `INSTANCE_BLOCKED` | 403 |
|
|
189
|
+
| `CONFIG_MISSING_ENV` | — (boot fail) |
|
|
190
|
+
| `POLICY_SIGNATURE_ERROR` | — (boot fail) |
|
|
191
|
+
| `BOOT_NOT_CALLED` | — |
|
|
192
|
+
| `WORKER_NOT_ALLOWED` | — |
|
|
193
|
+
| `COMMAND_NOT_ALLOWED` | — |
|
|
194
|
+
|
|
195
|
+
Danh sách đầy đủ trong source: `src/errors/codes.ts`
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Client API (License Server)
|
|
200
|
+
|
|
201
|
+
SDK gọi các endpoint (không gọi trực tiếp từ app thường):
|
|
202
|
+
|
|
203
|
+
| Endpoint | Mô tả |
|
|
204
|
+
|----------|--------|
|
|
205
|
+
| `POST /v1/client/activate` | Lần đầu |
|
|
206
|
+
| `POST /v1/client/check` | Periodic check |
|
|
207
|
+
| `POST /v1/client/refresh` | Force refresh |
|
|
208
|
+
| `GET /v1/public/products/{id}/keys` | Public keys |
|
|
209
|
+
|
|
210
|
+
Chi tiết: [corelicense.net/docs/client-api](https://corelicense.net/docs/client-api)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Cài đặt
|
|
2
|
+
|
|
3
|
+
Hướng dẫn cài package, cấu hình môi trường và chạy `boot()` lần đầu.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Cài package
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @corelicense/node
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Peer dependencies (tùy framework)
|
|
14
|
+
|
|
15
|
+
Chỉ cài khi dùng integration tương ứng:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install express # Express middleware
|
|
19
|
+
npm install fastify # Fastify plugin
|
|
20
|
+
npm install @nestjs/common @nestjs/core # NestJS
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
SDK không bắt buộc peer deps — tree-shake an toàn nếu chỉ dùng `CoreLicense` trực tiếp.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 2. Biến môi trường
|
|
28
|
+
|
|
29
|
+
### Bắt buộc
|
|
30
|
+
|
|
31
|
+
| Biến | Mô tả |
|
|
32
|
+
|------|--------|
|
|
33
|
+
| `CORELICENSE_KEY` | License key do admin cấp (`CL-...` hoặc `CL1.<base64url-api>.CL-...`) |
|
|
34
|
+
| `CORELICENSE_PRODUCT_ID` | Product ID trên License Server |
|
|
35
|
+
|
|
36
|
+
### Tùy chọn
|
|
37
|
+
|
|
38
|
+
| Biến | Mô tả | Mặc định |
|
|
39
|
+
|------|--------|----------|
|
|
40
|
+
| `CORELICENSE_API_URL` | Override URL License API | `https://api.corelicense.net` |
|
|
41
|
+
| `CORELICENSE_PUBLIC_KEY` | Pin public key (SPKI PEM) — bỏ qua fetch keys | auto-fetch |
|
|
42
|
+
|
|
43
|
+
**Thứ tự resolve API URL** (không cần set thủ công trên production):
|
|
44
|
+
|
|
45
|
+
1. `options.apiUrl` / `CORELICENSE_API_URL`
|
|
46
|
+
2. URL nhúng trong key `CL1.<base64url>.CL-...`
|
|
47
|
+
3. Cache `sdk.endpoint.json`
|
|
48
|
+
4. `https://api.corelicense.net`
|
|
49
|
+
|
|
50
|
+
### Dev local
|
|
51
|
+
|
|
52
|
+
```env
|
|
53
|
+
CORELICENSE_KEY=CL-AAAAA-BBBBB-CCCCC-DDDDD-EEEEE
|
|
54
|
+
CORELICENSE_PRODUCT_ID=demo
|
|
55
|
+
CORELICENSE_API_URL=http://localhost:8080
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Production
|
|
59
|
+
|
|
60
|
+
```env
|
|
61
|
+
CORELICENSE_KEY=CL1.xxxxx.CL-AAAAA-BBBBB-...
|
|
62
|
+
CORELICENSE_PRODUCT_ID=my-product
|
|
63
|
+
# Không cần CORELICENSE_API_URL — SDK dùng api.corelicense.net
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Server Go production cần `SDK_PUBLIC_URL=https://api.corelicense.net` khi generate key `CL1.*`.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 3. Khởi tạo tối thiểu
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { CoreLicense, loadOptionsFromEnv } from '@corelicense/node';
|
|
74
|
+
|
|
75
|
+
const license = new CoreLicense(
|
|
76
|
+
loadOptionsFromEnv({
|
|
77
|
+
cachePath: './storage/corelicense',
|
|
78
|
+
instance: {
|
|
79
|
+
strategy: 'install-id-domain-hash',
|
|
80
|
+
domain: process.env.APP_DOMAIN, // ví dụ: app.example.com
|
|
81
|
+
},
|
|
82
|
+
audit: {
|
|
83
|
+
enabled: true,
|
|
84
|
+
path: './storage/logs/corelicense.log',
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
await license.boot();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`loadOptionsFromEnv()` throw `ConfigError` (`CONFIG_MISSING_ENV`) nếu thiếu biến bắt buộc.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 4. `CoreLicenseOptions` (constructor)
|
|
97
|
+
|
|
98
|
+
| Field | Type | Mặc định | Mô tả |
|
|
99
|
+
|-------|------|----------|--------|
|
|
100
|
+
| `productId` | `string` | — | **Bắt buộc** |
|
|
101
|
+
| `licenseKey` | `string` | — | **Bắt buộc** |
|
|
102
|
+
| `apiUrl` | `string` | auto | License Server base URL |
|
|
103
|
+
| `publicKey` | `string` | — | Pin SPKI PEM |
|
|
104
|
+
| `cachePath` | `string` | `./storage/corelicense` | Thư mục cache |
|
|
105
|
+
| `checkIntervalMs` | `number` | `21600000` (6h) | Background refresh; `0` = tắt |
|
|
106
|
+
| `requestTimeoutMs` | `number` | `5000` | HTTP timeout |
|
|
107
|
+
| `offlineGrace` | `boolean` | `true` | Cho phép dùng cache khi server unreachable |
|
|
108
|
+
| `defaultBlockMode` | `'block' \| 'limited'` | `'limited'` | Middleware mode mặc định |
|
|
109
|
+
| `instance.strategy` | `'install-id-domain-hash' \| 'custom'` | `'install-id-domain-hash'` | Cách tính instance ID |
|
|
110
|
+
| `instance.domain` | `string` | — | Domain app (cho hash) |
|
|
111
|
+
| `instance.customId` | `string` | — | Khi `strategy: 'custom'` |
|
|
112
|
+
| `audit.enabled` | `boolean` | `true` | Ghi audit local |
|
|
113
|
+
| `audit.path` | `string` | `./storage/logs/corelicense.log` | File audit |
|
|
114
|
+
| `appVersion` | `string` | — | Gửi kèm SDK info lên server |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 5. Luồng `boot()`
|
|
119
|
+
|
|
120
|
+
1. Resolve API URL (pin / cache / env / default)
|
|
121
|
+
2. Tính **instance ID** (install-id + domain hash)
|
|
122
|
+
3. Fetch hoặc dùng **pinned public key**
|
|
123
|
+
4. Load policy token từ cache → verify
|
|
124
|
+
5. Nếu cần: `activate` (lần đầu) hoặc `check` (refresh)
|
|
125
|
+
6. Bật background checker (nếu `checkIntervalMs > 0`)
|
|
126
|
+
7. **`requireActive()`** — fail-closed nếu license không active
|
|
127
|
+
|
|
128
|
+
**Lưu ý v0.5:** Offline + grace hết hạn → `boot()` **throw**, không cho app chạy “im lặng”.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 6. Pin public key (enterprise)
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
const license = new CoreLicense(
|
|
136
|
+
loadOptionsFromEnv({
|
|
137
|
+
publicKey: process.env.CORELICENSE_PUBLIC_KEY,
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Khi pin, SDK **không** gọi `GET /v1/public/products/{id}/keys`. Sau key rotation trên server, cập nhật `CORELICENSE_PUBLIC_KEY` và restart app.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 7. File trên disk
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
storage/corelicense/
|
|
150
|
+
instance.json # instanceId đã resolve
|
|
151
|
+
policy.token # JWT policy (signed)
|
|
152
|
+
usage.json # quota usage theo ngày
|
|
153
|
+
sdk.endpoint.json # apiUrl tin cậy từ server
|
|
154
|
+
|
|
155
|
+
storage/logs/
|
|
156
|
+
corelicense.log # audit events (JSON lines)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Thêm `storage/` vào `.gitignore` của app khách.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 8. Kiểm tra sau cài
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
const status = await license.getStatus();
|
|
167
|
+
console.log(status); // { status: 'active', ... }
|
|
168
|
+
|
|
169
|
+
const policy = await license.getPolicy();
|
|
170
|
+
console.log(policy?.features);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
HTTP health route (không qua license guard):
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
app.get('/health', (_req, res) => res.json({ ok: true }));
|
|
177
|
+
app.get('/license/status', async (_req, res) => {
|
|
178
|
+
res.json(await license.getStatus());
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 9. Bước tiếp theo
|
|
185
|
+
|
|
186
|
+
→ [Tích hợp chi tiết](./integration.md) — Express, NestJS, module registry, DI, worker, production checklist.
|
|
187
|
+
|
|
188
|
+
→ [API Reference](./api-reference.md) — danh sách method và error codes.
|