Initial commit
This commit is contained in:
commit
af97bccb0e
10 changed files with 548 additions and 0 deletions
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
76
.gitignore
vendored
Normal file
76
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"js/ts.implicitProjectConfig.checkJs": true
|
||||
}
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Wildan M
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
2
README.md
Normal file
2
README.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# forward-domain
|
||||
|
||||
131
app.js
Normal file
131
app.js
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
// production endpoint (use pm2/phusion/whatever)
|
||||
|
||||
const argv = require('minimist')(process.argv.slice(2));
|
||||
const app = require("./index.js");
|
||||
|
||||
const https = require('https');
|
||||
const tls = require('tls');
|
||||
const certnode = require('certnode')
|
||||
const fs = require('fs');
|
||||
const {
|
||||
default: axios
|
||||
} = require('axios');
|
||||
const path = require('path');
|
||||
const record_email_prefix = 'forward-domain-cert-maintainer=';
|
||||
const client = new certnode.Client();
|
||||
const certsDir = path.join(__dirname, '.certs');
|
||||
var crypto = require('crypto');
|
||||
|
||||
/**
|
||||
* @type {Object<string, {cert: any, key: any, expire: number}>}
|
||||
*/
|
||||
const resolveCache = {};
|
||||
|
||||
function md5(str) {
|
||||
return crypto.createHash('md5').update(str).digest('hex');
|
||||
}
|
||||
|
||||
function getCertCachePath(host) {
|
||||
const hash = md5(host);
|
||||
return path.join(certsDir, hash.substr(0, 2), hash.substr(2), host);
|
||||
}
|
||||
async function findMaintainerEmail(host) {
|
||||
const resolve = await axios(`https://dns.google/resolve?name=${encodeURIComponent(host)}&type=TXT`);
|
||||
for (const head of resolve.data.Answer) {
|
||||
return head.data.slice(record_email_prefix.length);
|
||||
}
|
||||
throw new Error(record_email_prefix + ' TXT is missing');
|
||||
}
|
||||
|
||||
async function buildCache(host) {
|
||||
const dir = getCertCachePath(host);
|
||||
const keyP = path.join(dir, 'privateKey.pem');
|
||||
const certP = path.join(dir, 'publicKey.pem');
|
||||
const extP = path.join(dir, 'expire');
|
||||
await ensureDir(dir);
|
||||
try {
|
||||
await fs.promises.access(keyP, fs.constants.R_OK | fs.constants.W_OK);
|
||||
await fs.promises.access(certP, fs.constants.R_OK | fs.constants.W_OK);
|
||||
await fs.promises.access(extP, fs.constants.R_OK | fs.constants.W_OK);
|
||||
const expire = parseInt((await fs.promises.readFile(extP)).toString('utf8'));
|
||||
if (Date.now() > expire)
|
||||
throw null; // expired
|
||||
const cert = await fs.promises.readFile(certP, 'utf8');
|
||||
const key = await fs.promises.readFile(keyP, 'utf8')
|
||||
return {
|
||||
cert,
|
||||
key,
|
||||
expire
|
||||
};
|
||||
} catch (error) {
|
||||
const {
|
||||
certificate,
|
||||
privateKeyData
|
||||
} = await client.generateCertificate(host, await findMaintainerEmail(host));
|
||||
await fs.promises.writeFile(certP, certificate);
|
||||
await certnode.writeKeyToFile(keyP, privateKeyData, '');
|
||||
const expire = (Date.now() + 45 * 86400 * 1000);
|
||||
await fs.promises.writeFile(extP, expire.toString());
|
||||
return {
|
||||
cert: certificate,
|
||||
key: privateKeyData,
|
||||
expire
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function getKeyCert(servername) {
|
||||
let cache = resolveCache[servername];
|
||||
await ensureDir(certsDir);
|
||||
if (!cache || (Date.now() > cache.expire)) {
|
||||
cache = await buildCache(servername);
|
||||
resolveCache[servername] = cache;
|
||||
}
|
||||
return {
|
||||
key: cache.key,
|
||||
cert: cache.cert,
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureDir(dir) {
|
||||
try {
|
||||
await fs.promises.access(dir, fs.constants.W_OK | fs.constants.O_DIRECTORY);
|
||||
} catch (error) {
|
||||
await fs.promises.mkdir(dir, {
|
||||
recursive: true
|
||||
});
|
||||
}
|
||||
}
|
||||
const main = async () => {
|
||||
await client.generateAccountKeyPair()
|
||||
await ensureDir(certsDir);
|
||||
const httpsServer = https.createServer({
|
||||
SNICallback: async (servername, ctx) => {
|
||||
// Generate fresh account keys for Let's Encrypt
|
||||
try {
|
||||
return tls.createSecureContext(await getKeyCert(servername))
|
||||
} catch (error) {
|
||||
ctx(error, null);
|
||||
}
|
||||
}
|
||||
}, app.listeners[0]);
|
||||
|
||||
};
|
||||
|
||||
|
||||
// if (!argv.maintainerEmail)
|
||||
// throw new Error('--maintainerEmail is required');
|
||||
|
||||
// require("greenlock-express")
|
||||
// .init({
|
||||
// packageRoot: __dirname,
|
||||
// configDir: "./greenlock.d",
|
||||
// // contact for security and critical bug notices
|
||||
// maintainerEmail: argv.maintainerEmail,
|
||||
// // whether or not to run at cloudscale
|
||||
// cluster: false
|
||||
// })
|
||||
// // Serves on 80 and 443
|
||||
// // Get's SSL certificates magically!
|
||||
// .serve(app);
|
||||
60
index.js
Normal file
60
index.js
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// development endpoint (use ngrok)
|
||||
|
||||
const http = require('http');
|
||||
const argv = require('minimist')(process.argv.slice(2));
|
||||
const port = parseInt(argv.port || 3000);
|
||||
const record_prefix = 'forward-domain=';
|
||||
|
||||
const {
|
||||
default: axios
|
||||
} = require('axios');
|
||||
|
||||
/**
|
||||
* @type {Object<string, {expire: number, expand: boolean, url: string}>}
|
||||
*/
|
||||
const resolveCache = {};
|
||||
|
||||
async function buildCache(host) {
|
||||
const resolve = await axios(`https://dns.google/resolve?name=${encodeURIComponent(host)}&type=TXT`);
|
||||
for (const head of resolve.data.Answer) {
|
||||
let url = head.data.slice(record_prefix.length);
|
||||
let expand = false;
|
||||
if (url.endsWith('*')) {
|
||||
url = url.substr(0, -1);
|
||||
expand = true;
|
||||
}
|
||||
return {
|
||||
url,
|
||||
expand,
|
||||
expire: Date.now() + Math.max(head.TTL, 86400) * 1000,
|
||||
};
|
||||
}
|
||||
throw new Error(record_prefix + ' TXT is missing');
|
||||
}
|
||||
|
||||
const server = http.createServer(async function (req, res) {
|
||||
try {
|
||||
let cache = resolveCache[req.headers.host];
|
||||
if (!cache || (Date.now() > cache.expire)) {
|
||||
cache = await buildCache(req.headers.host);
|
||||
resolveCache[req.headers.host] = cache;
|
||||
}
|
||||
req.statusCode = 301;
|
||||
req.headers.location = cache.expand ? cache.url + req.url : cache.url;
|
||||
return;
|
||||
} catch (error) {
|
||||
res.statusCode = 400;
|
||||
res.write(error.message || 'Unknown error');
|
||||
} finally {
|
||||
res.end();
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
if (require.main === module) {
|
||||
server.listen(port, function () {
|
||||
console.log(`server start at port ${port}`);
|
||||
});
|
||||
} else {
|
||||
module.exports = server
|
||||
}
|
||||
236
package-lock.json
generated
Normal file
236
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
{
|
||||
"name": "forward-domain",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"certnode": "^0.0.1",
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/certnode": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/certnode/-/certnode-0.0.1.tgz",
|
||||
"integrity": "sha512-zMdAVGR5t6bjpmmRzUvNrIZXBZ3bggHagnFZOJMDjyMWnxpIqtLBEsUsosH99XyvT2SvgJ+I0jGhkNN3lIog9w==",
|
||||
"dependencies": {
|
||||
"jose-node-cjs-runtime": "^3.12.2",
|
||||
"pem": "^1.14.4"
|
||||
}
|
||||
},
|
||||
"node_modules/charenc": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
|
||||
"integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/crypt": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
|
||||
"integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-promisify": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.1.1.tgz",
|
||||
"integrity": "sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg=="
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.14.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.2.tgz",
|
||||
"integrity": "sha512-yLR6WaE2lbF0x4K2qE2p9PEXKLDjUjnR/xmjS3wHAYxtlsI9MLLBJUZirAHKzUZDGLxje7w/cXR49WOUo4rbsA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/is-buffer": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
|
||||
},
|
||||
"node_modules/jose-node-cjs-runtime": {
|
||||
"version": "3.15.4",
|
||||
"resolved": "https://registry.npmjs.org/jose-node-cjs-runtime/-/jose-node-cjs-runtime-3.15.4.tgz",
|
||||
"integrity": "sha512-YkfdukFlO54aqPQ/9H1W4KWkyjHNcw0GPMEVZOXrlgHwnN/wZ1+fiItKvFAAeYZZAzqmw+xMwRTLx4nl3K3ZoA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/md5": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
|
||||
"integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
|
||||
"dependencies": {
|
||||
"charenc": "0.0.2",
|
||||
"crypt": "0.0.2",
|
||||
"is-buffer": "~1.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"node_modules/os-tmpdir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pem": {
|
||||
"version": "1.14.4",
|
||||
"resolved": "https://registry.npmjs.org/pem/-/pem-1.14.4.tgz",
|
||||
"integrity": "sha512-v8lH3NpirgiEmbOqhx0vwQTxwi0ExsiWBGYh0jYNq7K6mQuO4gI6UEFlr6fLAdv9TPXRt6GqiwE37puQdIDS8g==",
|
||||
"dependencies": {
|
||||
"es6-promisify": "^6.0.0",
|
||||
"md5": "^2.2.1",
|
||||
"os-tmpdir": "^1.0.1",
|
||||
"which": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/node-which"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": {
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"certnode": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/certnode/-/certnode-0.0.1.tgz",
|
||||
"integrity": "sha512-zMdAVGR5t6bjpmmRzUvNrIZXBZ3bggHagnFZOJMDjyMWnxpIqtLBEsUsosH99XyvT2SvgJ+I0jGhkNN3lIog9w==",
|
||||
"requires": {
|
||||
"jose-node-cjs-runtime": "^3.12.2",
|
||||
"pem": "^1.14.4"
|
||||
}
|
||||
},
|
||||
"charenc": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
|
||||
"integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
|
||||
},
|
||||
"crypt": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
|
||||
"integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
|
||||
},
|
||||
"es6-promisify": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.1.1.tgz",
|
||||
"integrity": "sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg=="
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.2.tgz",
|
||||
"integrity": "sha512-yLR6WaE2lbF0x4K2qE2p9PEXKLDjUjnR/xmjS3wHAYxtlsI9MLLBJUZirAHKzUZDGLxje7w/cXR49WOUo4rbsA=="
|
||||
},
|
||||
"is-buffer": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
|
||||
},
|
||||
"isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
|
||||
},
|
||||
"jose-node-cjs-runtime": {
|
||||
"version": "3.15.4",
|
||||
"resolved": "https://registry.npmjs.org/jose-node-cjs-runtime/-/jose-node-cjs-runtime-3.15.4.tgz",
|
||||
"integrity": "sha512-YkfdukFlO54aqPQ/9H1W4KWkyjHNcw0GPMEVZOXrlgHwnN/wZ1+fiItKvFAAeYZZAzqmw+xMwRTLx4nl3K3ZoA=="
|
||||
},
|
||||
"md5": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
|
||||
"integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
|
||||
"requires": {
|
||||
"charenc": "0.0.2",
|
||||
"crypt": "0.0.2",
|
||||
"is-buffer": "~1.1.6"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"os-tmpdir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
|
||||
},
|
||||
"pem": {
|
||||
"version": "1.14.4",
|
||||
"resolved": "https://registry.npmjs.org/pem/-/pem-1.14.4.tgz",
|
||||
"integrity": "sha512-v8lH3NpirgiEmbOqhx0vwQTxwi0ExsiWBGYh0jYNq7K6mQuO4gI6UEFlr6fLAdv9TPXRt6GqiwE37puQdIDS8g==",
|
||||
"requires": {
|
||||
"es6-promisify": "^6.0.0",
|
||||
"md5": "^2.2.1",
|
||||
"os-tmpdir": "^1.0.1",
|
||||
"which": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
package.json
Normal file
17
package.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "forward-domain",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"certnode": "^0.0.1",
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
}
|
||||
0
public/.gitkeep
Normal file
0
public/.gitkeep
Normal file
Loading…
Add table
editor.link_modal.header
Reference in a new issue