diff --git a/package-lock.json b/package-lock.json index ab991b5..0b545e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -71,24 +71,23 @@ } }, "@mojoio/cloudflare": { - "version": "3.0.5", - "resolved": "https://verdaccio.lossless.one/@mojoio%2fcloudflare/-/cloudflare-3.0.5.tgz", - "integrity": "sha512-jaJkWQGkc7uJ9L1AbuHXSuskBe8NehRE+N3gekztgWhzuH/AjZHGc0HlAE8qiO8su+tilXkuexbLElSF24W+Gw==", + "version": "4.0.3", + "resolved": "https://verdaccio.lossless.one/@mojoio%2fcloudflare/-/cloudflare-4.0.3.tgz", + "integrity": "sha512-QHjBXzechtg3U1mDqC1aJj8X1PB4eSlIWavFBPlif9F+2TetrTiyfU5LZlP1Z1rRDdTRygTvJXtohsz2BYRFgw==", "dev": true, "requires": { - "@pushrocks/smartdelay": "^2.0.3", - "@pushrocks/smartlog": "^2.0.19", - "@pushrocks/smartpromise": "^3.0.2", - "@pushrocks/smartrequest": "^1.1.16", - "@pushrocks/smartstring": "^3.0.10", - "@tsclass/tsclass": "^2.0.1" + "@pushrocks/smartdelay": "^2.0.6", + "@pushrocks/smartlog": "^2.0.21", + "@pushrocks/smartpromise": "^3.0.6", + "@pushrocks/smartrequest": "^1.1.47", + "@pushrocks/smartstring": "^3.0.18", + "@tsclass/tsclass": "^3.0.4" } }, "@pushrocks/consolecolor": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@pushrocks/consolecolor/-/consolecolor-2.0.1.tgz", "integrity": "sha512-iOFCHVeFZ2OywbdwSxVI4/wokkcLrXVdHLgvMmkNhJ220eeLgjNZWx3EJo3vNW3zq5ybCSCUIq0878djBxrWpw==", - "dev": true, "requires": { "ansi-256-colors": "^1.1.0" } @@ -97,7 +96,6 @@ "version": "3.0.3", "resolved": "https://verdaccio.lossless.one/@pushrocks%2fearly/-/early-3.0.3.tgz", "integrity": "sha512-71/nwxTpqdp1glmHz4YaGusNl/XOOcPelAxC9RA6rpS/6280QyY2u4yx+mRdMrCzn7ruLYF5awbkS8llNZ94Pg==", - "dev": true, "requires": { "@pushrocks/consolecolor": "^2.0.1", "@pushrocks/smartpromise": "^2.0.5" @@ -106,8 +104,7 @@ "@pushrocks/smartpromise": { "version": "2.0.5", "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartpromise/-/smartpromise-2.0.5.tgz", - "integrity": "sha512-9j/chLtIiNkR0MDw7Mpxg9slxAVvAQwUZuiaPYX5KpHdKxQaHLI1VZ8IN0vPhwlfgNO4i4vGXV0wB8BvSDj03g==", - "dev": true + "integrity": "sha512-9j/chLtIiNkR0MDw7Mpxg9slxAVvAQwUZuiaPYX5KpHdKxQaHLI1VZ8IN0vPhwlfgNO4i4vGXV0wB8BvSDj03g==" } } }, @@ -426,7 +423,6 @@ "version": "3.2.0", "resolved": "https://verdaccio.lossless.one/@pushrocks%2ftapbundle/-/tapbundle-3.2.0.tgz", "integrity": "sha512-xnKIGe7NJqxWBaFeKIEXShDjV2wap1XJqmlp3m1MKqmIF62vRpZnkzpyE1ZAoNwiYQmIsXIpvP/PDv+iPevPfw==", - "dev": true, "requires": { "@pushrocks/early": "^3.0.3", "@pushrocks/smartdelay": "^2.0.3", @@ -439,7 +435,6 @@ "version": "2.0.6", "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartdelay/-/smartdelay-2.0.6.tgz", "integrity": "sha512-4wUnzWNhRPODpaaL5GuRaje/C5dg+TMhBxmr57PKc2fqYpy6azWJwonf/s5xpcbJLCPJRbj1x8M5MqgCFq2uvg==", - "dev": true, "requires": { "@pushrocks/smartpromise": "^3.0.6" } @@ -448,7 +443,6 @@ "version": "7.0.6", "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartfile/-/smartfile-7.0.6.tgz", "integrity": "sha512-X1kWg1HSQ/MqasRIHPCf6D9CRrcIrpTjW8KLc4GkApJ/W/IVaKsQkJQfdimlg1uc/9v8AYnoRdiGa51yxyghZA==", - "dev": true, "requires": { "@pushrocks/smarthash": "^2.0.6", "@pushrocks/smartpath": "^4.0.1", @@ -463,14 +457,12 @@ "@pushrocks/smartpromise": { "version": "3.0.6", "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartpromise/-/smartpromise-3.0.6.tgz", - "integrity": "sha512-vlQlBGNVIjfClgnsfgQBU6GIKcskYSFzEcKLt18ngPzPEcjKklXcxaqzLXpnoxR+KBh30QPE8255ncYHXuPPOg==", - "dev": true + "integrity": "sha512-vlQlBGNVIjfClgnsfgQBU6GIKcskYSFzEcKLt18ngPzPEcjKklXcxaqzLXpnoxR+KBh30QPE8255ncYHXuPPOg==" }, "@pushrocks/smartrequest": { "version": "1.1.47", "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartrequest/-/smartrequest-1.1.47.tgz", "integrity": "sha512-0AuqtAI14VeWeXl2WpJbgCybVlG03rOjdGchAqy5k5lg9ACLhN3Z4kmoLgpBysWO/L2SjlAKB489SRyV3acykg==", - "dev": true, "requires": { "@pushrocks/smartpromise": "^3.0.5", "@types/form-data": "^2.5.0", @@ -482,7 +474,6 @@ "version": "2.5.0", "resolved": "https://verdaccio.lossless.one/@types%2fform-data/-/form-data-2.5.0.tgz", "integrity": "sha512-23/wYiuckYYtFpL+4RPWiWmRQH2BjFuqCUi2+N3amB1a1Drv+i/byTrGvlLwRVLFNAZbwpbQ7JvTK+VCAPMbcg==", - "dev": true, "requires": { "form-data": "*" } @@ -491,7 +482,6 @@ "version": "8.0.1", "resolved": "https://verdaccio.lossless.one/@types%2ffs-extra/-/fs-extra-8.0.1.tgz", "integrity": "sha512-J00cVDALmi/hJOYsunyT52Hva5TnJeKP5yd1r+mH/ZU0mbYZflR0Z5kw5kITtKTRYMhm1JMClOFYdHnQszEvqw==", - "dev": true, "requires": { "@types/node": "*" } @@ -500,7 +490,6 @@ "version": "2.5.1", "resolved": "https://verdaccio.lossless.one/form-data/-/form-data-2.5.1.tgz", "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -511,7 +500,6 @@ "version": "8.1.0", "resolved": "https://verdaccio.lossless.one/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -522,7 +510,6 @@ "version": "7.1.6", "resolved": "https://verdaccio.lossless.one/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -535,14 +522,12 @@ "graceful-fs": { "version": "4.2.3", "resolved": "https://verdaccio.lossless.one/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" }, "js-yaml": { "version": "3.13.1", "resolved": "https://verdaccio.lossless.one/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -551,10 +536,9 @@ } }, "@tsclass/tsclass": { - "version": "2.0.13", - "resolved": "https://verdaccio.lossless.one/@tsclass%2ftsclass/-/tsclass-2.0.13.tgz", - "integrity": "sha512-ZhN3vLAahP4DOayACh/bskG4SpeqNpR4oH9776hZvz6GoFj7KQinzf03gzC5o2o6tnmOusQ1FYu1NGYmy6Rvdg==", - "dev": true, + "version": "3.0.4", + "resolved": "https://verdaccio.lossless.one/@tsclass%2ftsclass/-/tsclass-3.0.4.tgz", + "integrity": "sha512-q9PnwBFDTTlwKp6jYn/tL6zhqWvwaKyGsf81xI8xcV6IhjmFo3hk0iM4L3ydw7iTwqR+WmhYaWO5WdMy3/KA+w==", "requires": { "@pushrocks/tapbundle": "^3.0.13" } @@ -579,14 +563,12 @@ "@types/chai": { "version": "4.2.7", "resolved": "https://verdaccio.lossless.one/@types%2fchai/-/chai-4.2.7.tgz", - "integrity": "sha512-luq8meHGYwvky0O7u0eQZdA7B4Wd9owUCqvbw2m3XCrCU8mplYOujMBbvyS547AxJkC+pGnd0Cm15eNxEUNU8g==", - "dev": true + "integrity": "sha512-luq8meHGYwvky0O7u0eQZdA7B4Wd9owUCqvbw2m3XCrCU8mplYOujMBbvyS547AxJkC+pGnd0Cm15eNxEUNU8g==" }, "@types/chai-as-promised": { "version": "7.1.2", "resolved": "https://verdaccio.lossless.one/@types%2fchai-as-promised/-/chai-as-promised-7.1.2.tgz", "integrity": "sha512-PO2gcfR3Oxa+u0QvECLe1xKXOqYTzCmWf0FhLhjREoW3fPAVamjihL7v1MOVLJLsnAMdLcjkfrs01yvDMwVK4Q==", - "dev": true, "requires": { "@types/chai": "*" } @@ -595,7 +577,6 @@ "version": "1.4.2", "resolved": "https://verdaccio.lossless.one/@types%2fchai-string/-/chai-string-1.4.2.tgz", "integrity": "sha512-ld/1hV5qcPRGuwlPdvRfvM3Ka/iofOk2pH4VkasK4b1JJP1LjNmWWn0LsISf6RRzyhVOvs93rb9tM09e+UuF8Q==", - "dev": true, "requires": { "@types/chai": "*" } @@ -863,8 +844,7 @@ "ansi-256-colors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-256-colors/-/ansi-256-colors-1.1.0.tgz", - "integrity": "sha1-kQ3lDvzHwJ49gvL4er1rcAwYgYo=", - "dev": true + "integrity": "sha1-kQ3lDvzHwJ49gvL4er1rcAwYgYo=" }, "ansi-regex": { "version": "3.0.0", @@ -915,8 +895,7 @@ "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" }, "asynckit": { "version": "0.4.0", @@ -1046,7 +1025,6 @@ "version": "4.2.0", "resolved": "https://verdaccio.lossless.one/chai/-/chai-4.2.0.tgz", "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", @@ -1060,7 +1038,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", - "dev": true, "requires": { "check-error": "^1.0.2" } @@ -1068,8 +1045,7 @@ "chai-string": { "version": "1.5.0", "resolved": "https://verdaccio.lossless.one/chai-string/-/chai-string-1.5.0.tgz", - "integrity": "sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw==", - "dev": true + "integrity": "sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw==" }, "chalk": { "version": "2.4.2", @@ -1085,8 +1061,7 @@ "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" }, "cli-cursor": { "version": "2.1.0", @@ -1262,7 +1237,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, "requires": { "type-detect": "^4.0.0" } @@ -1568,8 +1542,7 @@ "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" }, "get-stream": { "version": "4.1.0", @@ -2339,8 +2312,7 @@ "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" }, "performance-now": { "version": "2.1.0", @@ -2680,7 +2652,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/smartchai/-/smartchai-2.0.1.tgz", "integrity": "sha512-9M+R56OhAHXScxgr2vzQqxGx0XMS0QXriNZuP7hjlbVbo2FUT+l60iEzbwPt9Ga+5u2cEEjSSoZEQVqlROaddA==", - "dev": true, "requires": { "@types/chai": "^4.1.2", "@types/chai-as-promised": "^7.1.0", @@ -2917,8 +2888,7 @@ "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" }, "type-fest": { "version": "0.8.1", diff --git a/package.json b/package.json index 9238201..b71ffa8 100644 --- a/package.json +++ b/package.json @@ -36,13 +36,14 @@ "@pushrocks/smartstring": "^3.0.18", "@pushrocks/smarttime": "^3.0.12", "@pushrocks/smartunique": "^3.0.1", + "@tsclass/tsclass": "^3.0.4", "acme-client": "^3.3.1" }, "devDependencies": { "@gitzone/tsbuild": "^2.1.17", "@gitzone/tsrun": "^1.2.8", "@gitzone/tstest": "^1.0.28", - "@mojoio/cloudflare": "^3.0.5", + "@mojoio/cloudflare": "^4.0.3", "@pushrocks/qenv": "^4.0.6", "@pushrocks/tapbundle": "^3.2.0", "@types/node": "^13.7.0", diff --git a/test/test.ts b/test/test.ts index 7c38cd7..5d50e2d 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,7 +1,12 @@ import { tap, expect } from '@pushrocks/tapbundle'; import { Qenv } from '@pushrocks/qenv'; +import * as cloudflare from '@mojoio/cloudflare'; const testQenv = new Qenv('./', './.nogit/'); +const testCloudflare = new cloudflare.CloudflareAccount({ + email: testQenv.getEnvVarOnDemand('CF_EMAIL'), + key: testQenv.getEnvVarOnDemand('CF_KEY') +}); import * as smartacme from '../ts/index'; @@ -16,11 +21,11 @@ tap.test('should create a valid instance of SmartAcme', async () => { mongoDbPass: testQenv.getEnvVarRequired('MONGODB_PASSWORD'), mongoDbUrl: testQenv.getEnvVarRequired('MONGODB_URL') }, - removeChallenge: async (...args) => { - console.log(args); + removeChallenge: async (dnsChallenge) => { + testCloudflare.convenience.acmeRemoveDnsChallenge(dnsChallenge); }, - setChallenge: async (...args) => { - console.log(args); + setChallenge: async (dnsChallenge) => { + testCloudflare.convenience.acmeSetDnsChallenge(dnsChallenge); }, environment: 'integration' }); diff --git a/ts/index.ts b/ts/index.ts index c324df1..7c69782 100644 --- a/ts/index.ts +++ b/ts/index.ts @@ -1,3 +1 @@ export * from './smartacme.classes.smartacme'; - -export * from './smartacme.classes.certremoteclient'; diff --git a/ts/interfaces/cert.ts b/ts/interfaces/cert.ts index 99b072a..3453588 100644 --- a/ts/interfaces/cert.ts +++ b/ts/interfaces/cert.ts @@ -1,6 +1,6 @@ export type TCertStatus = 'existing' | 'nonexisting' | 'pending' | 'failed'; -export interface ICert { +export interface IOldCert { id: string; domainName: string; created: number; diff --git a/ts/smartacme.classes.cert.ts b/ts/smartacme.classes.cert.ts index 02dd622..85e43d8 100644 --- a/ts/smartacme.classes.cert.ts +++ b/ts/smartacme.classes.cert.ts @@ -9,7 +9,7 @@ import { Collection, svDb, unI } from '@pushrocks/smartdata'; @plugins.smartdata.Collection(() => { return CertManager.activeDB; }) -export class Cert extends plugins.smartdata.SmartDataDbDoc implements interfaces.ICert { +export class Cert extends plugins.smartdata.SmartDataDbDoc implements plugins.tsclass.network.ICert { @unI() public id: string; @@ -28,28 +28,30 @@ export class Cert extends plugins.smartdata.SmartDataDbDoc implements inte @svDb() public csr: string; - /** - * computed value for when the certificate is still valid - */ - get validUntil(): number { - return ( - this.created + - plugins.smarttime.getMilliSecondsFromUnits({ - days: 90 - }) - ); + @svDb() + public validUntil: number; + + public isStillValid(): boolean { + return this.validUntil >= Date.now(); } - get isStillValid(): boolean { - const shouldBeValitAtLeastUntil = + public shouldBeRenewed(): boolean { + const shouldBeValidAtLeastUntil = Date.now() + plugins.smarttime.getMilliSecondsFromUnits({ days: 10 }); - return this.validUntil >= shouldBeValitAtLeastUntil; + return this.validUntil >= shouldBeValidAtLeastUntil; } - constructor(optionsArg: interfaces.ICert) { + public update(certDataArg: plugins.tsclass.network.ICert) { + Object.keys(certDataArg).forEach(key => { + this[key] = certDataArg[key]; + }); + } + + + constructor(optionsArg: plugins.tsclass.network.ICert) { super(); if (optionsArg) { Object.keys(optionsArg).forEach(key => { diff --git a/ts/smartacme.classes.certmanager.ts b/ts/smartacme.classes.certmanager.ts index f253ed6..6dd7d5c 100644 --- a/ts/smartacme.classes.certmanager.ts +++ b/ts/smartacme.classes.certmanager.ts @@ -16,7 +16,7 @@ export class CertManager { private mongoDescriptor: plugins.smartdata.IMongoDescriptor; public smartdataDb: plugins.smartdata.SmartdataDb; - public pendingMap: plugins.lik.Stringmap; + public interestMap: plugins.lik.InterestMap; constructor( smartAcmeArg: SmartAcme, @@ -34,18 +34,17 @@ export class CertManager { CertManager.activeDB = this.smartdataDb; // Pending Map - this.pendingMap = new plugins.lik.Stringmap(); + this.interestMap = new plugins.lik.InterestMap((certName) => certName); } /** * retrieves a certificate * @returns the Cert class or null - * @param domainName the domain Name to retrieve the vcertificate for + * @param certDomainNameArg the domain Name to retrieve the vcertificate for */ - public async retrieveCertificate(domainName: string): Promise { - await this.checkCerts(); + public async retrieveCertificate(certDomainNameArg: string): Promise { const existingCertificate: Cert = await Cert.getInstance({ - domainName + domainName: certDomainNameArg }); if (existingCertificate) { @@ -59,44 +58,20 @@ export class CertManager { * stores the certificate * @param optionsArg */ - public async storeCertificate(optionsArg: interfaces.ICert) { + public async storeCertificate(optionsArg: plugins.tsclass.network.ICert) { const cert = new Cert(optionsArg); await cert.save(); - this.pendingMap.removeString(optionsArg.domainName); - } - - public async deleteCertificate(domainNameArg: string) {} - - /** - * announce a certificate as being in the process of being retrieved - */ - public async announceCertificate(domainNameArg: string) { - this.pendingMap.addString(domainNameArg); - } - - /** - * gets the status of a certificate by certDomain name - * @param certDomainArg - */ - public async getCertificateStatus(certDomainArg: string): Promise { - const isPending = this.pendingMap.checkString(certDomainArg); - if (isPending) { - return 'pending'; + const interest = this.interestMap.findInterest(cert.domainName); + if (interest) { + interest.fullfillInterest(cert); + interest.markLost(); } - - // otherwise lets continue - const existingCertificate = await this.retrieveCertificate(certDomainArg); - if (existingCertificate) { - return 'existing'; - } - - return 'nonexisting'; } - /** - * checks all certs for expiration - */ - private async checkCerts() { - // TODO + public async deleteCertificate(certDomainNameArg: string) { + const cert: Cert = await Cert.getInstance({ + domainName: certDomainNameArg + }); + await cert.delete(); } } diff --git a/ts/smartacme.classes.certremoteclient.ts b/ts/smartacme.classes.certremoteclient.ts deleted file mode 100644 index 59aff2f..0000000 --- a/ts/smartacme.classes.certremoteclient.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as plugins from './smartacme.plugins'; -import * as interfaces from './interfaces'; - -// tslint:disable-next-line: max-classes-per-file -export class CertRemoteClient { - private remoteUrl: string; - private secret: string; - private logger: plugins.smartlog.Smartlog; - - constructor(optionsArg: { - remoteUrl: string; - secret: string; - logger?: plugins.smartlog.Smartlog; - }) { - this.remoteUrl = optionsArg.remoteUrl; - this.secret = optionsArg.secret; - optionsArg.logger - ? (this.logger = optionsArg.logger) - : (this.logger = plugins.smartlog.defaultLogger); - } - - /** - * - * @param domainNameArg - */ - public async getCertificateForDomain(domainNameArg: string): Promise { - let certificate: interfaces.ICert; - const doRequestCycle = async (): Promise => { - const responseBody: interfaces.ICertRemoteResponse = ( - await plugins.smartrequest.postJson(this.remoteUrl, { - requestBody: { - domainName: domainNameArg, - secret: this.secret - } - }) - ).body; - switch (responseBody.status as interfaces.TCertStatus) { - case 'pending': - this.logger.log('info', `request for ${domainNameArg} still pending!`); - await plugins.smartdelay.delayFor(5000); - const finalResponse = await doRequestCycle(); - return finalResponse; - case 'existing': - this.logger.log('ok', `got certificate for ${domainNameArg}`); - return responseBody.certificate; - case 'failed': - default: - console.log(`could not retrieve certificate for ${domainNameArg}`); - return null; - } - }; - certificate = await doRequestCycle(); - return certificate; - } -} diff --git a/ts/smartacme.classes.smartacme.ts b/ts/smartacme.classes.smartacme.ts index 2c7e346..b5636c7 100644 --- a/ts/smartacme.classes.smartacme.ts +++ b/ts/smartacme.classes.smartacme.ts @@ -13,8 +13,8 @@ export interface ISmartAcmeOptions { accountPrivateKey?: string; accountEmail: string; mongoDescriptor: plugins.smartdata.IMongoDescriptor; - setChallenge: (domainName: string, keyAuthorization: string) => Promise; - removeChallenge: (domainName: string) => Promise; + setChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise; + removeChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise; environment: 'production' | 'integration'; logger?: plugins.smartlog.Smartlog; } @@ -41,53 +41,13 @@ export class SmartAcme { private privateKey: string; // challenge fullfillment - private setChallenge: (domainName: string, keyAuthorization: string) => Promise; - private removeChallenge: (domainName: string) => Promise; + private setChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise; + private removeChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise; // certmanager private certmanager: CertManager; private certmatcher: CertMatcher; - /** - * the remote handler to hand the request and response to. - */ - public certremoteHandler = async ( - req: plugins.smartexpress.Request, - res: plugins.smartexpress.Response - ) => { - const requestBody: interfaces.ICertRemoteRequest = req.body; - this.logger.log('ok', `got certificate request for ${requestBody.domainName}`); - const certDomain = this.certmatcher.getCertificateDomainNameByDomainName( - requestBody.domainName - ); - this.logger.log('ok', `mapping ${requestBody.domainName} to ${certDomain}`); - let status: interfaces.TCertStatus = await this.certmanager.getCertificateStatus(certDomain); - let response: interfaces.ICertRemoteResponse; - switch (status) { - case 'existing': - this.logger.log('ok', `certificate exists for ${certDomain}. Sending certificate!`); - response = { - status, - certificate: await ( - await this.certmanager.retrieveCertificate(certDomain) - ).createSavableObject() - }; - break; - default: - if (status === 'nonexisting') { - this.getCertificateForDomain(certDomain); - status = 'pending'; - } - response = { - status - }; - break; - } - res.status(200); - res.send(response); - res.end(); - }; - constructor(optionsArg: ISmartAcmeOptions) { this.options = optionsArg; this.options.logger @@ -144,8 +104,8 @@ export class SmartAcme { * it runs through the following steps * * * look in the database - * * if in the database return it - * * of not in the database announce it + * * if in the database and still valid return it + * * if not in the database announce it * * then get it from letsencrypt * * store it * * remove it from the pending map (which it go onto by announcing it) @@ -154,20 +114,28 @@ export class SmartAcme { * @param domainArg */ public async getCertificateForDomain(domainArg: string): Promise { - const certDomain = this.certmatcher.getCertificateDomainNameByDomainName(domainArg); - const retrievedCertificate = await this.certmanager.retrieveCertificate(certDomain); + const certDomainName = this.certmatcher.getCertificateDomainNameByDomainName(domainArg); + const retrievedCertificate = await this.certmanager.retrieveCertificate(certDomainName); - if (retrievedCertificate) { + if (!retrievedCertificate && await this.certmanager.interestMap.checkInterest(certDomainName)) { + const existingCertificateInterest = this.certmanager.interestMap.findInterest(certDomainName); + const certificate = existingCertificateInterest.interestFullfilled; + return certificate; + } else if (retrievedCertificate && !retrievedCertificate.shouldBeRenewed()) { return retrievedCertificate; - } else { - await this.certmanager.announceCertificate(certDomain); + } else if (retrievedCertificate && retrievedCertificate.shouldBeRenewed) { + await retrievedCertificate.delete(); } + // lets make sure others get the same interest + await this.certmanager.interestMap.addInterest(certDomainName); + + /* Place new order */ const order = await this.client.createOrder({ identifiers: [ - { type: 'dns', value: certDomain }, - { type: 'dns', value: `*.${certDomain}` } + { type: 'dns', value: certDomainName }, + { type: 'dns', value: `*.${certDomainName}` } ] }); @@ -176,7 +144,7 @@ export class SmartAcme { for (const authz of authorizations) { console.log(authz); - const domainDnsName: string = `_acme-challenge.${authz.identifier.value}`; + const fullHostName: string = `_acme-challenge.${authz.identifier.value}`; const dnsChallenge: string = authz.challenges.find(challengeArg => { return challengeArg.type === 'dns-01'; }); @@ -185,8 +153,11 @@ export class SmartAcme { try { /* Satisfy challenge */ - await this.setChallenge(domainDnsName, keyAuthorization); - await this.smartdns.checkUntilAvailable(domainDnsName, 'TXT', keyAuthorization, 100, 5000); + await this.setChallenge({ + hostName: fullHostName, + challenge: keyAuthorization + }); + await this.smartdns.checkUntilAvailable(fullHostName, 'TXT', keyAuthorization, 100, 5000); console.log('Cool down an extra 60 second for region availability'); await plugins.smartdelay.delayFor(60000); @@ -201,7 +172,10 @@ export class SmartAcme { } finally { /* Clean up challenge response */ try { - await this.removeChallenge(domainDnsName); + await this.removeChallenge({ + hostName: fullHostName, + challenge: keyAuthorization + }); } catch (e) { console.log(e); } @@ -210,8 +184,8 @@ export class SmartAcme { /* Finalize order */ const [key, csr] = await plugins.acme.forge.createCsr({ - commonName: `*.${certDomain}`, - altNames: [certDomain] + commonName: `*.${certDomainName}`, + altNames: [certDomainName] }); await this.client.finalizeOrder(order, csr); @@ -221,14 +195,19 @@ export class SmartAcme { await this.certmanager.storeCertificate({ id: plugins.smartunique.shortId(), - domainName: certDomain, + domainName: certDomainName, privateKey: key.toString(), publicKey: cert.toString(), csr: csr.toString(), - created: Date.now() + created: Date.now(), + validUntil: + Date.now() + + plugins.smarttime.getMilliSecondsFromUnits({ + days: 90 + }) }); - const newCertificate = await this.certmanager.retrieveCertificate(certDomain); + const newCertificate = await this.certmanager.retrieveCertificate(certDomainName); return newCertificate; } } diff --git a/ts/smartacme.plugins.ts b/ts/smartacme.plugins.ts index dfe968d..e62357b 100644 --- a/ts/smartacme.plugins.ts +++ b/ts/smartacme.plugins.ts @@ -25,7 +25,14 @@ export { smarttime }; -// thirs party scope +// @tsclass scope +import * as tsclass from '@tsclass/tsclass'; + +export { + tsclass +} + +// third party scope import * as acme from 'acme-client'; export { acme };