|
| 1 | +import * as express from 'express'; |
| 2 | +import * as fs from 'fs'; |
| 3 | +import * as cors from 'cors'; |
| 4 | +import * as compression from 'compression'; |
| 5 | +import * as helmet from 'helmet'; |
| 6 | +import {CloudflareChallenge} from '@interactivetraining/le-challenge-cloudflare'; |
| 7 | +import {IPackageParams} from './interfaces'; |
| 8 | +import {downloadPackage} from './helpers'; |
| 9 | + |
| 10 | +require('dotenv').config(); |
| 11 | + |
| 12 | +if (!fs.existsSync('cache')) fs.mkdirSync('cache'); |
| 13 | +if (!fs.existsSync('acme')) fs.mkdirSync('acme'); |
| 14 | + |
| 15 | +const app = express(); |
| 16 | + |
| 17 | +app.use(helmet()); |
| 18 | +app.use(cors()); |
| 19 | +app.use(compression()); |
| 20 | + |
| 21 | +app.get(['/:scope?/:package@:version/*', '/:scope?/:package/*'], async (req, res) => { |
| 22 | + try { |
| 23 | + let params: IPackageParams = req.params; |
| 24 | + |
| 25 | + // correct the params.package value when there isn't a scope or version provided |
| 26 | + if (!params.version && params.scope && !req.url.split('/')[1].includes('@')) { |
| 27 | + params.package = params.scope; |
| 28 | + params.scope = undefined; |
| 29 | + } |
| 30 | + |
| 31 | + if (!params.version) params.version = 'latest'; |
| 32 | + |
| 33 | + const packagePath = `cache/${params.version}/${(params.scope) ? `${params.scope}/` : ``}${params.package}`; |
| 34 | + const filePath = `${packagePath}/${params['0']}`; |
| 35 | + |
| 36 | + if (!fs.existsSync(packagePath) || params.version === 'latest') { |
| 37 | + await downloadPackage({ |
| 38 | + scope: params.scope, |
| 39 | + package: params.package, |
| 40 | + version: params.version |
| 41 | + }, `cache/${params.version}`); |
| 42 | + } |
| 43 | + |
| 44 | + console.log(`${(params.scope) ? `${params.scope}/` : ``}${params.package}@${params.version}: ${params['0']}`); |
| 45 | + |
| 46 | + res.setHeader('Cache-Control', (!params.version.includes('.')) ? 'no-cache' : 'public, max-age=31536000'); |
| 47 | + res.sendFile(filePath, {root: `./`}); |
| 48 | + } catch (e) { |
| 49 | + console.log(e); |
| 50 | + const status = (e.hasOwnProperty('statusCode')) ? e.statusCode : 500; |
| 51 | + const message = (e.hasOwnProperty('statusMessage')) ? e.statusMessage : e.message; |
| 52 | + res.status(status).send(message); |
| 53 | + } |
| 54 | +}); |
| 55 | + |
| 56 | +require('greenlock-express').create({ |
| 57 | + version: 'draft-11', |
| 58 | + server: 'https://acme-v02.api.letsencrypt.org/directory', |
| 59 | + email: process.env.LETS_ENCRYPT_EMAIL, |
| 60 | + agreeTos: (process.env.LETS_ENCRYPT_AGREE_TO_TOS.trim() === 'true'), |
| 61 | + approveDomains: [ |
| 62 | + process.env.DOMAIN |
| 63 | + ], |
| 64 | + configDir: 'acme/', |
| 65 | + app: app, |
| 66 | + challengeType: 'dns-01', |
| 67 | + challenge: new CloudflareChallenge({ |
| 68 | + cloudflare: { |
| 69 | + email: process.env.CLOUDFLARE_EMAIL, |
| 70 | + key: process.env.CLOUDFLARE_API_KEY |
| 71 | + }, |
| 72 | + acmePrefix: '_acme-challenge', |
| 73 | + verifyPropagation: {waitFor: 5000, retries: 50}, |
| 74 | + useDNSOverHTTPS: false |
| 75 | + }) |
| 76 | +}).listen(80, 443, () => console.log(`Listening...`)); |
0 commit comments