@certd/plugin-cert 1.24.0 → 1.24.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.
@@ -1,242 +1,266 @@
1
- // @ts-ignore
2
- import * as acme from "@certd/acme-client";
3
- import _ from "lodash-es";
4
- import psl from "psl";
5
- export class AcmeService {
6
- options;
7
- userContext;
8
- logger;
9
- sslProvider;
10
- skipLocalVerify = true;
11
- eab;
12
- constructor(options) {
13
- this.options = options;
14
- this.userContext = options.userContext;
15
- this.logger = options.logger;
16
- this.sslProvider = options.sslProvider || "letsencrypt";
17
- this.eab = options.eab;
18
- this.skipLocalVerify = options.skipLocalVerify ?? false;
19
- acme.setLogger((text) => {
20
- this.logger.info(text);
21
- });
22
- }
23
- async getAccountConfig(email, urlMapping) {
24
- const conf = (await this.userContext.getObj(this.buildAccountKey(email))) || {};
25
- if (urlMapping && urlMapping.mappings) {
26
- for (const key in urlMapping.mappings) {
27
- if (Object.prototype.hasOwnProperty.call(urlMapping.mappings, key)) {
28
- const element = urlMapping.mappings[key];
29
- if (conf.accountUrl?.indexOf(element) > -1) {
30
- //如果用了代理url,要替换回去
31
- conf.accountUrl = conf.accountUrl.replace(element, key);
32
- }
33
- }
34
- }
35
- }
36
- return conf;
37
- }
38
- buildAccountKey(email) {
39
- return `acme.config.${this.sslProvider}.${email}`;
40
- }
41
- async saveAccountConfig(email, conf) {
42
- await this.userContext.setObj(this.buildAccountKey(email), conf);
43
- }
44
- async getAcmeClient(email, isTest = false) {
45
- const urlMapping = {
46
- enabled: false,
47
- mappings: {
48
- "acme-v02.api.letsencrypt.org": "letsencrypt.proxy.handsfree.work",
49
- "dv.acme-v02.api.pki.goog": "google.proxy.handsfree.work",
50
- },
51
- };
52
- const conf = await this.getAccountConfig(email, urlMapping);
53
- if (conf.key == null) {
54
- conf.key = await this.createNewKey();
55
- await this.saveAccountConfig(email, conf);
56
- }
57
- let directoryUrl = "";
58
- if (isTest) {
59
- directoryUrl = acme.directory[this.sslProvider].staging;
60
- }
61
- else {
62
- directoryUrl = acme.directory[this.sslProvider].production;
63
- }
64
- if (this.options.useMappingProxy) {
65
- urlMapping.enabled = true;
66
- }
67
- const client = new acme.Client({
68
- directoryUrl: directoryUrl,
69
- accountKey: conf.key,
70
- accountUrl: conf.accountUrl,
71
- externalAccountBinding: this.eab,
72
- backoffAttempts: 15,
73
- backoffMin: 5000,
74
- backoffMax: 10000,
75
- urlMapping,
76
- signal: this.options.signal,
77
- });
78
- if (conf.accountUrl == null) {
79
- const accountPayload = {
80
- termsOfServiceAgreed: true,
81
- contact: [`mailto:${email}`],
82
- externalAccountBinding: this.eab,
83
- };
84
- await client.createAccount(accountPayload);
85
- conf.accountUrl = client.getAccountUrl();
86
- await this.saveAccountConfig(email, conf);
87
- }
88
- return client;
89
- }
90
- async createNewKey() {
91
- const key = await acme.forge.createPrivateKey();
92
- return key.toString();
93
- }
94
- parseDomain(fullDomain) {
95
- const parsed = psl.parse(fullDomain);
96
- if (parsed.error) {
97
- throw new Error(`解析${fullDomain}域名失败:` + JSON.stringify(parsed.error));
98
- }
99
- return parsed.domain;
100
- }
101
- async challengeCreateFn(authz, challenge, keyAuthorization, dnsProvider) {
102
- this.logger.info("Triggered challengeCreateFn()");
103
- /* http-01 */
104
- const fullDomain = authz.identifier.value;
105
- if (challenge.type === "http-01") {
106
- const filePath = `/var/www/html/.well-known/acme-challenge/${challenge.token}`;
107
- const fileContents = keyAuthorization;
108
- this.logger.info(`Creating challenge response for ${fullDomain} at path: ${filePath}`);
109
- /* Replace this */
110
- this.logger.info(`Would write "${fileContents}" to path "${filePath}"`);
111
- // await fs.writeFileAsync(filePath, fileContents);
112
- }
113
- else if (challenge.type === "dns-01") {
114
- /* dns-01 */
115
- const dnsRecord = `_acme-challenge.${fullDomain}`;
116
- const recordValue = keyAuthorization;
117
- this.logger.info(`Creating TXT record for ${fullDomain}: ${dnsRecord}`);
118
- /* Replace this */
119
- this.logger.info(`Would create TXT record "${dnsRecord}" with value "${recordValue}"`);
120
- const domain = this.parseDomain(fullDomain);
121
- this.logger.info("解析到域名domain=", domain);
122
- return await dnsProvider.createRecord({
123
- fullRecord: dnsRecord,
124
- type: "TXT",
125
- value: recordValue,
126
- domain,
127
- });
128
- }
129
- }
130
- /**
131
- * Function used to remove an ACME challenge response
132
- *
133
- * @param {object} authz Authorization object
134
- * @param {object} challenge Selected challenge
135
- * @param {string} keyAuthorization Authorization key
136
- * @param recordItem challengeCreateFn create record item
137
- * @param dnsProvider dnsProvider
138
- * @returns {Promise}
139
- */
140
- async challengeRemoveFn(authz, challenge, keyAuthorization, recordItem, dnsProvider) {
141
- this.logger.info("Triggered challengeRemoveFn()");
142
- /* http-01 */
143
- const fullDomain = authz.identifier.value;
144
- if (challenge.type === "http-01") {
145
- const filePath = `/var/www/html/.well-known/acme-challenge/${challenge.token}`;
146
- this.logger.info(`Removing challenge response for ${fullDomain} at path: ${filePath}`);
147
- /* Replace this */
148
- this.logger.info(`Would remove file on path "${filePath}"`);
149
- // await fs.unlinkAsync(filePath);
150
- }
151
- else if (challenge.type === "dns-01") {
152
- const dnsRecord = `_acme-challenge.${fullDomain}`;
153
- const recordValue = keyAuthorization;
154
- this.logger.info(`Removing TXT record for ${fullDomain}: ${dnsRecord}`);
155
- /* Replace this */
156
- this.logger.info(`Would remove TXT record "${dnsRecord}" with value "${recordValue}"`);
157
- const domain = this.parseDomain(fullDomain);
158
- try {
159
- await dnsProvider.removeRecord({
160
- fullRecord: dnsRecord,
161
- type: "TXT",
162
- value: keyAuthorization,
163
- record: recordItem,
164
- domain,
165
- });
166
- }
167
- catch (e) {
168
- this.logger.error("删除解析记录出错:", e);
169
- throw e;
170
- }
171
- }
172
- }
173
- async order(options) {
174
- const { email, isTest, domains, csrInfo, dnsProvider } = options;
175
- const client = await this.getAcmeClient(email, isTest);
176
- /* Create CSR */
177
- const { commonName, altNames } = this.buildCommonNameByDomains(domains);
178
- let privateKey = null;
179
- const privateKeyType = options.privateKeyType || "rsa_2048";
180
- const privateKeyArr = privateKeyType.split("_");
181
- const type = privateKeyArr[0];
182
- const size = parseInt(privateKeyArr[1]);
183
- if (type == "ec") {
184
- const name = "P-" + size;
185
- privateKey = await acme.crypto.createPrivateEcdsaKey(name);
186
- }
187
- else {
188
- privateKey = await acme.crypto.createPrivateRsaKey(size);
189
- }
190
- const [key, csr] = await acme.crypto.createCsr({
191
- commonName,
192
- ...csrInfo,
193
- altNames,
194
- }, privateKey);
195
- if (dnsProvider == null) {
196
- throw new Error("dnsProvider 不能为空");
197
- }
198
- /* 自动申请证书 */
199
- const crt = await client.auto({
200
- csr,
201
- email: email,
202
- termsOfServiceAgreed: true,
203
- skipChallengeVerification: this.skipLocalVerify,
204
- challengePriority: ["dns-01"],
205
- challengeCreateFn: async (authz, challenge, keyAuthorization) => {
206
- return await this.challengeCreateFn(authz, challenge, keyAuthorization, dnsProvider);
207
- },
208
- challengeRemoveFn: async (authz, challenge, keyAuthorization, recordItem) => {
209
- return await this.challengeRemoveFn(authz, challenge, keyAuthorization, recordItem, dnsProvider);
210
- },
211
- signal: this.options.signal,
212
- });
213
- const cert = {
214
- crt: crt.toString(),
215
- key: key.toString(),
216
- csr: csr.toString(),
217
- };
218
- /* Done */
219
- this.logger.debug(`CSR:\n${cert.csr}`);
220
- this.logger.debug(`Certificate:\n${cert.crt}`);
221
- this.logger.info("证书申请成功");
222
- return cert;
223
- }
224
- buildCommonNameByDomains(domains) {
225
- if (typeof domains === "string") {
226
- domains = domains.split(",");
227
- }
228
- if (domains.length === 0) {
229
- throw new Error("domain can not be empty");
230
- }
231
- const commonName = domains[0];
232
- let altNames = undefined;
233
- if (domains.length > 1) {
234
- altNames = _.slice(domains, 1);
235
- }
236
- return {
237
- commonName,
238
- altNames,
239
- };
240
- }
241
- }
242
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNtZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wbHVnaW4vY2VydC1wbHVnaW4vYWNtZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxhQUFhO0FBQ2IsT0FBTyxLQUFLLElBQUksTUFBTSxvQkFBb0IsQ0FBQztBQUMzQyxPQUFPLENBQUMsTUFBTSxXQUFXLENBQUM7QUFLMUIsT0FBTyxHQUFHLE1BQU0sS0FBSyxDQUFDO0FBcUJ0QixNQUFNLE9BQU8sV0FBVztJQUN0QixPQUFPLENBQXFCO0lBQzVCLFdBQVcsQ0FBVztJQUN0QixNQUFNLENBQVM7SUFDZixXQUFXLENBQWM7SUFDekIsZUFBZSxHQUFHLElBQUksQ0FBQztJQUN2QixHQUFHLENBQXVDO0lBQzFDLFlBQVksT0FBMkI7UUFDckMsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLFdBQVcsR0FBRyxPQUFPLENBQUMsV0FBVyxDQUFDO1FBQ3ZDLElBQUksQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQztRQUM3QixJQUFJLENBQUMsV0FBVyxHQUFHLE9BQU8sQ0FBQyxXQUFXLElBQUksYUFBYSxDQUFDO1FBQ3hELElBQUksQ0FBQyxHQUFHLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQztRQUN2QixJQUFJLENBQUMsZUFBZSxHQUFHLE9BQU8sQ0FBQyxlQUFlLElBQUksS0FBSyxDQUFDO1FBQ3hELElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxJQUFZLEVBQUUsRUFBRTtZQUM5QixJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN6QixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxLQUFLLENBQUMsZ0JBQWdCLENBQUMsS0FBYSxFQUFFLFVBQXNCO1FBQzFELE1BQU0sSUFBSSxHQUFHLENBQUMsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxJQUFJ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
1
+ // @ts-ignore
2
+ import * as acme from "@certd/acme-client";
3
+ import _ from "lodash-es";
4
+ import psl from "psl";
5
+ import { utils } from "@certd/pipeline";
6
+ export class AcmeService {
7
+ options;
8
+ userContext;
9
+ logger;
10
+ sslProvider;
11
+ skipLocalVerify = true;
12
+ eab;
13
+ constructor(options) {
14
+ this.options = options;
15
+ this.userContext = options.userContext;
16
+ this.logger = options.logger;
17
+ this.sslProvider = options.sslProvider || "letsencrypt";
18
+ this.eab = options.eab;
19
+ this.skipLocalVerify = options.skipLocalVerify ?? false;
20
+ acme.setLogger((text) => {
21
+ this.logger.info(text);
22
+ });
23
+ }
24
+ async getAccountConfig(email, urlMapping) {
25
+ const conf = (await this.userContext.getObj(this.buildAccountKey(email))) || {};
26
+ if (urlMapping && urlMapping.mappings) {
27
+ for (const key in urlMapping.mappings) {
28
+ if (Object.prototype.hasOwnProperty.call(urlMapping.mappings, key)) {
29
+ const element = urlMapping.mappings[key];
30
+ if (conf.accountUrl?.indexOf(element) > -1) {
31
+ //如果用了代理url,要替换回去
32
+ conf.accountUrl = conf.accountUrl.replace(element, key);
33
+ }
34
+ }
35
+ }
36
+ }
37
+ return conf;
38
+ }
39
+ buildAccountKey(email) {
40
+ return `acme.config.${this.sslProvider}.${email}`;
41
+ }
42
+ async saveAccountConfig(email, conf) {
43
+ await this.userContext.setObj(this.buildAccountKey(email), conf);
44
+ }
45
+ async getAcmeClient(email, isTest = false) {
46
+ const urlMapping = {
47
+ enabled: false,
48
+ mappings: {
49
+ "acme-v02.api.letsencrypt.org": "letsencrypt.proxy.handsfree.work",
50
+ "dv.acme-v02.api.pki.goog": "google.proxy.handsfree.work",
51
+ },
52
+ };
53
+ const conf = await this.getAccountConfig(email, urlMapping);
54
+ if (conf.key == null) {
55
+ conf.key = await this.createNewKey();
56
+ await this.saveAccountConfig(email, conf);
57
+ }
58
+ let directoryUrl = "";
59
+ if (isTest) {
60
+ directoryUrl = acme.directory[this.sslProvider].staging;
61
+ }
62
+ else {
63
+ directoryUrl = acme.directory[this.sslProvider].production;
64
+ }
65
+ if (this.options.useMappingProxy) {
66
+ urlMapping.enabled = true;
67
+ }
68
+ else {
69
+ //测试directory是否可以访问
70
+ const isOk = await this.testDirectory(directoryUrl);
71
+ if (!isOk) {
72
+ this.logger.info("测试访问失败,自动使用代理");
73
+ urlMapping.enabled = true;
74
+ }
75
+ }
76
+ const client = new acme.Client({
77
+ directoryUrl: directoryUrl,
78
+ accountKey: conf.key,
79
+ accountUrl: conf.accountUrl,
80
+ externalAccountBinding: this.eab,
81
+ backoffAttempts: 15,
82
+ backoffMin: 5000,
83
+ backoffMax: 10000,
84
+ urlMapping,
85
+ signal: this.options.signal,
86
+ });
87
+ if (conf.accountUrl == null) {
88
+ const accountPayload = {
89
+ termsOfServiceAgreed: true,
90
+ contact: [`mailto:${email}`],
91
+ externalAccountBinding: this.eab,
92
+ };
93
+ await client.createAccount(accountPayload);
94
+ conf.accountUrl = client.getAccountUrl();
95
+ await this.saveAccountConfig(email, conf);
96
+ }
97
+ return client;
98
+ }
99
+ async createNewKey() {
100
+ const key = await acme.forge.createPrivateKey();
101
+ return key.toString();
102
+ }
103
+ parseDomain(fullDomain) {
104
+ const parsed = psl.parse(fullDomain);
105
+ if (parsed.error) {
106
+ throw new Error(`解析${fullDomain}域名失败:` + JSON.stringify(parsed.error));
107
+ }
108
+ return parsed.domain;
109
+ }
110
+ async challengeCreateFn(authz, challenge, keyAuthorization, dnsProvider) {
111
+ this.logger.info("Triggered challengeCreateFn()");
112
+ /* http-01 */
113
+ const fullDomain = authz.identifier.value;
114
+ if (challenge.type === "http-01") {
115
+ const filePath = `/var/www/html/.well-known/acme-challenge/${challenge.token}`;
116
+ const fileContents = keyAuthorization;
117
+ this.logger.info(`Creating challenge response for ${fullDomain} at path: ${filePath}`);
118
+ /* Replace this */
119
+ this.logger.info(`Would write "${fileContents}" to path "${filePath}"`);
120
+ // await fs.writeFileAsync(filePath, fileContents);
121
+ }
122
+ else if (challenge.type === "dns-01") {
123
+ /* dns-01 */
124
+ const dnsRecord = `_acme-challenge.${fullDomain}`;
125
+ const recordValue = keyAuthorization;
126
+ this.logger.info(`Creating TXT record for ${fullDomain}: ${dnsRecord}`);
127
+ /* Replace this */
128
+ this.logger.info(`Would create TXT record "${dnsRecord}" with value "${recordValue}"`);
129
+ const domain = this.parseDomain(fullDomain);
130
+ this.logger.info("解析到域名domain=", domain);
131
+ return await dnsProvider.createRecord({
132
+ fullRecord: dnsRecord,
133
+ type: "TXT",
134
+ value: recordValue,
135
+ domain,
136
+ });
137
+ }
138
+ }
139
+ /**
140
+ * Function used to remove an ACME challenge response
141
+ *
142
+ * @param {object} authz Authorization object
143
+ * @param {object} challenge Selected challenge
144
+ * @param {string} keyAuthorization Authorization key
145
+ * @param recordItem challengeCreateFn create record item
146
+ * @param dnsProvider dnsProvider
147
+ * @returns {Promise}
148
+ */
149
+ async challengeRemoveFn(authz, challenge, keyAuthorization, recordItem, dnsProvider) {
150
+ this.logger.info("Triggered challengeRemoveFn()");
151
+ /* http-01 */
152
+ const fullDomain = authz.identifier.value;
153
+ if (challenge.type === "http-01") {
154
+ const filePath = `/var/www/html/.well-known/acme-challenge/${challenge.token}`;
155
+ this.logger.info(`Removing challenge response for ${fullDomain} at path: ${filePath}`);
156
+ /* Replace this */
157
+ this.logger.info(`Would remove file on path "${filePath}"`);
158
+ // await fs.unlinkAsync(filePath);
159
+ }
160
+ else if (challenge.type === "dns-01") {
161
+ const dnsRecord = `_acme-challenge.${fullDomain}`;
162
+ const recordValue = keyAuthorization;
163
+ this.logger.info(`Removing TXT record for ${fullDomain}: ${dnsRecord}`);
164
+ /* Replace this */
165
+ this.logger.info(`Would remove TXT record "${dnsRecord}" with value "${recordValue}"`);
166
+ const domain = this.parseDomain(fullDomain);
167
+ try {
168
+ await dnsProvider.removeRecord({
169
+ fullRecord: dnsRecord,
170
+ type: "TXT",
171
+ value: keyAuthorization,
172
+ record: recordItem,
173
+ domain,
174
+ });
175
+ }
176
+ catch (e) {
177
+ this.logger.error("删除解析记录出错:", e);
178
+ throw e;
179
+ }
180
+ }
181
+ }
182
+ async order(options) {
183
+ const { email, isTest, domains, csrInfo, dnsProvider } = options;
184
+ const client = await this.getAcmeClient(email, isTest);
185
+ /* Create CSR */
186
+ const { commonName, altNames } = this.buildCommonNameByDomains(domains);
187
+ let privateKey = null;
188
+ const privateKeyType = options.privateKeyType || "rsa_2048";
189
+ const privateKeyArr = privateKeyType.split("_");
190
+ const type = privateKeyArr[0];
191
+ const size = parseInt(privateKeyArr[1]);
192
+ if (type == "ec") {
193
+ const name = "P-" + size;
194
+ privateKey = await acme.crypto.createPrivateEcdsaKey(name);
195
+ }
196
+ else {
197
+ privateKey = await acme.crypto.createPrivateRsaKey(size);
198
+ }
199
+ const [key, csr] = await acme.crypto.createCsr({
200
+ commonName,
201
+ ...csrInfo,
202
+ altNames,
203
+ }, privateKey);
204
+ if (dnsProvider == null) {
205
+ throw new Error("dnsProvider 不能为空");
206
+ }
207
+ /* 自动申请证书 */
208
+ const crt = await client.auto({
209
+ csr,
210
+ email: email,
211
+ termsOfServiceAgreed: true,
212
+ skipChallengeVerification: this.skipLocalVerify,
213
+ challengePriority: ["dns-01"],
214
+ challengeCreateFn: async (authz, challenge, keyAuthorization) => {
215
+ return await this.challengeCreateFn(authz, challenge, keyAuthorization, dnsProvider);
216
+ },
217
+ challengeRemoveFn: async (authz, challenge, keyAuthorization, recordItem) => {
218
+ return await this.challengeRemoveFn(authz, challenge, keyAuthorization, recordItem, dnsProvider);
219
+ },
220
+ signal: this.options.signal,
221
+ });
222
+ const cert = {
223
+ crt: crt.toString(),
224
+ key: key.toString(),
225
+ csr: csr.toString(),
226
+ };
227
+ /* Done */
228
+ this.logger.debug(`CSR:\n${cert.csr}`);
229
+ this.logger.debug(`Certificate:\n${cert.crt}`);
230
+ this.logger.info("证书申请成功");
231
+ return cert;
232
+ }
233
+ buildCommonNameByDomains(domains) {
234
+ if (typeof domains === "string") {
235
+ domains = domains.split(",");
236
+ }
237
+ if (domains.length === 0) {
238
+ throw new Error("domain can not be empty");
239
+ }
240
+ const commonName = domains[0];
241
+ let altNames = undefined;
242
+ if (domains.length > 1) {
243
+ altNames = _.slice(domains, 1);
244
+ }
245
+ return {
246
+ commonName,
247
+ altNames,
248
+ };
249
+ }
250
+ async testDirectory(directoryUrl) {
251
+ try {
252
+ await utils.http({
253
+ url: directoryUrl,
254
+ method: "GET",
255
+ timeout: 5000,
256
+ });
257
+ }
258
+ catch (e) {
259
+ this.logger.error(`${directoryUrl},测试访问失败`, e);
260
+ return false;
261
+ }
262
+ this.logger.info(`${directoryUrl},测试访问成功`);
263
+ return true;
264
+ }
265
+ }
266
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNtZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wbHVnaW4vY2VydC1wbHVnaW4vYWNtZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxhQUFhO0FBQ2IsT0FBTyxLQUFLLElBQUksTUFBTSxvQkFBb0IsQ0FBQztBQUMzQyxPQUFPLENBQUMsTUFBTSxXQUFXLENBQUM7QUFLMUIsT0FBTyxHQUFHLE1BQU0sS0FBSyxDQUFDO0FBRXRCLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQW1CeEMsTUFBTSxPQUFPLFdBQVc7SUFDdEIsT0FBTyxDQUFxQjtJQUM1QixXQUFXLENBQVc7SUFDdEIsTUFBTSxDQUFTO0lBQ2YsV0FBVyxDQUFjO0lBQ3pCLGVBQWUsR0FBRyxJQUFJLENBQUM7SUFDdkIsR0FBRyxDQUF1QztJQUMxQyxZQUFZLE9BQTJCO1FBQ3JDLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxXQUFXLEdBQUcsT0FBTyxDQUFDLFdBQVcsQ0FBQztRQUN2QyxJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUM7UUFDN0IsSUFBSSxDQUFDLFdBQVcsR0FBRyxPQUFPLENBQUMsV0FBVyxJQUFJLGFBQWEsQ0FBQztRQUN4RCxJQUFJLENBQUMsR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUM7UUFDdkIsSUFBSSxDQUFDLGVBQWUsR0FBRyxPQUFPLENBQUMsZUFBZSxJQUFJLEtBQUssQ0FBQztRQUN4RCxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsSUFBWSxFQUFFLEVBQUU7WUFDOUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekIsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQsS0FBSyxDQUFDLGdCQUFnQixDQUFDLEtBQWEsRUFBRSxVQUFzQjtRQUMxRCxNQUFNLElBQUksR0FBRyxD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