Files
is-a-dev/utils/domain-service.js
T
2021-01-05 20:05:14 +05:30

128 lines
4.0 KiB
JavaScript

const R = require('ramda');
const { cpanel } = require('./lib/cpanel');
const { DOMAIN_DOMAIN } = 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 }) => ({
line: id,
name,
type,
address,
...(type === 'CNAME' ? { cname: address } : {}),
...(type === 'TXT' ? { txtdata: address } : {}),
});
const cleanName = name => name === DOMAIN_DOMAIN ? '@' : `${name}`.replace(new RegExp(`\\.${DOMAIN_DOMAIN}\\.?$`), '').toLowerCase();
const zoneToRecord = ({ name, type, cname, address, record, line: id }) => ({
id,
name: cleanName(name),
type: `${type}`,
address: `${cname || address || record}`.replace(/\.$/g, '').toLowerCase(),
});
const redirectionToRecord = ({ domain, destination }) => ({
id: domain,
name: cleanName(domain),
type: 'URL',
address: `${destination}`.replace(/\/$/g, ''),
});
const getHostKey = host => `${host.name}##${host.type}##${host.address}`;
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)
.filter(r => !['www'].includes(r.name));
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(failed);
return null;
});
}, Promise.resolve());
const getDomainService = ({ cpanel }) => {
const fetchZoneRecords = R.compose(then(R.map(zoneToRecord)), cpanel.zone.fetch);
const fetchRedirections = R.compose(then(R.map(redirectionToRecord)), cpanel.redirection.fetch);
const addZoneRecord = lazyTask(R.compose(
cpanel.zone.add,
recordToZone,
print(({ name }) => `Adding zone for ${name}...`),
));
const removeZoneRecord = lazyTask(R.compose(
cpanel.zone.remove,
R.pick(['line']),
recordToZone,
print(({ name }) => `Deleting zone for ${name}...`),
));
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('name', 'www'), R.always(null) ], // Ignore www
[ 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 ],
])));
const updateHosts = async hosts => {
const remoteHostList = await getHosts();
const { add, remove } = diffRecords(remoteHostList, hosts);
await executeBatch([
...removeRecords(remove),
...addRecords(add),
]);
return { added: add.length, removed: remove.length };
};
return { getHosts, addZoneRecord, removeZoneRecord, updateHosts };
};
const domainService = getDomainService({ cpanel });
module.exports = {
getDomainService,
domainService,
diffRecords,
};