|
| 1 | +import {Container, Service} from 'typedi'; |
| 2 | +import {ConfigService} from './configService'; |
| 3 | +import {CloudflareApi, ICloudflareResourceZone} from '../types/cloudflare'; |
| 4 | +import * as cloudflare from 'cloudflare'; |
| 5 | +import * as dns from "dns"; |
| 6 | +import {flatten} from '../helpers'; |
| 7 | + |
| 8 | +@Service() |
| 9 | +export class CloudflareService { |
| 10 | + private config: ConfigService; |
| 11 | + private cf: CloudflareApi; |
| 12 | + private primaryZone: ICloudflareResourceZone; |
| 13 | + private challengeData: string; |
| 14 | + |
| 15 | + constructor() { |
| 16 | + this.config = Container.get(ConfigService); |
| 17 | + if (this.config && this.config.cloudflare) { |
| 18 | + this.cf = cloudflare({ |
| 19 | + email: this.config.cloudflare.email, |
| 20 | + key: this.config.cloudflare.apiKey |
| 21 | + }); |
| 22 | + } |
| 23 | + } |
| 24 | + |
| 25 | + async getZone(): Promise<ICloudflareResourceZone> { |
| 26 | + if (!this.primaryZone) { |
| 27 | + return (await this.cf.zones.browse({ |
| 28 | + page: 1, |
| 29 | + per_page: 1000 |
| 30 | + })).result.find(el => el.name.includes(this.config.getParentDomain())); |
| 31 | + } |
| 32 | + return this.primaryZone; |
| 33 | + } |
| 34 | + |
| 35 | + async removeChallengeRecord(): Promise<void> { |
| 36 | + const zoneId = (await this.getZone()).id; |
| 37 | + const findRecord = (await this.cf.dnsRecords.browse(zoneId, { |
| 38 | + page: 1, |
| 39 | + per_page: 1000 |
| 40 | + })).result.find(el => el.type === 'TXT' && el.name.includes('_acme-challenge')); |
| 41 | + if (findRecord) { |
| 42 | + await this.cf.dnsRecords.del(zoneId, findRecord.id); |
| 43 | + } |
| 44 | + } |
| 45 | + |
| 46 | + locallyVerifyTxtRecord(count = 0) { |
| 47 | + return new Promise((resolve) => { |
| 48 | + dns.resolveTxt(`_acme-challenge.${this.config.domain}`, (err, addresses) => { |
| 49 | + // if (err) console.error(err); |
| 50 | + const values = flatten(addresses, 2); |
| 51 | + if (values.find(el => el.includes(this.challengeData))) { |
| 52 | + console.log('domain verified!', values); |
| 53 | + resolve(); |
| 54 | + } else { |
| 55 | + setTimeout(async () => { |
| 56 | + console.log('(' + count + ') verifying acme challenge...'); |
| 57 | + resolve((await this.locallyVerifyTxtRecord(count + 1))); |
| 58 | + }, 8000); |
| 59 | + } |
| 60 | + }); |
| 61 | + }) |
| 62 | + }; |
| 63 | + |
| 64 | + addChallengeRecord(data: string) { |
| 65 | + this.challengeData = data; |
| 66 | + return new Promise(async (resolve) => { |
| 67 | + await this.cf.dnsRecords.add((await this.getZone()).id, { |
| 68 | + type: 'TXT', |
| 69 | + name: `_acme-challenge.${this.config.domain}`, |
| 70 | + content: this.challengeData, |
| 71 | + proxied: false, |
| 72 | + ttl: 1 |
| 73 | + }); |
| 74 | + this.locallyVerifyTxtRecord().then(() => resolve()); |
| 75 | + }) |
| 76 | + } |
| 77 | + |
| 78 | +} |
0 commit comments