@certd/plugin-cert 1.24.4 → 1.25.2

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,4 +0,0 @@
1
- export declare class EabAccess {
2
- kid: string;
3
- hmacKey: string;
4
- }
@@ -1 +0,0 @@
1
- export * from "./eab-access";
@@ -1,27 +0,0 @@
1
- import { HttpClient, IAccess, ILogger, Registrable } from "@certd/pipeline";
2
- export type DnsProviderDefine = Registrable & {
3
- accessType: string;
4
- autowire?: {
5
- [key: string]: any;
6
- };
7
- };
8
- export type CreateRecordOptions = {
9
- fullRecord: string;
10
- type: string;
11
- value: any;
12
- domain: string;
13
- };
14
- export type RemoveRecordOptions<T> = CreateRecordOptions & {
15
- record: T;
16
- };
17
- export type DnsProviderContext = {
18
- access: IAccess;
19
- logger: ILogger;
20
- http: HttpClient;
21
- };
22
- export interface IDnsProvider<T = any> {
23
- onInstance(): Promise<void>;
24
- createRecord(options: CreateRecordOptions): Promise<T>;
25
- removeRecord(options: RemoveRecordOptions<T>): Promise<void>;
26
- setCtx(ctx: DnsProviderContext): void;
27
- }
@@ -1,8 +0,0 @@
1
- import { CreateRecordOptions, DnsProviderContext, IDnsProvider, RemoveRecordOptions } from "./api";
2
- export declare abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
3
- ctx: DnsProviderContext;
4
- setCtx(ctx: DnsProviderContext): void;
5
- abstract createRecord(options: CreateRecordOptions): Promise<T>;
6
- abstract onInstance(): Promise<void>;
7
- abstract removeRecord(options: RemoveRecordOptions<T>): Promise<void>;
8
- }
@@ -1,3 +0,0 @@
1
- import { DnsProviderDefine } from "./api";
2
- export declare const DNS_PROVIDER_CLASS_KEY = "pipeline:dns-provider";
3
- export declare function IsDnsProvider(define: DnsProviderDefine): ClassDecorator;
@@ -1,4 +0,0 @@
1
- export * from "./api";
2
- export * from "./registry";
3
- export * from "./decorator";
4
- export * from "./base";
@@ -1,2 +0,0 @@
1
- import { Registry } from "@certd/pipeline";
2
- export declare const dnsProviderRegistry: Registry<unknown>;
package/dist/d/index.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export * from "./plugin";
2
- export * from "./dns-provider";
3
- export * from "./access";
@@ -1,54 +0,0 @@
1
- import * as acme from "@certd/acme-client";
2
- import { Logger } from "log4js";
3
- import { IContext } from "@certd/pipeline";
4
- import { IDnsProvider } from "../../dns-provider";
5
- import { ClientExternalAccountBindingOptions } from "@certd/acme-client";
6
- export type CertInfo = {
7
- crt: string;
8
- key: string;
9
- csr: string;
10
- };
11
- export type SSLProvider = "letsencrypt" | "buypass" | "zerossl";
12
- export declare class AcmeService {
13
- userContext: IContext;
14
- logger: Logger;
15
- sslProvider: SSLProvider;
16
- skipLocalVerify: boolean;
17
- eab?: ClientExternalAccountBindingOptions;
18
- constructor(options: {
19
- userContext: IContext;
20
- logger: Logger;
21
- sslProvider: SSLProvider;
22
- eab?: ClientExternalAccountBindingOptions;
23
- skipLocalVerify?: boolean;
24
- });
25
- getAccountConfig(email: string): Promise<any>;
26
- buildAccountKey(email: string): string;
27
- saveAccountConfig(email: string, conf: any): Promise<void>;
28
- getAcmeClient(email: string, isTest?: boolean): Promise<acme.Client>;
29
- createNewKey(): Promise<string>;
30
- parseDomain(fullDomain: string): string;
31
- challengeCreateFn(authz: any, challenge: any, keyAuthorization: string, dnsProvider: IDnsProvider): Promise<any>;
32
- /**
33
- * Function used to remove an ACME challenge response
34
- *
35
- * @param {object} authz Authorization object
36
- * @param {object} challenge Selected challenge
37
- * @param {string} keyAuthorization Authorization key
38
- * @param recordItem challengeCreateFn create record item
39
- * @param dnsProvider dnsProvider
40
- * @returns {Promise}
41
- */
42
- challengeRemoveFn(authz: any, challenge: any, keyAuthorization: string, recordItem: any, dnsProvider: IDnsProvider): Promise<void>;
43
- order(options: {
44
- email: string;
45
- domains: string | string[];
46
- dnsProvider: any;
47
- csrInfo: any;
48
- isTest?: boolean;
49
- }): Promise<CertInfo>;
50
- buildCommonNameByDomains(domains: string | string[]): {
51
- commonName: string;
52
- altNames: string[] | undefined;
53
- };
54
- }
@@ -1,16 +0,0 @@
1
- import { CertInfo } from "./acme";
2
- import forge from "node-forge";
3
- export declare class CertReader implements CertInfo {
4
- crt: string;
5
- key: string;
6
- csr: string;
7
- detail: any;
8
- expires: number;
9
- constructor(certInfo: CertInfo);
10
- toCertInfo(): CertInfo;
11
- getCrtDetail(crt: string): {
12
- detail: forge.pki.Certificate;
13
- expires: Date;
14
- };
15
- saveToFile(type: "crt" | "key", filepath?: string): string;
16
- }
@@ -1,52 +0,0 @@
1
- import { AbstractTaskPlugin, HttpClient, IAccessService, IContext, Step } from "@certd/pipeline";
2
- import { AcmeService, CertInfo, SSLProvider } from "./acme";
3
- import { Logger } from "log4js";
4
- import { CertReader } from "./cert-reader";
5
- export { CertReader };
6
- export type { CertInfo };
7
- export declare class CertApplyPlugin extends AbstractTaskPlugin {
8
- domains: string;
9
- email: string;
10
- sslProvider: SSLProvider;
11
- eabAccessId: number;
12
- dnsProviderType: string;
13
- dnsProviderAccess: string;
14
- skipLocalVerify: boolean;
15
- renewDays: number;
16
- forceUpdate: string;
17
- csrInfo: string;
18
- intro: string;
19
- acme: AcmeService;
20
- logger: Logger;
21
- userContext: IContext;
22
- accessService: IAccessService;
23
- http: HttpClient;
24
- lastStatus: Step;
25
- cert?: CertInfo;
26
- onInstance(): Promise<void>;
27
- execute(): Promise<void>;
28
- output(certReader: CertReader): Promise<void>;
29
- zipCert(cert: CertInfo, applyTime: string): Promise<void>;
30
- /**
31
- * 是否更新证书
32
- */
33
- condition(): Promise<CertReader | null>;
34
- doCertApply(): Promise<CertReader>;
35
- formatCert(pem: string): string;
36
- formatCerts(cert: {
37
- crt: string;
38
- key: string;
39
- csr: string;
40
- }): CertInfo;
41
- readLastCert(): Promise<CertReader | undefined>;
42
- /**
43
- * 检查是否过期,默认提前20天
44
- * @param expires
45
- * @param maxDays
46
- * @returns {boolean}
47
- */
48
- isWillExpire(expires: number, maxDays?: number): {
49
- isWillExpire: boolean;
50
- leftDays: number;
51
- };
52
- }
@@ -1 +0,0 @@
1
- export * from "./cert-plugin";
@@ -1,16 +0,0 @@
1
- import type { CertInfo } from "./acme.js";
2
- import { CertReader } from "./cert-reader.js";
3
- import { CertApplyBasePlugin } from "./base.js";
4
- import { EabAccess } from "../../access";
5
- export { CertReader };
6
- export type { CertInfo };
7
- export declare class CertApplyLegoPlugin extends CertApplyBasePlugin {
8
- dnsType: string;
9
- environment: string;
10
- eabAccessId: number;
11
- customArgs: string;
12
- eab?: EabAccess;
13
- onInstance(): Promise<void>;
14
- onInit(): Promise<void>;
15
- doCertApply(): Promise<CertReader>;
16
- }
@@ -1,145 +0,0 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
7
- var __metadata = (this && this.__metadata) || function (k, v) {
8
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
- };
10
- import { IsTaskPlugin, RunStrategy, sp, TaskInput } from "@certd/pipeline";
11
- import { CertReader } from "./cert-reader.js";
12
- import { CertApplyBasePlugin } from "./base.js";
13
- import fs from "fs";
14
- import path from "path";
15
- export { CertReader };
16
- let CertApplyLegoPlugin = class CertApplyLegoPlugin extends CertApplyBasePlugin {
17
- dnsType;
18
- environment;
19
- eabAccessId;
20
- customArgs = "";
21
- eab;
22
- async onInstance() {
23
- this.accessService = this.ctx.accessService;
24
- this.logger = this.ctx.logger;
25
- this.userContext = this.ctx.userContext;
26
- this.http = this.ctx.http;
27
- this.lastStatus = this.ctx.lastStatus;
28
- if (this.eabAccessId) {
29
- this.eab = await this.ctx.accessService.getById(this.eabAccessId);
30
- }
31
- }
32
- async onInit() { }
33
- async doCertApply() {
34
- const env = {};
35
- const env_lines = this.environment.split("\n");
36
- for (const line of env_lines) {
37
- const [key, value] = line.trim().split("=");
38
- env[key] = value.trim();
39
- }
40
- let domainArgs = "";
41
- for (const domain of this.domains) {
42
- domainArgs += ` -d "${domain}"`;
43
- }
44
- this.logger.info(`环境变量:${JSON.stringify(env)}`);
45
- let eabArgs = "";
46
- if (this.eab) {
47
- eabArgs = ` --eab "${this.eab.kid}" --kid "${this.eab.kid}" --hmac "${this.eab.hmacKey}"`;
48
- }
49
- const keyType = "-k rsa2048";
50
- const saveDir = `./data/.lego/pipeline_${this.pipeline.id}/`;
51
- const savePathArgs = `--path "${saveDir}"`;
52
- const os_type = process.platform === "win32" ? "windows" : "linux";
53
- const legoPath = path.resolve("./tools", os_type, "lego");
54
- const cmds = [
55
- `${legoPath} -a --email "${this.email}" --dns ${this.dnsType} ${keyType} ${domainArgs} ${eabArgs} ${savePathArgs} ${this.customArgs || ""} run`,
56
- ];
57
- await sp.spawn({
58
- cmd: cmds,
59
- logger: this.logger,
60
- env,
61
- });
62
- //读取证书文件
63
- // example.com.crt
64
- // example.com.issuer.crt
65
- // example.com.json
66
- // example.com.key
67
- let domain1 = this.domains[0];
68
- domain1 = domain1.replaceAll("*", "_");
69
- const crtPath = path.resolve(saveDir, "certificates", `${domain1}.crt`);
70
- if (fs.existsSync(crtPath) === false) {
71
- throw new Error(`证书文件不存在,证书申请失败:${crtPath}`);
72
- }
73
- const crt = fs.readFileSync(crtPath, "utf8");
74
- const keyPath = path.resolve(saveDir, "certificates", `${domain1}.key`);
75
- const key = fs.readFileSync(keyPath, "utf8");
76
- const csr = "";
77
- const cert = { crt, key, csr };
78
- const certInfo = this.formatCerts(cert);
79
- return new CertReader(certInfo);
80
- }
81
- };
82
- __decorate([
83
- TaskInput({
84
- title: "DNS类型",
85
- component: {
86
- name: "a-input",
87
- vModel: "value",
88
- },
89
- required: true,
90
- }),
91
- __metadata("design:type", String)
92
- ], CertApplyLegoPlugin.prototype, "dnsType", void 0);
93
- __decorate([
94
- TaskInput({
95
- title: "环境变量",
96
- component: {
97
- name: "a-textarea",
98
- vModel: "value",
99
- rows: 6,
100
- },
101
- required: true,
102
- helper: "一行一条,例如 appKeyId=xxxxx",
103
- }),
104
- __metadata("design:type", String)
105
- ], CertApplyLegoPlugin.prototype, "environment", void 0);
106
- __decorate([
107
- TaskInput({
108
- title: "EAB授权",
109
- component: {
110
- name: "pi-access-selector",
111
- type: "eab",
112
- },
113
- helper: "如果需要提供EAB授权",
114
- }),
115
- __metadata("design:type", Number)
116
- ], CertApplyLegoPlugin.prototype, "eabAccessId", void 0);
117
- __decorate([
118
- TaskInput({
119
- title: "自定义LEGO参数",
120
- component: {
121
- name: "a-input",
122
- vModel: "value",
123
- },
124
- }),
125
- __metadata("design:type", Object)
126
- ], CertApplyLegoPlugin.prototype, "customArgs", void 0);
127
- CertApplyLegoPlugin = __decorate([
128
- IsTaskPlugin({
129
- name: "CertApplyLego",
130
- title: "证书申请(Lego)",
131
- desc: "支持海量DNS解析提供商,推荐使用",
132
- default: {
133
- input: {
134
- renewDays: 20,
135
- forceUpdate: false,
136
- },
137
- strategy: {
138
- runStrategy: RunStrategy.AlwaysRun,
139
- },
140
- },
141
- })
142
- ], CertApplyLegoPlugin);
143
- export { CertApplyLegoPlugin };
144
- new CertApplyLegoPlugin();
145
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGVnby5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wbHVnaW4vY2VydC1wbHVnaW4vbGVnby50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7QUFBQSxPQUFPLEVBQUUsWUFBWSxFQUFFLFdBQVcsRUFBRSxFQUFFLEVBQVEsU0FBUyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFakYsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNoRCxPQUFPLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFFcEIsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBRXhCLE9BQU8sRUFBRSxVQUFVLEVBQUUsQ0FBQztBQWlCZixJQUFNLG1CQUFtQixHQUF6QixNQUFNLG1CQUFvQixTQUFRLG1CQUFtQjtJQVMxRCxPQUFPLENBQVU7SUFZakIsV0FBVyxDQUFVO0lBVXJCLFdBQVcsQ0FBVTtJQVNyQixVQUFVLEdBQUcsRUFBRSxDQUFDO0lBRWhCLEdBQUcsQ0FBYTtJQUVoQixLQUFLLENBQUMsVUFBVTtRQUNkLElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUM7UUFDNUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQztRQUM5QixJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDO1FBQ3hDLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUM7UUFDMUIsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLFVBQWtCLENBQUM7UUFDOUMsSUFBSSxJQUFJLENBQUMsV0FBVyxFQUFFO1lBQ3BCLElBQUksQ0FBQyxHQUFHLEdBQUcsTUFBTSxJQUFJLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1NBQ25FO0lBQ0gsQ0FBQztJQUNELEtBQUssQ0FBQyxNQUFNLEtBQW1CLENBQUM7SUFFaEMsS0FBSyxDQUFDLFdBQVc7UUFDZixNQUFNLEdBQUcsR0FBUSxFQUFFLENBQUM7UUFDcEIsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDL0MsS0FBSyxNQUFNLElBQUksSUFBSSxTQUFTLEVBQUU7WUFDNUIsTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQzVDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7U0FDekI7UUFFRCxJQUFJLFVBQVUsR0FBRyxFQUFFLENBQUM7UUFDcEIsS0FBSyxNQUFNLE1BQU0sSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFO1lBQ2pDLFVBQVUsSUFBSSxRQUFRLE1BQU0sR0FBRyxDQUFDO1NBQ2pDO1FBQ0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNoRCxJQUFJLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDakIsSUFBSSxJQUFJLENBQUMsR0FBRyxFQUFFO1lBQ1osT0FBTyxHQUFHLFdBQVcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLFlBQVksSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLGFBQWEsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEdBQUcsQ0FBQztTQUMzRjtRQUNELE1BQU0sT0FBTyxHQUFHLFlBQVksQ0FBQztRQUU3QixNQUFNLE9BQU8sR0FBRyx5QkFBeUIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLEdBQUcsQ0FBQztRQUM3RCxNQUFNLFlBQVksR0FBRyxXQUFXLE9BQU8sR0FBRyxDQUFDO1FBQzNDLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxRQUFRLEtBQUssT0FBTyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUNuRSxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRSxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDMUQsTUFBTSxJQUFJLEdBQUc7WUFDWCxHQUFHLFFBQVEsZ0JBQWdCLElBQUksQ0FBQyxLQUFLLFdBQVcsSUFBSSxDQUFDLE9BQU8sSUFBSSxPQUFPLElBQUksVUFBVSxJQUFJLE9BQU8sSUFBSSxZQUFZLEtBQUssSUFBSSxDQUFDLFVBQVUsSUFBSSxFQUFFLE1BQU07U0FDakosQ0FBQztRQUVGLE1BQU0sRUFBRSxDQUFDLEtBQUssQ0FBQztZQUNiLEdBQUcsRUFBRSxJQUFJO1lBQ1QsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNO1lBQ25CLEdBQUc7U0FDSixDQUFDLENBQUM7UUFFSCxRQUFRO1FBQ1Isa0JBQWtCO1FBQ2xCLHlCQUF5QjtRQUN6QixtQkFBbUI7UUFDbkIsa0JBQWtCO1FBRWxCLElBQUksT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDOUIsT0FBTyxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQ3ZDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLGNBQWMsRUFBRSxHQUFHLE9BQU8sTUFBTSxDQUFDLENBQUM7UUFDeEUsSUFBSSxFQUFFLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEtBQUssRUFBRTtZQUNwQyxNQUFNLElBQUksS0FBSyxDQUFDLGtCQUFrQixPQUFPLEVBQUUsQ0FBQyxDQUFDO1NBQzlDO1FBQ0QsTUFBTSxHQUFHLEdBQUcsRUFBRSxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDN0MsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsY0FBYyxFQUFFLEdBQUcsT0FBTyxNQUFNLENBQUMsQ0FBQztRQUN4RSxNQUFNLEdBQUcsR0FBRyxFQUFFLENBQUMsWUFBWSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztRQUM3QyxNQUFNLEdBQUcsR0FBRyxFQUFFLENBQUM7UUFDZixNQUFNLElBQUksR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7UUFDL0IsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN4QyxPQUFPLElBQUksVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ2xDLENBQUM7Q0FDRixDQUFBO0FBNUdDO0lBQUMsU0FBUyxDQUFDO1FBQ1QsS0FBSyxFQUFFLE9BQU87UUFDZCxTQUFTLEVBQUU7WUFDVCxJQUFJLEVBQUUsU0FBUztZQUNmLE1BQU0sRUFBRSxPQUFPO1NBQ2hCO1FBQ0QsUUFBUSxFQUFFLElBQUk7S0FDZixDQUFDOztvREFDZTtBQUVqQjtJQUFDLFNBQVMsQ0FBQztRQUNULEtBQUssRUFBRSxNQUFNO1FBQ2IsU0FBUyxFQUFFO1lBQ1QsSUFBSSxFQUFFLFlBQVk7WUFDbEIsTUFBTSxFQUFFLE9BQU87WUFDZixJQUFJLEVBQUUsQ0FBQztTQUNSO1FBQ0QsUUFBUSxFQUFFLElBQUk7UUFDZCxNQUFNLEVBQUUsd0JBQXdCO0tBQ2pDLENBQUM7O3dEQUNtQjtBQUVyQjtJQUFDLFNBQVMsQ0FBQztRQUNULEtBQUssRUFBRSxPQUFPO1FBQ2QsU0FBUyxFQUFFO1lBQ1QsSUFBSSxFQUFFLG9CQUFvQjtZQUMxQixJQUFJLEVBQUUsS0FBSztTQUNaO1FBQ0QsTUFBTSxFQUFFLGFBQWE7S0FDdEIsQ0FBQzs7d0RBQ21CO0FBRXJCO0lBQUMsU0FBUyxDQUFDO1FBQ1QsS0FBSyxFQUFFLFdBQVc7UUFDbEIsU0FBUyxFQUFFO1lBQ1QsSUFBSSxFQUFFLFNBQVM7WUFDZixNQUFNLEVBQUUsT0FBTztTQUNoQjtLQUNGLENBQUM7O3VEQUNjO0FBeENMLG1CQUFtQjtJQWQvQixZQUFZLENBQUM7UUFDWixJQUFJLEVBQUUsZUFBZTtRQUNyQixLQUFLLEVBQUUsWUFBWTtRQUNuQixJQUFJLEVBQUUsbUJBQW1CO1FBQ3pCLE9BQU8sRUFBRTtZQUNQLEtBQUssRUFBRTtnQkFDTCxTQUFTLEVBQUUsRUFBRTtnQkFDYixXQUFXLEVBQUUsS0FBSzthQUNuQjtZQUNELFFBQVEsRUFBRTtnQkFDUixXQUFXLEVBQUUsV0FBVyxDQUFDLFNBQVM7YUFDbkM7U0FDRjtLQUNGLENBQUM7R0FDVyxtQkFBbUIsQ0E2Ry9CO1NBN0dZLG1CQUFtQjtBQStHaEMsSUFBSSxtQkFBbUIsRUFBRSxDQUFDIn0=