mirror of
https://github.com/tiennm99/is-a-dev.git
synced 2026-05-16 22:59:15 +00:00
207 lines
5.8 KiB
JavaScript
207 lines
5.8 KiB
JavaScript
const R = require("ramda");
|
|
const { cpanel } = require("./lib/cpanel");
|
|
const { DOMAIN_DOMAIN, VALID_RECORD_TYPES } = require("./constants");
|
|
const { then, log, print, lazyTask, batchLazyTasks } = require("./helpers");
|
|
|
|
const BATCH_SIZE = 1;
|
|
|
|
const recordToRedirection = ({ name, address }) => ({
|
|
domain: name === "@" ? DOMAIN_DOMAIN : `${name}.${DOMAIN_DOMAIN}`,
|
|
redirect: address,
|
|
type: "permanent",
|
|
redirect_wildcard: 1,
|
|
redirect_www: 1,
|
|
});
|
|
const recordToZone = ({ name, type, address, id, priority }) => ({
|
|
line: id,
|
|
name: name === "@" ? `${DOMAIN_DOMAIN}.` : name,
|
|
type,
|
|
address,
|
|
...(type === "MX" ? { priority } : {}),
|
|
...(type === "CNAME" ? { cname: address } : {}),
|
|
...(type === "TXT" ? { txtdata: address } : {}),
|
|
});
|
|
|
|
const cleanName = (name) =>
|
|
[DOMAIN_DOMAIN, `${DOMAIN_DOMAIN}.`].includes(name)
|
|
? "@"
|
|
: `${name}`
|
|
.replace(new RegExp(`\\.${DOMAIN_DOMAIN}\\.?$`), "")
|
|
.toLowerCase();
|
|
|
|
const zoneToRecord = ({
|
|
name,
|
|
type,
|
|
cname,
|
|
address,
|
|
priority,
|
|
preference,
|
|
exchange,
|
|
record,
|
|
line: id,
|
|
}) => ({
|
|
id,
|
|
name: cleanName(name),
|
|
type: `${type}`,
|
|
address: `${exchange || cname || address || record}`
|
|
.replace(/\.$/g, "")
|
|
.toLowerCase(),
|
|
priority: priority || preference,
|
|
});
|
|
const redirectionToRecord = ({ domain, destination }) => ({
|
|
id: domain,
|
|
name: cleanName(domain),
|
|
type: "URL",
|
|
address: `${destination}`.replace(/\/$/g, ""),
|
|
});
|
|
|
|
const recordToEmailMx = ({ name, address, priority }) => ({
|
|
domain: `${name}.is-a.dev`,
|
|
exchanger: address,
|
|
priority,
|
|
});
|
|
|
|
const getHostKey = (host) =>
|
|
`${host.name.toLowerCase()}##${host.type.toLowerCase()}##${host.address.toLowerCase()}`;
|
|
|
|
const isReserved = (domain) =>
|
|
domain.name.startsWith("*") || !VALID_RECORD_TYPES.includes(domain.type);
|
|
|
|
const diffRecords = (oldRecords, newRecords) => {
|
|
const isMatchingRecord = (a, b) => getHostKey(a) === getHostKey(b);
|
|
|
|
const remove = R.differenceWith(isMatchingRecord, oldRecords, newRecords);
|
|
const add = R.differenceWith(isMatchingRecord, newRecords, oldRecords);
|
|
|
|
return { add, remove };
|
|
};
|
|
|
|
const executeBatch = (batches) =>
|
|
batches.reduce((promise, batch, index) => {
|
|
return promise.then(async () => {
|
|
log(
|
|
">>> Running batch number:",
|
|
index + 1,
|
|
`(size: ${batch.length})`,
|
|
);
|
|
|
|
const values = await Promise.all(
|
|
batch.map((fn) => fn().catch((e) => console.error(e))),
|
|
);
|
|
|
|
const results = values.map((data) =>
|
|
R.pathOr({ result: data }, ["cpanelresult", "data", 0], data),
|
|
);
|
|
const failed = results.filter((x) => (x.result || {}).status != 1);
|
|
|
|
log(`${values.length - failed.length}/${values.length}`);
|
|
failed.length && log(JSON.stringify(failed, null, 2));
|
|
|
|
return null;
|
|
});
|
|
}, Promise.resolve());
|
|
|
|
const getDomainService = ({ cpanel }) => {
|
|
const fetchZoneRecords = R.compose(
|
|
then(R.reject(isReserved)),
|
|
then(R.map(zoneToRecord)),
|
|
cpanel.zone.fetch,
|
|
);
|
|
const fetchRedirections = R.compose(
|
|
then(R.map(redirectionToRecord)),
|
|
cpanel.redirection.fetch,
|
|
);
|
|
|
|
const addZoneRecord = lazyTask(
|
|
R.compose(
|
|
R.ifElse(
|
|
R.propEq("type", "MX"),
|
|
R.compose(cpanel.email.add, recordToEmailMx),
|
|
cpanel.zone.add,
|
|
),
|
|
recordToZone,
|
|
print(
|
|
(r) => `Adding zone for ${r.name}: (${r.type} ${r.address})...`,
|
|
),
|
|
),
|
|
);
|
|
const removeZoneRecord = lazyTask(
|
|
R.compose(
|
|
R.ifElse(
|
|
R.propEq("type", "MX"),
|
|
R.compose(cpanel.email.remove, recordToEmailMx),
|
|
R.compose(cpanel.zone.remove, R.pick(["line"])),
|
|
),
|
|
recordToZone,
|
|
print(
|
|
(r) =>
|
|
`Deleting zone for ${r.name}: (${r.type} ${r.address})...`,
|
|
),
|
|
),
|
|
);
|
|
const addRedirection = lazyTask(
|
|
R.compose(
|
|
cpanel.redirection.add,
|
|
recordToRedirection,
|
|
print(({ name }) => `Adding redirection for ${name}`),
|
|
),
|
|
);
|
|
const removeRedirection = lazyTask(
|
|
R.compose(
|
|
cpanel.redirection.remove,
|
|
R.pick(["domain"]),
|
|
recordToRedirection,
|
|
print(({ name }) => `Deleting redirection for ${name}`),
|
|
),
|
|
);
|
|
|
|
const getHosts = () =>
|
|
Promise.all([fetchZoneRecords(), fetchRedirections()]).then(R.flatten);
|
|
|
|
const addRecords = R.compose(
|
|
batchLazyTasks(BATCH_SIZE),
|
|
R.filter(Boolean),
|
|
R.map(
|
|
R.cond([
|
|
[R.propEq("type", "URL"), addRedirection],
|
|
[R.T, addZoneRecord],
|
|
]),
|
|
),
|
|
);
|
|
const removeRecords = R.compose(
|
|
batchLazyTasks(BATCH_SIZE),
|
|
R.map(
|
|
R.cond([
|
|
[R.propEq("type", "URL"), removeRedirection],
|
|
[R.T, removeZoneRecord],
|
|
]),
|
|
),
|
|
R.sort((a, b) => b.id - a.id),
|
|
);
|
|
|
|
const updateHosts = async (hosts) => {
|
|
const remoteHostList = await getHosts();
|
|
const { add, remove } = diffRecords(remoteHostList, hosts);
|
|
console.log(`Adding ${add.length}; Removing ${remove.length}`);
|
|
|
|
await executeBatch([...removeRecords(remove), ...addRecords(add)]);
|
|
return { added: add.length, removed: remove.length };
|
|
};
|
|
|
|
return {
|
|
getHosts,
|
|
get: cpanel.zone.fetch,
|
|
addZoneRecord,
|
|
removeZoneRecord,
|
|
updateHosts,
|
|
};
|
|
};
|
|
|
|
const domainService = getDomainService({ cpanel });
|
|
|
|
module.exports = {
|
|
getDomainService,
|
|
domainService,
|
|
diffRecords,
|
|
};
|