dh2cf
Version:
Syncs DreamHost DNS records to Cloudflare in case you want to use both services.
360 lines (343 loc) • 11.8 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
var __export = (target, all) => {
__markAsModule(target);
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __reExport = (target, module2, desc) => {
if (module2 && typeof module2 === "object" || typeof module2 === "function") {
for (let key of __getOwnPropNames(module2))
if (!__hasOwnProp.call(target, key) && key !== "default")
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable });
}
return target;
};
var __toModule = (module2) => {
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2);
};
var __decorateClass = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result)
__defProp(target, key, result);
return result;
};
// src/cli.ts
__export(exports, {
default: () => main
});
var import_yargs = __toModule(require("yargs"));
// src/index.ts
var import_typescript_memoize = __toModule(require("typescript-memoize"));
var import_got = __toModule(require("got"));
// node_modules/read-pkg-up/index.js
var import_node_path4 = __toModule(require("node:path"));
// node_modules/read-pkg-up/node_modules/find-up/index.js
var import_node_path2 = __toModule(require("node:path"));
// node_modules/read-pkg-up/node_modules/locate-path/index.js
var import_node_process = __toModule(require("node:process"));
var import_node_path = __toModule(require("node:path"));
var import_node_fs = __toModule(require("node:fs"));
// node_modules/read-pkg-up/node_modules/yocto-queue/index.js
var Node = class {
value;
next;
constructor(value) {
this.value = value;
}
};
var Queue = class {
#head;
#tail;
#size;
constructor() {
this.clear();
}
enqueue(value) {
const node = new Node(value);
if (this.#head) {
this.#tail.next = node;
this.#tail = node;
} else {
this.#head = node;
this.#tail = node;
}
this.#size++;
}
dequeue() {
const current = this.#head;
if (!current) {
return;
}
this.#head = this.#head.next;
this.#size--;
return current.value;
}
clear() {
this.#head = void 0;
this.#tail = void 0;
this.#size = 0;
}
get size() {
return this.#size;
}
*[Symbol.iterator]() {
let current = this.#head;
while (current) {
yield current.value;
current = current.next;
}
}
};
// node_modules/read-pkg-up/node_modules/locate-path/index.js
var typeMappings = {
directory: "isDirectory",
file: "isFile"
};
function checkType(type) {
if (type in typeMappings) {
return;
}
throw new Error(`Invalid type specified: ${type}`);
}
var matchType = (type, stat) => type === void 0 || stat[typeMappings[type]]();
function locatePathSync(paths, {
cwd = import_node_process.default.cwd(),
type = "file",
allowSymlinks = true
} = {}) {
checkType(type);
const statFunction = allowSymlinks ? import_node_fs.default.statSync : import_node_fs.default.lstatSync;
for (const path_ of paths) {
try {
const stat = statFunction(import_node_path.default.resolve(cwd, path_));
if (matchType(type, stat)) {
return path_;
}
} catch {
}
}
}
// node_modules/read-pkg-up/node_modules/path-exists/index.js
var import_node_fs2 = __toModule(require("node:fs"));
// node_modules/read-pkg-up/node_modules/find-up/index.js
var findUpStop = Symbol("findUpStop");
function findUpMultipleSync(name, options = {}) {
let directory = import_node_path2.default.resolve(options.cwd || "");
const { root } = import_node_path2.default.parse(directory);
const stopAt = options.stopAt || root;
const limit = options.limit || Number.POSITIVE_INFINITY;
const paths = [name].flat();
const runMatcher = (locateOptions) => {
if (typeof name !== "function") {
return locatePathSync(paths, locateOptions);
}
const foundPath = name(locateOptions.cwd);
if (typeof foundPath === "string") {
return locatePathSync([foundPath], locateOptions);
}
return foundPath;
};
const matches = [];
while (true) {
const foundPath = runMatcher(__spreadProps(__spreadValues({}, options), { cwd: directory }));
if (foundPath === findUpStop) {
break;
}
if (foundPath) {
matches.push(import_node_path2.default.resolve(directory, foundPath));
}
if (directory === stopAt || matches.length >= limit) {
break;
}
directory = import_node_path2.default.dirname(directory);
}
return matches;
}
function findUpSync(name, options = {}) {
const matches = findUpMultipleSync(name, __spreadProps(__spreadValues({}, options), { limit: 1 }));
return matches[0];
}
// node_modules/read-pkg/index.js
var import_node_process2 = __toModule(require("node:process"));
var import_node_fs3 = __toModule(require("node:fs"));
var import_node_path3 = __toModule(require("node:path"));
var import_parse_json = __toModule(require("parse-json"));
var import_normalize_package_data = __toModule(require("normalize-package-data"));
function readPackageSync({ cwd = import_node_process2.default.cwd(), normalize = true } = {}) {
const filePath = import_node_path3.default.resolve(cwd, "package.json");
const json = (0, import_parse_json.default)(import_node_fs3.default.readFileSync(filePath, "utf8"));
if (normalize) {
(0, import_normalize_package_data.default)(json);
}
return json;
}
// node_modules/read-pkg-up/index.js
function readPackageUpSync(options) {
const filePath = findUpSync("package.json", options);
if (!filePath) {
return;
}
return {
packageJson: readPackageSync(__spreadProps(__spreadValues({}, options), { cwd: import_node_path4.default.dirname(filePath) })),
path: filePath
};
}
// src/version.ts
var { packageJson } = readPackageUpSync({ cwd: __dirname });
var version = packageJson.version;
var version_default = version;
// src/dh-list-records-response.ts
var z = __toModule(require("zod"));
var DhListRecordsResponseSchema = z.object({
result: z.enum(["success"]),
data: z.array(z.object({
record: z.string(),
type: z.string(),
value: z.string(),
zone: z.string()
}))
});
// src/index.ts
var Dh2Cf = class {
constructor(config) {
this.config = config;
const client = import_got.default.extend({
headers: {
"User-Agent": `npmjs.com/package/dh2cf/v/${version_default}`
},
responseType: "json",
prefixUrl: "https://api.dreamhost.com"
});
this.dhClient = client.extend({
prefixUrl: "https://api.dreamhost.com"
});
this.cfClient = client.extend({
prefixUrl: "https://api.cloudflare.com/client/v4"
});
}
async run() {
console.dir({ options: this.config });
const dhRecords = await this.getDreamHostRecords();
console.dir({ dhRecords }, { depth: null });
}
get allowedTypes() {
return new Set(this.config.types);
}
get ignoredRecords() {
return new Set(this.config.ignoreNames.map((name) => name === "@" ? this.config.zone : name).map((name) => `${name}.${this.config.zone}`));
}
async getDreamHostRecords() {
console.log("Fetching dns records from DreamHost");
const query = `?key=${this.config.dhToken}&cmd=dns-list_records&format=json`;
const response = await this.dhClient.get(query).json();
const dhListRecordsResponse = DhListRecordsResponseSchema.parse(response);
const records = dhListRecordsResponse.data.filter((record) => record.zone === this.config.zone);
if (this.config.verbose) {
console.log(`DreamHost records for zone '${this.config.zone}`);
for (const record of records) {
console.log(JSON.stringify(record));
}
}
return records;
}
};
__decorateClass([
(0, import_typescript_memoize.Memoize)()
], Dh2Cf.prototype, "allowedTypes", 1);
__decorateClass([
(0, import_typescript_memoize.Memoize)()
], Dh2Cf.prototype, "ignoredRecords", 1);
// src/config.ts
var z2 = __toModule(require("zod"));
var ConfigSchema = z2.object({
cfEmail: z2.string().min(1),
cfToken: z2.string().min(1),
dhToken: z2.string().min(1),
ignoreNames: z2.array(z2.string()).default([]),
sync: z2.boolean().default(false),
types: z2.array(z2.string()).default([]),
verbose: z2.boolean().default(false),
zone: z2.string().min(1)
});
// src/cli.ts
async function main() {
const cliConfig = import_yargs.default.env("DH2CF").option("cf-email", {
type: "string",
demandOption: true,
describe: "Cloudflare account email"
}).option("cf-token", {
type: "string",
demandOption: true,
describe: "Cloudflare API token"
}).option("dh-token", {
type: "string",
demandOption: true,
describe: "DreamHost API token"
}).option("ignore-names", {
type: "array",
alias: "i",
describe: 'Record names to ignore, e.g. for zone example.com, the value "ssh @" would skip ssh.example.com and example.com',
default: []
}).option("sync", {
type: "boolean",
alias: "S",
default: false,
describe: "Sync records from DreamHost to Cloudflare. Does not delete records that only exist in Cloudflare. This option is required in order to actually perform any operations."
}).option("types", {
type: "array",
alias: "t",
describe: 'Record types to process, e.g. "A CNAME MX"',
default: "A CNAME MX TXT".split(" ")
}).option("verbose", {
type: "boolean",
alias: "v",
default: false
}).option("zone", {
type: "string",
alias: "z",
demandOption: true,
describe: 'Zone to compare, e.g. "example.com"'
}).group(["cf-email", "cf-token", "dh-token"], "Cloudflare and DreamHost credentials:").group(["ignore-names", "sync", "types", "zone"], "DNS sync options:").example("$0 --zone example.com", "Preview changes that would sync for example.com").example("$0 --zone example.com --sync", "Perform the sync for example.com").argv;
let config;
try {
config = ConfigSchema.parse(cliConfig);
} catch (error) {
console.error(`${error}
`);
import_yargs.default.showHelp();
process.exit(1);
}
const dh2cf = new Dh2Cf(config);
await dh2cf.run();
}
main().catch((error) => {
throw error;
});
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {});