node-opcua-server-configuration 2.97.0 → 2.98.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/dist/clientTools/index.d.ts +1 -1
- package/dist/clientTools/index.js +17 -17
- package/dist/clientTools/push_certificate_management_client.d.ts +176 -176
- package/dist/clientTools/push_certificate_management_client.js +463 -463
- package/dist/clientTools/push_certificate_management_client.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.js +27 -27
- package/dist/push_certificate_manager.d.ts +141 -141
- package/dist/push_certificate_manager.js +2 -2
- package/dist/server/install_certificate_file_watcher.d.ts +5 -5
- package/dist/server/install_certificate_file_watcher.js +23 -23
- package/dist/server/install_push_certitifate_management.d.ts +19 -19
- package/dist/server/install_push_certitifate_management.js +215 -215
- package/dist/server/promote_trust_list.d.ts +6 -6
- package/dist/server/promote_trust_list.js +175 -175
- package/dist/server/push_certificate_manager_helpers.d.ts +4 -4
- package/dist/server/push_certificate_manager_helpers.js +411 -411
- package/dist/server/push_certificate_manager_server_impl.d.ts +47 -47
- package/dist/server/push_certificate_manager_server_impl.js +525 -525
- package/dist/server/roles_and_permissions.d.ts +3 -3
- package/dist/server/roles_and_permissions.js +38 -38
- package/dist/server/tools.d.ts +3 -3
- package/dist/server/tools.js +19 -19
- package/dist/server/trust_list_server.d.ts +13 -13
- package/dist/server/trust_list_server.js +89 -89
- package/dist/standard_certificate_types.d.ts +6 -6
- package/dist/standard_certificate_types.js +13 -13
- package/dist/trust_list.d.ts +79 -79
- package/dist/trust_list.js +2 -2
- package/dist/trust_list_impl.js +25 -25
- package/package.json +32 -28
- package/bin/configurator.ts +0 -304
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-opcua-server-configuration",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "pure nodejs OPCUA SDK - module -
|
|
3
|
+
"version": "2.98.1",
|
|
4
|
+
"description": "pure nodejs OPCUA SDK - module server-configuration",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsc -b",
|
|
7
7
|
"lint": "eslint source/**/*.ts",
|
|
@@ -12,36 +12,36 @@
|
|
|
12
12
|
"types": "./dist/index.d.ts",
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"chalk": "4.1.2",
|
|
15
|
-
"memfs": "^3.
|
|
16
|
-
"node-opcua-address-space": "2.
|
|
17
|
-
"node-opcua-address-space-base": "2.
|
|
18
|
-
"node-opcua-assert": "2.
|
|
19
|
-
"node-opcua-basic-types": "2.
|
|
20
|
-
"node-opcua-binary-stream": "2.
|
|
21
|
-
"node-opcua-certificate-manager": "2.
|
|
22
|
-
"node-opcua-client": "2.
|
|
23
|
-
"node-opcua-common": "2.
|
|
24
|
-
"node-opcua-constants": "2.
|
|
15
|
+
"memfs": "^3.5.0",
|
|
16
|
+
"node-opcua-address-space": "2.98.1",
|
|
17
|
+
"node-opcua-address-space-base": "2.98.1",
|
|
18
|
+
"node-opcua-assert": "2.98.1",
|
|
19
|
+
"node-opcua-basic-types": "2.98.1",
|
|
20
|
+
"node-opcua-binary-stream": "2.98.1",
|
|
21
|
+
"node-opcua-certificate-manager": "2.98.1",
|
|
22
|
+
"node-opcua-client": "2.98.1",
|
|
23
|
+
"node-opcua-common": "2.98.1",
|
|
24
|
+
"node-opcua-constants": "2.98.1",
|
|
25
25
|
"node-opcua-crypto": "^2.1.2",
|
|
26
|
-
"node-opcua-data-model": "2.
|
|
27
|
-
"node-opcua-debug": "2.
|
|
28
|
-
"node-opcua-file-transfer": "2.
|
|
29
|
-
"node-opcua-hostname": "2.
|
|
30
|
-
"node-opcua-nodeid": "2.
|
|
26
|
+
"node-opcua-data-model": "2.98.1",
|
|
27
|
+
"node-opcua-debug": "2.98.1",
|
|
28
|
+
"node-opcua-file-transfer": "2.98.1",
|
|
29
|
+
"node-opcua-hostname": "2.98.1",
|
|
30
|
+
"node-opcua-nodeid": "2.98.1",
|
|
31
31
|
"node-opcua-pki": "^3.0.2",
|
|
32
|
-
"node-opcua-pseudo-session": "2.
|
|
33
|
-
"node-opcua-secure-channel": "2.
|
|
34
|
-
"node-opcua-server": "2.
|
|
35
|
-
"node-opcua-service-translate-browse-path": "2.
|
|
36
|
-
"node-opcua-status-code": "2.
|
|
37
|
-
"node-opcua-types": "2.
|
|
38
|
-
"node-opcua-variant": "2.
|
|
32
|
+
"node-opcua-pseudo-session": "2.98.1",
|
|
33
|
+
"node-opcua-secure-channel": "2.98.1",
|
|
34
|
+
"node-opcua-server": "2.98.1",
|
|
35
|
+
"node-opcua-service-translate-browse-path": "2.98.1",
|
|
36
|
+
"node-opcua-status-code": "2.98.1",
|
|
37
|
+
"node-opcua-types": "2.98.1",
|
|
38
|
+
"node-opcua-variant": "2.98.1",
|
|
39
39
|
"rimraf": "3.0.2"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"node-opcua-data-value": "2.
|
|
43
|
-
"node-opcua-leak-detector": "2.
|
|
44
|
-
"node-opcua-nodesets": "2.
|
|
42
|
+
"node-opcua-data-value": "2.98.1",
|
|
43
|
+
"node-opcua-leak-detector": "2.98.1",
|
|
44
|
+
"node-opcua-nodesets": "2.98.1",
|
|
45
45
|
"should": "^13.2.3"
|
|
46
46
|
},
|
|
47
47
|
"author": "Etienne Rossignon",
|
|
@@ -59,5 +59,9 @@
|
|
|
59
59
|
"internet of things"
|
|
60
60
|
],
|
|
61
61
|
"homepage": "http://node-opcua.github.io/",
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "07dcdd8e8c7f2b55544c6e23023093e35674829c",
|
|
63
|
+
"files": [
|
|
64
|
+
"dist",
|
|
65
|
+
"source"
|
|
66
|
+
]
|
|
63
67
|
}
|
package/bin/configurator.ts
DELETED
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
import * as path from "path";
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
import * as util from "util";
|
|
4
|
-
import * as os from "os";
|
|
5
|
-
import {
|
|
6
|
-
AttributeIds,
|
|
7
|
-
ClientSession,
|
|
8
|
-
IBasicSession,
|
|
9
|
-
makeApplicationUrn,
|
|
10
|
-
MessageSecurityMode,
|
|
11
|
-
OPCUAClient,
|
|
12
|
-
resolveNodeId,
|
|
13
|
-
SecurityPolicy,
|
|
14
|
-
StatusCodes,
|
|
15
|
-
UserTokenType
|
|
16
|
-
} from "node-opcua-client";
|
|
17
|
-
import {
|
|
18
|
-
Certificate,
|
|
19
|
-
exploreCertificate,
|
|
20
|
-
exploreCertificateSigningRequest,
|
|
21
|
-
makeSHA1Thumbprint,
|
|
22
|
-
publicKeyAndPrivateKeyMatches,
|
|
23
|
-
readCertificate,
|
|
24
|
-
readCertificateRevocationList,
|
|
25
|
-
split_der,
|
|
26
|
-
toPem
|
|
27
|
-
} from "node-opcua-crypto";
|
|
28
|
-
import { CertificateAuthority } from "node-opcua-pki";
|
|
29
|
-
import { OPCUACertificateManager } from "node-opcua-certificate-manager";
|
|
30
|
-
import { TrustListDataType } from "node-opcua-types";
|
|
31
|
-
|
|
32
|
-
import { CertificateType } from "../source";
|
|
33
|
-
import { ClientPushCertificateManagement } from "../source/clientTools";
|
|
34
|
-
|
|
35
|
-
const endpointUrl = "opc.tcp://localhost:48010";
|
|
36
|
-
|
|
37
|
-
function dumpCertificateInfo(certificate: Certificate) {
|
|
38
|
-
console.log("thumbprint ", makeSHA1Thumbprint(certificate).toString("hex").toUpperCase());
|
|
39
|
-
const i = exploreCertificate(certificate);
|
|
40
|
-
console.log(" serial number : ", i.tbsCertificate.serialNumber);
|
|
41
|
-
console.log(" subject : ");
|
|
42
|
-
for (const [k, v] of Object.entries(i.tbsCertificate.subject)) {
|
|
43
|
-
console.log(` ${k.padEnd(18)}: ${v}`);
|
|
44
|
-
}
|
|
45
|
-
console.log(" notBefore : ", i.tbsCertificate.validity.notBefore);
|
|
46
|
-
console.log(" notAfter : ", i.tbsCertificate.validity.notAfter);
|
|
47
|
-
console.log(" issuer : ");
|
|
48
|
-
for (const [k, v] of Object.entries(i.tbsCertificate.issuer)) {
|
|
49
|
-
console.log(` ${k.padEnd(18)}: ${v}`);
|
|
50
|
-
}
|
|
51
|
-
console.log(" signature algo : ", i.signatureAlgorithm);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function dumpTrustedList(trustList: TrustListDataType) {
|
|
55
|
-
if (trustList.trustedCertificates && trustList.trustedCertificates.length) {
|
|
56
|
-
console.log("number of trusted certificates", trustList.trustedCertificates.length);
|
|
57
|
-
for (const certificate of trustList.trustedCertificates) {
|
|
58
|
-
dumpCertificateInfo(certificate);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (trustList.issuerCertificates && trustList.issuerCertificates.length) {
|
|
62
|
-
console.log("number of issuers certificates", trustList.issuerCertificates.length);
|
|
63
|
-
for (const certificate of trustList.issuerCertificates) {
|
|
64
|
-
dumpCertificateInfo(certificate);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
async function dumpApplicationTrustedCertificates(session: IBasicSession) {
|
|
69
|
-
const s = new ClientPushCertificateManagement(session);
|
|
70
|
-
const applicationGroup = await s.getCertificateGroup("DefaultApplicationGroup");
|
|
71
|
-
|
|
72
|
-
const trustList = await applicationGroup.getTrustList();
|
|
73
|
-
const tl = await trustList.readTrustedCertificateList();
|
|
74
|
-
dumpTrustedList(tl);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async function dumpUserTokenTrustedCertificates(session: IBasicSession) {
|
|
78
|
-
const s = new ClientPushCertificateManagement(session);
|
|
79
|
-
const applicationGroup = await s.getCertificateGroup("DefaultUserTokenGroup");
|
|
80
|
-
|
|
81
|
-
const trustList = await applicationGroup.getTrustList();
|
|
82
|
-
const tl = await trustList.readTrustedCertificateList();
|
|
83
|
-
dumpTrustedList(tl);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async function getRejectedList(session: IBasicSession) {
|
|
87
|
-
const s = new ClientPushCertificateManagement(session);
|
|
88
|
-
const rejectedList = await s.getRejectedList();
|
|
89
|
-
if (rejectedList.statusCode.isNotGood()) {
|
|
90
|
-
throw new Error(rejectedList.statusCode.name);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
console.log("number of rejected certificates ", rejectedList.certificates?.length);
|
|
94
|
-
for (const certificate of rejectedList.certificates || []) {
|
|
95
|
-
dumpCertificateInfo(certificate);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async function dumpServerConfiguration(session: IBasicSession) {
|
|
100
|
-
const capabilitiesDataValue = await session.read({
|
|
101
|
-
nodeId: resolveNodeId("ServerConfiguration_ServerCapabilities"),
|
|
102
|
-
attributeId: AttributeIds.Value
|
|
103
|
-
});
|
|
104
|
-
const capabilities = capabilitiesDataValue.value.value as string[];
|
|
105
|
-
console.log(" server capabilities : ", capabilities.join(","));
|
|
106
|
-
|
|
107
|
-
const multicastDnsEnabledDataValue = await session.read({
|
|
108
|
-
nodeId: resolveNodeId("ServerConfiguration_MulticastDnsEnabled"),
|
|
109
|
-
attributeId: AttributeIds.Value
|
|
110
|
-
});
|
|
111
|
-
const multicastDnsEnabled = multicastDnsEnabledDataValue.value.value as boolean;
|
|
112
|
-
console.log(" multicastDns enabled : ", multicastDnsEnabled);
|
|
113
|
-
|
|
114
|
-
const maxTrustListSizeDataValue = await session.read({
|
|
115
|
-
nodeId: resolveNodeId("ServerConfiguration_MaxTrustListSize"),
|
|
116
|
-
attributeId: AttributeIds.Value
|
|
117
|
-
});
|
|
118
|
-
const maxTrustListSize = maxTrustListSizeDataValue.value.value as number;
|
|
119
|
-
console.log(" max trust list size : ", maxTrustListSize);
|
|
120
|
-
|
|
121
|
-
const supportedPrivateKeyFormatsDataValue = await session.read({
|
|
122
|
-
nodeId: resolveNodeId("ServerConfiguration_SupportedPrivateKeyFormats"),
|
|
123
|
-
attributeId: AttributeIds.Value
|
|
124
|
-
});
|
|
125
|
-
const supportedPrivateKeyFormats = supportedPrivateKeyFormatsDataValue.value.value as string[];
|
|
126
|
-
console.log(" key format : ", supportedPrivateKeyFormats.join(","));
|
|
127
|
-
//
|
|
128
|
-
await dumpApplicationTrustedCertificates(session);
|
|
129
|
-
//
|
|
130
|
-
await dumpUserTokenTrustedCertificates(session);
|
|
131
|
-
//
|
|
132
|
-
await getRejectedList(session);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async function addApplicationCertificate(session: IBasicSession) {
|
|
136
|
-
const s = new ClientPushCertificateManagement(session);
|
|
137
|
-
const ag = await s.getApplicationGroup();
|
|
138
|
-
const trustList = await ag.getTrustList();
|
|
139
|
-
|
|
140
|
-
const certificateFile = path.join(__dirname, "../../node-opcua-samples/certificates/client_cert_2048.pem");
|
|
141
|
-
|
|
142
|
-
const certificate = await readCertificate(certificateFile);
|
|
143
|
-
await trustList.addCertificate(certificate, true);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
async function addApplicationIssuerCertificateAndCRL(session: IBasicSession) {
|
|
147
|
-
/**
|
|
148
|
-
*/
|
|
149
|
-
}
|
|
150
|
-
async function replaceServerCertificate(session: IBasicSession, caAuthority: CertificateAuthority) {
|
|
151
|
-
const certificateAutorityhPath = path.join(caAuthority.location);
|
|
152
|
-
const caCertificate = await readCertificate(caAuthority.caCertificate);
|
|
153
|
-
// also get crl
|
|
154
|
-
const crl = await readCertificateRevocationList(caAuthority.revocationList);
|
|
155
|
-
|
|
156
|
-
// get signing request
|
|
157
|
-
const s = new ClientPushCertificateManagement(session);
|
|
158
|
-
const ag = await s.getApplicationGroup();
|
|
159
|
-
const csr = await s.createSigningRequest("DefaultApplicationGroup", CertificateType.RsaSha256Application, "CN=toto", false);
|
|
160
|
-
if (csr.statusCode.isNotGood()) {
|
|
161
|
-
console.log("Signing Request = ", csr.statusCode.toString());
|
|
162
|
-
throw new Error(csr.statusCode.name);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
console.log(csr.certificateSigningRequest?.toString("base64"));
|
|
166
|
-
|
|
167
|
-
const certificateFile = path.join(certificateAutorityhPath, "demo.pem");
|
|
168
|
-
const csrFilename = path.join(certificateAutorityhPath, "csr.pem");
|
|
169
|
-
fs.writeFileSync(csrFilename, toPem(csr.certificateSigningRequest!, "CERTIFICATE REQUEST"));
|
|
170
|
-
|
|
171
|
-
const certificateRequestInfo = exploreCertificateSigningRequest(csr.certificateSigningRequest!);
|
|
172
|
-
console.log(util.inspect(certificateRequestInfo, { depth: 10 }));
|
|
173
|
-
|
|
174
|
-
const applicationUri = certificateRequestInfo.extensionRequest.subjectAltName.uniformResourceIdentifier[0];
|
|
175
|
-
console.log("applicationUri = ", applicationUri);
|
|
176
|
-
await caAuthority.signCertificateRequest(certificateFile, csrFilename, {
|
|
177
|
-
startDate: new Date(),
|
|
178
|
-
endDate: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10),
|
|
179
|
-
reason: "Signature by me",
|
|
180
|
-
applicationUri
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const certificateChain = readCertificate(certificateFile);
|
|
184
|
-
dumpCertificateInfo(certificateChain);
|
|
185
|
-
|
|
186
|
-
// now publish certificate
|
|
187
|
-
|
|
188
|
-
const certificates = split_der(certificateChain);
|
|
189
|
-
|
|
190
|
-
const a = await s.getApplicationGroup();
|
|
191
|
-
const tl = await a.getTrustList();
|
|
192
|
-
tl.addCertificate(caCertificate, false);
|
|
193
|
-
|
|
194
|
-
const result = await s.updateCertificate("DefaultApplicationGroup", CertificateType.RsaSha256Application, certificates[0], [
|
|
195
|
-
certificates[1],
|
|
196
|
-
crl
|
|
197
|
-
]);
|
|
198
|
-
if (result.statusCode.isNotGood()) {
|
|
199
|
-
throw new Error("updateCertificate failed " + csr.statusCode.name);
|
|
200
|
-
}
|
|
201
|
-
if (result.applyChangesRequired) {
|
|
202
|
-
console.log("Applying changes");
|
|
203
|
-
const statusCode = await s.applyChanges();
|
|
204
|
-
if (statusCode.isNotGood()) {
|
|
205
|
-
throw new Error("applyChanged failed " + csr.statusCode.name);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
(async () => {
|
|
210
|
-
try {
|
|
211
|
-
const configFolder = path.join(__dirname, "../temp/aa");
|
|
212
|
-
if (!fs.existsSync(configFolder)) {
|
|
213
|
-
fs.mkdirSync(configFolder);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// --------------------------------------------------------------- CA Authority
|
|
217
|
-
const certificateAutorityhPath = path.join(configFolder, "CA");
|
|
218
|
-
const caAuthority = new CertificateAuthority({
|
|
219
|
-
location: certificateAutorityhPath,
|
|
220
|
-
keySize: 4096
|
|
221
|
-
});
|
|
222
|
-
await caAuthority.initialize();
|
|
223
|
-
await caAuthority.constructCACertificateWithCRL();
|
|
224
|
-
const caCertificate = await readCertificate(caAuthority.caCertificate);
|
|
225
|
-
const crl = await readCertificateRevocationList(caAuthority.revocationList);
|
|
226
|
-
|
|
227
|
-
// --------------------------------------------------------------- Client PKI
|
|
228
|
-
const hostname = os.hostname();
|
|
229
|
-
const applicationUri = makeApplicationUrn(hostname, "Client");
|
|
230
|
-
|
|
231
|
-
const pkiPath = path.join(configFolder, "PKI");
|
|
232
|
-
const clientCertificateManager = new OPCUACertificateManager({
|
|
233
|
-
automaticallyAcceptUnknownCertificate: true,
|
|
234
|
-
rootFolder: pkiPath,
|
|
235
|
-
keySize: 4096
|
|
236
|
-
});
|
|
237
|
-
await clientCertificateManager.initialize();
|
|
238
|
-
const certificateFile = path.join(clientCertificateManager.rootDir, "own/certs/client_certificate.pem");
|
|
239
|
-
if (!fs.existsSync(certificateFile)) {
|
|
240
|
-
await clientCertificateManager.createSelfSignedCertificate({
|
|
241
|
-
applicationUri,
|
|
242
|
-
startDate: new Date(),
|
|
243
|
-
endDate: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
244
|
-
validity: 365,
|
|
245
|
-
subject: "CN=Sterfive",
|
|
246
|
-
dns: [hostname],
|
|
247
|
-
outputFile: certificateFile
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// ------------------------------------------------------------------- Add CA Certificate to Client PKI
|
|
252
|
-
console.log(caCertificate.toString("base64"));
|
|
253
|
-
await clientCertificateManager.addIssuer(caCertificate, false, true);
|
|
254
|
-
await clientCertificateManager.addRevocationList(crl);
|
|
255
|
-
|
|
256
|
-
const client = OPCUAClient.create({
|
|
257
|
-
applicationUri,
|
|
258
|
-
endpointMustExist: false,
|
|
259
|
-
connectionStrategy: {
|
|
260
|
-
maxRetry: 1
|
|
261
|
-
},
|
|
262
|
-
securityMode: MessageSecurityMode.SignAndEncrypt,
|
|
263
|
-
securityPolicy: SecurityPolicy.Basic256Sha256,
|
|
264
|
-
|
|
265
|
-
clientCertificateManager,
|
|
266
|
-
certificateFile
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
await client.withSessionAsync(
|
|
270
|
-
{
|
|
271
|
-
endpointUrl,
|
|
272
|
-
userIdentity: { type: UserTokenType.UserName, userName: "root", password: "secret" }
|
|
273
|
-
},
|
|
274
|
-
async (session) => {
|
|
275
|
-
try {
|
|
276
|
-
await dumpServerConfiguration(session);
|
|
277
|
-
|
|
278
|
-
// add application certificate
|
|
279
|
-
|
|
280
|
-
await addApplicationCertificate(session);
|
|
281
|
-
|
|
282
|
-
await dumpServerConfiguration(session);
|
|
283
|
-
|
|
284
|
-
// add issuer certificate + crl
|
|
285
|
-
await addApplicationIssuerCertificateAndCRL(session);
|
|
286
|
-
|
|
287
|
-
// await replaceServerCertificate(session, caAuthority);
|
|
288
|
-
|
|
289
|
-
console.log("done");
|
|
290
|
-
} catch (err) {
|
|
291
|
-
if (err instanceof Error) {
|
|
292
|
-
console.log("Error ", err.message);
|
|
293
|
-
}
|
|
294
|
-
console.log(err);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
);
|
|
298
|
-
} catch (err) {
|
|
299
|
-
if (err instanceof Error) {
|
|
300
|
-
console.log("Error ", err.message);
|
|
301
|
-
}
|
|
302
|
-
console.log(err);
|
|
303
|
-
}
|
|
304
|
-
})();
|