hik-iot-sdk 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 +467 -0
- package/dist/core/__tests__/crypto.test.d.ts +1 -0
- package/dist/core/__tests__/crypto.test.js +68 -0
- package/dist/core/auth.d.ts +51 -0
- package/dist/core/auth.js +292 -0
- package/dist/core/config.d.ts +31 -0
- package/dist/core/config.js +11 -0
- package/dist/core/crypto.d.ts +12 -0
- package/dist/core/crypto.js +88 -0
- package/dist/core/errors.d.ts +45 -0
- package/dist/core/errors.js +61 -0
- package/dist/core/http.d.ts +29 -0
- package/dist/core/http.js +134 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +10 -0
- package/dist/modules/access/access.schema.d.ts +2307 -0
- package/dist/modules/access/access.schema.js +353 -0
- package/dist/modules/access/access.service.d.ts +55 -0
- package/dist/modules/access/access.service.js +145 -0
- package/dist/modules/access/access.types.d.ts +307 -0
- package/dist/modules/access/access.types.js +3 -0
- package/dist/modules/access/index.d.ts +2 -0
- package/dist/modules/access/index.js +20 -0
- package/dist/modules/card/card.schema.d.ts +27 -0
- package/dist/modules/card/card.schema.js +12 -0
- package/dist/modules/card/card.service.d.ts +10 -0
- package/dist/modules/card/card.service.js +50 -0
- package/dist/modules/card/card.types.d.ts +18 -0
- package/dist/modules/card/card.types.js +2 -0
- package/dist/modules/device/device.schema.d.ts +258 -0
- package/dist/modules/device/device.schema.js +44 -0
- package/dist/modules/device/device.service.d.ts +14 -0
- package/dist/modules/device/device.service.js +69 -0
- package/dist/modules/device/device.types.d.ts +46 -0
- package/dist/modules/device/device.types.js +2 -0
- package/dist/modules/face/face.schema.d.ts +27 -0
- package/dist/modules/face/face.schema.js +12 -0
- package/dist/modules/face/face.service.d.ts +9 -0
- package/dist/modules/face/face.service.js +42 -0
- package/dist/modules/face/face.types.d.ts +17 -0
- package/dist/modules/face/face.types.js +2 -0
- package/dist/modules/person/person.schema.d.ts +270 -0
- package/dist/modules/person/person.schema.js +42 -0
- package/dist/modules/person/person.service.d.ts +16 -0
- package/dist/modules/person/person.service.js +88 -0
- package/dist/modules/person/person.types.d.ts +65 -0
- package/dist/modules/person/person.types.js +2 -0
- package/dist/sdk.d.ts +60 -0
- package/dist/sdk.js +106 -0
- package/package.json +29 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AuthManager = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
const pino_1 = __importDefault(require("pino"));
|
|
10
|
+
const errors_1 = require("./errors");
|
|
11
|
+
const crypto_1 = require("./crypto");
|
|
12
|
+
const logger = (0, pino_1.default)({ name: 'hik-auth' });
|
|
13
|
+
const AppTokenResponseSchema = zod_1.z.object({
|
|
14
|
+
code: zod_1.z.number(),
|
|
15
|
+
msg: zod_1.z.string(),
|
|
16
|
+
data: zod_1.z.object({
|
|
17
|
+
appKey: zod_1.z.string(),
|
|
18
|
+
appAccessToken: zod_1.z.string(),
|
|
19
|
+
expiresIn: zod_1.z.number(),
|
|
20
|
+
refreshAppToken: zod_1.z.string(),
|
|
21
|
+
}).optional(),
|
|
22
|
+
});
|
|
23
|
+
const AuthCodeResponseSchema = zod_1.z.object({
|
|
24
|
+
code: zod_1.z.number(),
|
|
25
|
+
msg: zod_1.z.string(),
|
|
26
|
+
data: zod_1.z.object({
|
|
27
|
+
appKey: zod_1.z.string(),
|
|
28
|
+
redirectUrl: zod_1.z.string(),
|
|
29
|
+
authCode: zod_1.z.string(),
|
|
30
|
+
}).optional(),
|
|
31
|
+
});
|
|
32
|
+
const UserTokenResponseSchema = zod_1.z.object({
|
|
33
|
+
code: zod_1.z.number(),
|
|
34
|
+
msg: zod_1.z.string(),
|
|
35
|
+
data: zod_1.z.object({
|
|
36
|
+
appKey: zod_1.z.string(),
|
|
37
|
+
userAccessToken: zod_1.z.string(),
|
|
38
|
+
refreshUserToken: zod_1.z.string(),
|
|
39
|
+
expiresIn: zod_1.z.string(),
|
|
40
|
+
teamNo: zod_1.z.string(),
|
|
41
|
+
personNo: zod_1.z.string(),
|
|
42
|
+
accountNo: zod_1.z.string(),
|
|
43
|
+
}).optional(),
|
|
44
|
+
});
|
|
45
|
+
class AuthManager {
|
|
46
|
+
constructor(config, userCredentials) {
|
|
47
|
+
this.appTokenInfo = null;
|
|
48
|
+
this.userTokenInfo = null;
|
|
49
|
+
this.appTokenPromise = null;
|
|
50
|
+
this.userTokenPromise = null;
|
|
51
|
+
this.config = config;
|
|
52
|
+
this.userCredentials = userCredentials;
|
|
53
|
+
this.crypto = new crypto_1.HikCrypto(config.appSecret);
|
|
54
|
+
this.httpClient = axios_1.default.create({
|
|
55
|
+
baseURL: config.baseUrl,
|
|
56
|
+
timeout: config.timeout,
|
|
57
|
+
headers: {
|
|
58
|
+
'Content-Type': 'application/json',
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
encryptBody(data) {
|
|
63
|
+
if (!this.config.enableEncrypt) {
|
|
64
|
+
return data;
|
|
65
|
+
}
|
|
66
|
+
return this.crypto.encryptBody(data);
|
|
67
|
+
}
|
|
68
|
+
decryptResponse(response) {
|
|
69
|
+
if (!this.config.enableEncrypt || !response.data || typeof response.data !== 'string') {
|
|
70
|
+
return response;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const decryptedData = this.crypto.decrypt(response.data);
|
|
74
|
+
return {
|
|
75
|
+
...response,
|
|
76
|
+
data: JSON.parse(decryptedData),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return response;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async getUserAccessToken() {
|
|
84
|
+
if (this.userTokenInfo && this.isUserTokenValid()) {
|
|
85
|
+
return this.userTokenInfo.userAccessToken;
|
|
86
|
+
}
|
|
87
|
+
return this.fetchUserToken();
|
|
88
|
+
}
|
|
89
|
+
isUserTokenValid() {
|
|
90
|
+
if (!this.userTokenInfo)
|
|
91
|
+
return false;
|
|
92
|
+
const bufferTime = 60 * 60 * 1000;
|
|
93
|
+
return Date.now() < this.userTokenInfo.expiresAt - bufferTime;
|
|
94
|
+
}
|
|
95
|
+
async getAppAccessToken() {
|
|
96
|
+
if (this.appTokenInfo && this.isAppTokenValid()) {
|
|
97
|
+
return this.appTokenInfo.appAccessToken;
|
|
98
|
+
}
|
|
99
|
+
if (this.appTokenInfo && this.shouldRefreshAppToken()) {
|
|
100
|
+
return this.refreshAppToken();
|
|
101
|
+
}
|
|
102
|
+
return this.fetchNewAppToken();
|
|
103
|
+
}
|
|
104
|
+
isAppTokenValid() {
|
|
105
|
+
if (!this.appTokenInfo)
|
|
106
|
+
return false;
|
|
107
|
+
const bufferTime = 10 * 60 * 1000;
|
|
108
|
+
return Date.now() < this.appTokenInfo.expiresAt - bufferTime;
|
|
109
|
+
}
|
|
110
|
+
shouldRefreshAppToken() {
|
|
111
|
+
if (!this.appTokenInfo)
|
|
112
|
+
return false;
|
|
113
|
+
const oneHourMs = 60 * 60 * 1000;
|
|
114
|
+
return Date.now() > this.appTokenInfo.expiresAt - oneHourMs;
|
|
115
|
+
}
|
|
116
|
+
async fetchNewAppToken() {
|
|
117
|
+
if (this.appTokenPromise) {
|
|
118
|
+
const info = await this.appTokenPromise;
|
|
119
|
+
return info.appAccessToken;
|
|
120
|
+
}
|
|
121
|
+
this.appTokenPromise = this.doFetchAppToken();
|
|
122
|
+
try {
|
|
123
|
+
const info = await this.appTokenPromise;
|
|
124
|
+
return info.appAccessToken;
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
this.appTokenPromise = null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async doFetchAppToken() {
|
|
131
|
+
logger.debug('Fetching new app access token');
|
|
132
|
+
// 认证接口不需要加密
|
|
133
|
+
const response = await this.httpClient.post('/auth/exchangeAppToken', {
|
|
134
|
+
appKey: this.config.appKey,
|
|
135
|
+
appSecret: this.config.appSecret,
|
|
136
|
+
});
|
|
137
|
+
const result = AppTokenResponseSchema.parse(response.data);
|
|
138
|
+
if (result.code !== 0 || !result.data) {
|
|
139
|
+
throw new errors_1.HikAuthError(result.msg);
|
|
140
|
+
}
|
|
141
|
+
const expiresInMs = result.data.expiresIn * 60 * 60 * 1000;
|
|
142
|
+
this.appTokenInfo = {
|
|
143
|
+
appAccessToken: result.data.appAccessToken,
|
|
144
|
+
refreshAppToken: result.data.refreshAppToken,
|
|
145
|
+
expiresAt: Date.now() + expiresInMs,
|
|
146
|
+
};
|
|
147
|
+
logger.debug({ expiresIn: result.data.expiresIn }, 'App token fetched successfully');
|
|
148
|
+
return this.appTokenInfo;
|
|
149
|
+
}
|
|
150
|
+
async refreshAppToken() {
|
|
151
|
+
if (!this.appTokenInfo) {
|
|
152
|
+
return this.fetchNewAppToken();
|
|
153
|
+
}
|
|
154
|
+
if (this.appTokenPromise) {
|
|
155
|
+
const info = await this.appTokenPromise;
|
|
156
|
+
return info.appAccessToken;
|
|
157
|
+
}
|
|
158
|
+
this.appTokenPromise = this.doRefreshAppToken();
|
|
159
|
+
try {
|
|
160
|
+
const info = await this.appTokenPromise;
|
|
161
|
+
return info.appAccessToken;
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
this.appTokenPromise = null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async doRefreshAppToken() {
|
|
168
|
+
if (!this.appTokenInfo) {
|
|
169
|
+
return this.doFetchAppToken();
|
|
170
|
+
}
|
|
171
|
+
logger.debug('Refreshing app access token');
|
|
172
|
+
try {
|
|
173
|
+
// 认证接口不需要加密
|
|
174
|
+
const response = await this.httpClient.post('/auth/refreshAppToken', {
|
|
175
|
+
appAccessToken: this.appTokenInfo.appAccessToken,
|
|
176
|
+
refreshAppToken: this.appTokenInfo.refreshAppToken,
|
|
177
|
+
});
|
|
178
|
+
const result = AppTokenResponseSchema.parse(response.data);
|
|
179
|
+
if (result.code !== 0 || !result.data) {
|
|
180
|
+
logger.warn('App token refresh failed, fetching new token');
|
|
181
|
+
return this.doFetchAppToken();
|
|
182
|
+
}
|
|
183
|
+
const expiresInMs = result.data.expiresIn * 60 * 60 * 1000;
|
|
184
|
+
this.appTokenInfo = {
|
|
185
|
+
appAccessToken: result.data.appAccessToken,
|
|
186
|
+
refreshAppToken: result.data.refreshAppToken,
|
|
187
|
+
expiresAt: Date.now() + expiresInMs,
|
|
188
|
+
};
|
|
189
|
+
logger.debug({ expiresIn: result.data.expiresIn }, 'App token refreshed successfully');
|
|
190
|
+
return this.appTokenInfo;
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
logger.warn({ error }, 'App token refresh error, fetching new token');
|
|
194
|
+
return this.doFetchAppToken();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async fetchUserToken() {
|
|
198
|
+
if (this.userTokenPromise) {
|
|
199
|
+
const info = await this.userTokenPromise;
|
|
200
|
+
return info.userAccessToken;
|
|
201
|
+
}
|
|
202
|
+
this.userTokenPromise = this.doFetchUserToken();
|
|
203
|
+
try {
|
|
204
|
+
const info = await this.userTokenPromise;
|
|
205
|
+
return info.userAccessToken;
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
this.userTokenPromise = null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async doFetchUserToken() {
|
|
212
|
+
logger.debug('Fetching user access token');
|
|
213
|
+
const authCode = await this.applyAuthCode();
|
|
214
|
+
const appAccessToken = await this.getAppAccessToken();
|
|
215
|
+
// GET 请求参数需要加密
|
|
216
|
+
let url = '/auth/third/code2Token';
|
|
217
|
+
if (this.config.enableEncrypt) {
|
|
218
|
+
const querySecret = this.crypto.encryptParams({ authCode });
|
|
219
|
+
url = `/auth/third/code2Token?querySecret=${encodeURIComponent(querySecret)}`;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
url = `/auth/third/code2Token?authCode=${authCode}`;
|
|
223
|
+
}
|
|
224
|
+
const response = await this.httpClient.get(url, {
|
|
225
|
+
headers: {
|
|
226
|
+
'App-Access-Token': appAccessToken,
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
// 解密响应数据
|
|
230
|
+
let responseData = response.data;
|
|
231
|
+
if (this.config.enableEncrypt && responseData.data && typeof responseData.data === 'string') {
|
|
232
|
+
try {
|
|
233
|
+
const decryptedData = this.crypto.decrypt(responseData.data);
|
|
234
|
+
responseData = {
|
|
235
|
+
...responseData,
|
|
236
|
+
data: JSON.parse(decryptedData),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
logger.warn({ error }, 'Failed to decrypt user token response');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const result = UserTokenResponseSchema.parse(responseData);
|
|
244
|
+
if (result.code !== 0 || !result.data) {
|
|
245
|
+
throw new errors_1.HikAuthError(result.msg);
|
|
246
|
+
}
|
|
247
|
+
const expiresInDays = parseInt(result.data.expiresIn, 10);
|
|
248
|
+
const expiresInMs = expiresInDays * 24 * 60 * 60 * 1000;
|
|
249
|
+
this.userTokenInfo = {
|
|
250
|
+
userAccessToken: result.data.userAccessToken,
|
|
251
|
+
refreshUserToken: result.data.refreshUserToken,
|
|
252
|
+
expiresAt: Date.now() + expiresInMs,
|
|
253
|
+
teamNo: result.data.teamNo,
|
|
254
|
+
personNo: result.data.personNo,
|
|
255
|
+
accountNo: result.data.accountNo,
|
|
256
|
+
};
|
|
257
|
+
logger.debug({ expiresIn: result.data.expiresIn }, 'User token fetched successfully');
|
|
258
|
+
return this.userTokenInfo;
|
|
259
|
+
}
|
|
260
|
+
async applyAuthCode() {
|
|
261
|
+
logger.debug('Applying auth code');
|
|
262
|
+
// 认证接口不需要加密
|
|
263
|
+
const response = await this.httpClient.post('/auth/third/applyAuthCode', {
|
|
264
|
+
appKey: this.config.appKey,
|
|
265
|
+
userName: this.userCredentials.userName,
|
|
266
|
+
password: this.userCredentials.password,
|
|
267
|
+
redirectUrl: this.userCredentials.redirectUrl,
|
|
268
|
+
});
|
|
269
|
+
const result = AuthCodeResponseSchema.parse(response.data);
|
|
270
|
+
if (result.code !== 0 || !result.data) {
|
|
271
|
+
throw new errors_1.HikAuthError(result.msg);
|
|
272
|
+
}
|
|
273
|
+
logger.debug('Auth code obtained successfully');
|
|
274
|
+
return result.data.authCode;
|
|
275
|
+
}
|
|
276
|
+
clearAppToken() {
|
|
277
|
+
this.appTokenInfo = null;
|
|
278
|
+
}
|
|
279
|
+
clearUserToken() {
|
|
280
|
+
this.userTokenInfo = null;
|
|
281
|
+
}
|
|
282
|
+
getUserInfo() {
|
|
283
|
+
if (!this.userTokenInfo)
|
|
284
|
+
return null;
|
|
285
|
+
return {
|
|
286
|
+
teamNo: this.userTokenInfo.teamNo,
|
|
287
|
+
personNo: this.userTokenInfo.personNo,
|
|
288
|
+
accountNo: this.userTokenInfo.accountNo,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
exports.AuthManager = AuthManager;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const HikConfigSchema: z.ZodObject<{
|
|
3
|
+
appKey: z.ZodString;
|
|
4
|
+
appSecret: z.ZodString;
|
|
5
|
+
baseUrl: z.ZodDefault<z.ZodString>;
|
|
6
|
+
enableEncrypt: z.ZodDefault<z.ZodBoolean>;
|
|
7
|
+
timeout: z.ZodDefault<z.ZodNumber>;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
appKey: string;
|
|
10
|
+
appSecret: string;
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
enableEncrypt: boolean;
|
|
13
|
+
timeout: number;
|
|
14
|
+
}, {
|
|
15
|
+
appKey: string;
|
|
16
|
+
appSecret: string;
|
|
17
|
+
baseUrl?: string | undefined;
|
|
18
|
+
enableEncrypt?: boolean | undefined;
|
|
19
|
+
timeout?: number | undefined;
|
|
20
|
+
}>;
|
|
21
|
+
export type HikConfig = z.infer<typeof HikConfigSchema>;
|
|
22
|
+
export interface HikSdkOptions {
|
|
23
|
+
appKey: string;
|
|
24
|
+
appSecret: string;
|
|
25
|
+
userName: string;
|
|
26
|
+
password: string;
|
|
27
|
+
redirectUrl?: string;
|
|
28
|
+
baseUrl?: string;
|
|
29
|
+
enableEncrypt?: boolean;
|
|
30
|
+
timeout?: number;
|
|
31
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HikConfigSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
exports.HikConfigSchema = zod_1.z.object({
|
|
6
|
+
appKey: zod_1.z.string().min(1, 'appKey is required'),
|
|
7
|
+
appSecret: zod_1.z.string().min(1, 'appSecret is required'),
|
|
8
|
+
baseUrl: zod_1.z.string().url().default('https://open-api.hikiot.com'),
|
|
9
|
+
enableEncrypt: zod_1.z.boolean().default(true), // 海康互联默认开启加密
|
|
10
|
+
timeout: zod_1.z.number().positive().default(30000),
|
|
11
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class HikCrypto {
|
|
2
|
+
private readonly privateKeyPem;
|
|
3
|
+
constructor(appSecret: string);
|
|
4
|
+
private formatPrivateKey;
|
|
5
|
+
private formatBase64;
|
|
6
|
+
encrypt(data: string): string;
|
|
7
|
+
decrypt(data: string): string;
|
|
8
|
+
encryptParams(params: Record<string, string | number | boolean>): string;
|
|
9
|
+
encryptBody(body: Record<string, unknown>): {
|
|
10
|
+
bodySecret: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.HikCrypto = void 0;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
const node_forge_1 = __importDefault(require("node-forge"));
|
|
9
|
+
class HikCrypto {
|
|
10
|
+
constructor(appSecret) {
|
|
11
|
+
// appSecret 是 PKCS#8 格式的私钥 base64 编码
|
|
12
|
+
this.privateKeyPem = this.formatPrivateKey(appSecret);
|
|
13
|
+
}
|
|
14
|
+
formatPrivateKey(appSecret) {
|
|
15
|
+
// 如果已经是 PEM 格式,直接返回
|
|
16
|
+
if (appSecret.includes('-----BEGIN')) {
|
|
17
|
+
return appSecret;
|
|
18
|
+
}
|
|
19
|
+
// 海康的 appSecret 是 PKCS#8 格式的私钥
|
|
20
|
+
// 需要添加正确的 PEM 头部
|
|
21
|
+
// 先尝试 PKCS#8 格式
|
|
22
|
+
const pkcs8Pem = `-----BEGIN PRIVATE KEY-----\n${this.formatBase64(appSecret)}\n-----END PRIVATE KEY-----`;
|
|
23
|
+
try {
|
|
24
|
+
// 验证是否是有效的私钥
|
|
25
|
+
crypto_1.default.createPrivateKey(pkcs8Pem);
|
|
26
|
+
return pkcs8Pem;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// 如果 PKCS#8 失败,尝试 PKCS#1 格式
|
|
30
|
+
return `-----BEGIN RSA PRIVATE KEY-----\n${this.formatBase64(appSecret)}\n-----END RSA PRIVATE KEY-----`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
formatBase64(base64) {
|
|
34
|
+
// 将 base64 字符串按 64 字符换行
|
|
35
|
+
const lines = [];
|
|
36
|
+
for (let i = 0; i < base64.length; i += 64) {
|
|
37
|
+
lines.push(base64.slice(i, i + 64));
|
|
38
|
+
}
|
|
39
|
+
return lines.join('\n');
|
|
40
|
+
}
|
|
41
|
+
encrypt(data) {
|
|
42
|
+
const chunkSize = 117; // RSA 1024 位密钥最大加密长度(字节)
|
|
43
|
+
const encryptedChunks = [];
|
|
44
|
+
const dataBuffer = Buffer.from(data, 'utf-8');
|
|
45
|
+
for (let i = 0; i < dataBuffer.length; i += chunkSize) {
|
|
46
|
+
const chunk = dataBuffer.slice(i, i + chunkSize);
|
|
47
|
+
const encryptedChunk = crypto_1.default.privateEncrypt({
|
|
48
|
+
key: this.privateKeyPem,
|
|
49
|
+
padding: crypto_1.default.constants.RSA_PKCS1_PADDING,
|
|
50
|
+
}, chunk);
|
|
51
|
+
encryptedChunks.push(encryptedChunk);
|
|
52
|
+
}
|
|
53
|
+
return Buffer.concat(encryptedChunks).toString('base64');
|
|
54
|
+
}
|
|
55
|
+
decrypt(data) {
|
|
56
|
+
const decodedData = Buffer.from(data, 'base64');
|
|
57
|
+
const pki = node_forge_1.default.pki;
|
|
58
|
+
const key = pki.privateKeyFromPem(this.privateKeyPem);
|
|
59
|
+
const chunkSize = 128; // RSA 1024 位密钥加密后的块大小
|
|
60
|
+
const decryptedChunks = [];
|
|
61
|
+
for (let i = 0; i < decodedData.length; i += chunkSize) {
|
|
62
|
+
const chunk = decodedData.slice(i, i + chunkSize);
|
|
63
|
+
const forgeBuffer = node_forge_1.default.util.createBuffer(chunk.toString('binary'));
|
|
64
|
+
const decryptedChunk = key.decrypt(forgeBuffer.getBytes(), 'RSAES-PKCS1-V1_5');
|
|
65
|
+
decryptedChunks.push(Buffer.from(decryptedChunk, 'binary'));
|
|
66
|
+
}
|
|
67
|
+
const decryptedBuffer = Buffer.concat(decryptedChunks);
|
|
68
|
+
const rawString = decryptedBuffer.toString('utf-8');
|
|
69
|
+
// 海康返回的数据是 URL 编码的
|
|
70
|
+
try {
|
|
71
|
+
return decodeURIComponent(rawString);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return rawString;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
encryptParams(params) {
|
|
78
|
+
const queryString = Object.entries(params)
|
|
79
|
+
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
|
|
80
|
+
.join('&');
|
|
81
|
+
return this.encrypt(queryString);
|
|
82
|
+
}
|
|
83
|
+
encryptBody(body) {
|
|
84
|
+
const jsonString = JSON.stringify(body);
|
|
85
|
+
return { bodySecret: this.encrypt(jsonString) };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.HikCrypto = HikCrypto;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export declare class HikError extends Error {
|
|
2
|
+
readonly code: number;
|
|
3
|
+
readonly detail?: string;
|
|
4
|
+
constructor(code: number, message: string, detail?: string);
|
|
5
|
+
static fromResponse(response: {
|
|
6
|
+
code: number;
|
|
7
|
+
msg: string;
|
|
8
|
+
detail?: string;
|
|
9
|
+
}): HikError;
|
|
10
|
+
}
|
|
11
|
+
export declare class HikAuthError extends HikError {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class HikDeviceError extends HikError {
|
|
15
|
+
constructor(code: number, message: string, detail?: string);
|
|
16
|
+
}
|
|
17
|
+
export declare const ERROR_CODES: {
|
|
18
|
+
readonly SUCCESS: 0;
|
|
19
|
+
readonly SYSTEM_ERROR: 400000;
|
|
20
|
+
readonly API_NOT_FOUND: 400001;
|
|
21
|
+
readonly RATE_LIMIT: 400002;
|
|
22
|
+
readonly API_DISABLED: 400003;
|
|
23
|
+
readonly NO_PERMISSION: 400004;
|
|
24
|
+
readonly INVALID_APP_KEY: 400005;
|
|
25
|
+
readonly MISSING_APP_KEY: 400006;
|
|
26
|
+
readonly MISSING_TEAM_NO: 400007;
|
|
27
|
+
readonly APP_NOT_EXIST: 400008;
|
|
28
|
+
readonly INVALID_APP_ACCESS_TOKEN: 400015;
|
|
29
|
+
readonly INVALID_USER_ACCESS_TOKEN: 400019;
|
|
30
|
+
readonly DEVICE_NOT_EXIST: 160001;
|
|
31
|
+
readonly REQUEST_TIMEOUT: 160099;
|
|
32
|
+
readonly DEVICE_OFFLINE: 160101;
|
|
33
|
+
readonly DEVICE_TIMEOUT: 160102;
|
|
34
|
+
readonly DEVICE_SETTING_ERROR: 160103;
|
|
35
|
+
readonly DEVICE_NOT_SUPPORT: 160104;
|
|
36
|
+
readonly CARD_LIMIT_REACHED: 160111;
|
|
37
|
+
readonly PERSON_DELETED: 160112;
|
|
38
|
+
readonly FACE_TOO_SMALL: 160113;
|
|
39
|
+
readonly FACE_INVALID: 160114;
|
|
40
|
+
readonly FACE_DOWNLOAD_FAILED: 160115;
|
|
41
|
+
readonly PERSON_LIMIT_REACHED: 160116;
|
|
42
|
+
readonly FACE_NOT_DETECTED: 160117;
|
|
43
|
+
readonly REMOTE_OPEN_NOT_SUPPORT: 160119;
|
|
44
|
+
readonly DOOR_OFFLINE: 160120;
|
|
45
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ERROR_CODES = exports.HikDeviceError = exports.HikAuthError = exports.HikError = void 0;
|
|
4
|
+
class HikError extends Error {
|
|
5
|
+
constructor(code, message, detail) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'HikError';
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.detail = detail;
|
|
10
|
+
Object.setPrototypeOf(this, HikError.prototype);
|
|
11
|
+
}
|
|
12
|
+
static fromResponse(response) {
|
|
13
|
+
return new HikError(response.code, response.msg, response.detail);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.HikError = HikError;
|
|
17
|
+
class HikAuthError extends HikError {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super(400015, message);
|
|
20
|
+
this.name = 'HikAuthError';
|
|
21
|
+
Object.setPrototypeOf(this, HikAuthError.prototype);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.HikAuthError = HikAuthError;
|
|
25
|
+
class HikDeviceError extends HikError {
|
|
26
|
+
constructor(code, message, detail) {
|
|
27
|
+
super(code, message, detail);
|
|
28
|
+
this.name = 'HikDeviceError';
|
|
29
|
+
Object.setPrototypeOf(this, HikDeviceError.prototype);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.HikDeviceError = HikDeviceError;
|
|
33
|
+
exports.ERROR_CODES = {
|
|
34
|
+
SUCCESS: 0,
|
|
35
|
+
SYSTEM_ERROR: 400000,
|
|
36
|
+
API_NOT_FOUND: 400001,
|
|
37
|
+
RATE_LIMIT: 400002,
|
|
38
|
+
API_DISABLED: 400003,
|
|
39
|
+
NO_PERMISSION: 400004,
|
|
40
|
+
INVALID_APP_KEY: 400005,
|
|
41
|
+
MISSING_APP_KEY: 400006,
|
|
42
|
+
MISSING_TEAM_NO: 400007,
|
|
43
|
+
APP_NOT_EXIST: 400008,
|
|
44
|
+
INVALID_APP_ACCESS_TOKEN: 400015,
|
|
45
|
+
INVALID_USER_ACCESS_TOKEN: 400019,
|
|
46
|
+
DEVICE_NOT_EXIST: 160001,
|
|
47
|
+
REQUEST_TIMEOUT: 160099,
|
|
48
|
+
DEVICE_OFFLINE: 160101,
|
|
49
|
+
DEVICE_TIMEOUT: 160102,
|
|
50
|
+
DEVICE_SETTING_ERROR: 160103,
|
|
51
|
+
DEVICE_NOT_SUPPORT: 160104,
|
|
52
|
+
CARD_LIMIT_REACHED: 160111,
|
|
53
|
+
PERSON_DELETED: 160112,
|
|
54
|
+
FACE_TOO_SMALL: 160113,
|
|
55
|
+
FACE_INVALID: 160114,
|
|
56
|
+
FACE_DOWNLOAD_FAILED: 160115,
|
|
57
|
+
PERSON_LIMIT_REACHED: 160116,
|
|
58
|
+
FACE_NOT_DETECTED: 160117,
|
|
59
|
+
REMOTE_OPEN_NOT_SUPPORT: 160119,
|
|
60
|
+
DOOR_OFFLINE: 160120,
|
|
61
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { HikConfig } from './config';
|
|
3
|
+
import type { AuthManager } from './auth';
|
|
4
|
+
export type BaseResponse<T = unknown> = {
|
|
5
|
+
code: number;
|
|
6
|
+
msg: string;
|
|
7
|
+
data?: T;
|
|
8
|
+
detail?: string | null;
|
|
9
|
+
count?: number;
|
|
10
|
+
};
|
|
11
|
+
export declare class HttpClient {
|
|
12
|
+
private readonly client;
|
|
13
|
+
private readonly authManager;
|
|
14
|
+
private readonly config;
|
|
15
|
+
private readonly crypto;
|
|
16
|
+
constructor(config: HikConfig, authManager: AuthManager);
|
|
17
|
+
private setupInterceptors;
|
|
18
|
+
post<T>(path: string, data?: object, schema?: z.ZodType<T>): Promise<T>;
|
|
19
|
+
get<T>(path: string, config?: {
|
|
20
|
+
params?: Record<string, string | number | boolean>;
|
|
21
|
+
headers?: Record<string, string>;
|
|
22
|
+
}, schema?: z.ZodType<T>): Promise<T>;
|
|
23
|
+
private handleResponse;
|
|
24
|
+
}
|
|
25
|
+
declare module 'axios' {
|
|
26
|
+
interface AxiosRequestConfig {
|
|
27
|
+
_retry?: boolean;
|
|
28
|
+
}
|
|
29
|
+
}
|