diff --git a/Dockerfile b/Dockerfile index d5a4de6b7..990a6e0b2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,17 +5,18 @@ RUN ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime RUN apt-get -y update RUN apt-get install -y nodejs npm curl wget dnsutils certbot --fix-missing +RUN apt-get install -y unzip -RUN npm i -g n yarn && n 15.11 +RUN bash -c "curl -fsSL https://bun.sh/install | bash -s 'bun-v1.0.15'" -RUN node -v +RUN ~/.bun/bin/bun -v WORKDIR /opt/app -COPY yarn.lock . +COPY bun.lockb . COPY package.json . -RUN yarn install +RUN ~/.bun/bin/bun install CMD ["sh", "-c", "cp -r node_modules code; cd code; tail -f /dev/null"] diff --git a/domains/@.json b/domains/@.json index 56f1851e8..6fdb9372b 100644 --- a/domains/@.json +++ b/domains/@.json @@ -6,6 +6,8 @@ "email": "phenax5@gmail.com" }, "record": { - "URL": "http://www.is-a.dev" + "URL": "http://www.is-a.dev", + "MX": ["mail.is-a.dev"], + "TXT": ["v=spf1 ip4:51.161.54.164 include:mail.is-a.dev ~all"] } } diff --git a/domains/_discord.jirafey.json b/domains/_discord.jirafey.json new file mode 100644 index 000000000..363a429d5 --- /dev/null +++ b/domains/_discord.jirafey.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "jirafey", + "email": "jirafey@tuta.io" + }, + "record": { + "TXT": "dh=3c80d94ed181b223255b5fbf35f8fda6a402058f" + } +} diff --git a/domains/_github-challenge-is-a-dev-org.json b/domains/_github-challenge-is-a-dev-org.json new file mode 100644 index 000000000..db1e677a9 --- /dev/null +++ b/domains/_github-challenge-is-a-dev-org.json @@ -0,0 +1,10 @@ +{ + "owner": { + "username": "is-a-dev", + "email": "phenax5@gmail.com" + }, + + "record": { + "TXT": "2c9dfbe7c8" + } +} diff --git a/domains/_github-pages-challenge-is-a-dev.team.json b/domains/_github-pages-challenge-is-a-dev.team.json new file mode 100644 index 000000000..d7e139dbd --- /dev/null +++ b/domains/_github-pages-challenge-is-a-dev.team.json @@ -0,0 +1,10 @@ +{ + "owner": { + "username": "is-a-dev", + "email": "phenax5@gmail.com" + }, + + "record": { + "TXT": "c551c059fb167540ec0498d9011556" + } +} diff --git a/domains/a-j.json b/domains/a-j.json new file mode 100644 index 000000000..17469e2f2 --- /dev/null +++ b/domains/a-j.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "alijafari-gd", + "email": "ali.jafari.sn@gmail.com" + }, + "record": { + "URL": "https://alijafari.is-a.dev" + } +} diff --git a/domains/aayank.json b/domains/aayank.json new file mode 100644 index 000000000..269b5017e --- /dev/null +++ b/domains/aayank.json @@ -0,0 +1,14 @@ +{ + "owner": { + "username": "aayank13", + "email": "aayank1306@gmail.com" + }, + "record": { + "A": [ + "217.174.245.249", + "51.161.54.161" + ], + "MX": ["mail.is-a.dev"], + "TXT": "v=spf1 mx a:mail.is-a.dev ~all" + } +} \ No newline at end of file diff --git a/domains/alijafari.json b/domains/alijafari.json new file mode 100644 index 000000000..e638a8338 --- /dev/null +++ b/domains/alijafari.json @@ -0,0 +1,14 @@ +{ + "owner": { + "username": "alijafari-gd", + "email": "ali.jafari.sn@gmail.com" + }, + "record": { + "A": [ + "217.174.245.249", + "51.161.54.161" + ], + "MX": ["mail.is-a.dev"], + "TXT": "v=spf1 mx a:mail.is-a.dev ~all" + } +} \ No newline at end of file diff --git a/domains/blek.json b/domains/blek.json new file mode 100644 index 000000000..df17670cf --- /dev/null +++ b/domains/blek.json @@ -0,0 +1,11 @@ +{ + "description": "blek is a dev", + "owner": { + "username": "b1ek", + "email": "me@blek.codes" + }, + "record": { + "A": [ "185.130.226.115" ], + "AAAA": [ "2a05:b40:0:570:4563:92af:9a56:7fc8" ] + } +} diff --git a/domains/bot.dracx.json b/domains/bot.dracx.json new file mode 100644 index 000000000..68f770565 --- /dev/null +++ b/domains/bot.dracx.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "DarindaDraX", + "email": "123kishanvish@gmail.com" + }, + "record": { + "A": ["69.30.249.53"] + } +} diff --git a/domains/lavalink.trung.json b/domains/lavalink.trung.json new file mode 100644 index 000000000..c372c8070 --- /dev/null +++ b/domains/lavalink.trung.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "vuthanhtrung2010", + "email": "vuthanhtrungsuper@gmail.com" + }, + "record": { + "A": ["69.30.249.53"] + } +} diff --git a/domains/nik.json b/domains/nik.json index 36ee7b613..fae6c2cf1 100644 --- a/domains/nik.json +++ b/domains/nik.json @@ -1,6 +1,6 @@ { "owner": { - "username": "Nikhil", + "username": "nikxso", "email": "nikhilsoniya123@gmail.com", "discord": "nikxso#0000" }, diff --git a/domains/stefdp.json b/domains/stefdp.json index 56c2fc2fd..2882eface 100644 --- a/domains/stefdp.json +++ b/domains/stefdp.json @@ -12,7 +12,7 @@ "mail.is-a.dev" ], "TXT": [ - " v=spf1 mx a:mail.is-a.dev ip4:217.174.245.249 ~all" + "v=spf1 mx a:mail.is-a.dev ip4:217.174.245.249 ~all" ] } } diff --git a/scripts/certbot.sh b/scripts/certbot.sh index 2ab0447ae..ee84aa6d7 100755 --- a/scripts/certbot.sh +++ b/scripts/certbot.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -export ENV=production +export NODE_ENV=production DRY_RUN=0 if_dry_run() { [[ $DRY_RUN == 1 ]] && echo "$1" || echo "$2"; } @@ -32,7 +32,7 @@ update_record() { local name="$3"; local address="$4"; local ttl=${5:-"1"}; - echo " + bun -e " const { domainService } = require('./utils/domain-service'); const { ENV, DOMAIN_DOMAIN } = require('./utils/constants'); const method = '$method'; @@ -57,7 +57,7 @@ update_record() { } main().catch(console.error); - " | node - + " } update_acme_txt_record() { @@ -68,6 +68,7 @@ reset_acme() { sleep 1; update_record remove TXT '_acme-challenge' ''; update_record remove TXT '_acme-challenge' ''; + # update_record add CNAME 'www' "is-a-dev.github.io"; } get_acme() { dig +noall +answer _acme-challenge.is-a.dev TXT | awk '{print $5}'; } @@ -84,7 +85,6 @@ esac ### STEPS ### # Run ./scripts/certbot.sh cert -# Run ./scripts/certbot.sh acme_txt "" # cp -r /tmp/is-a-dev-whatever /opt/app/code/is-a-dev-cert # Upload cert.pem and privkey.pem (from config/live/is-a.dev/) contents to SSL > Manage SSL Sites # Run ./scripts/certbot.sh reset diff --git a/scripts/register-domains.js b/scripts/register-domains.js index 370949af6..20670f8af 100644 --- a/scripts/register-domains.js +++ b/scripts/register-domains.js @@ -15,7 +15,7 @@ const toHostList = R.chain(data => { // URL redirection must contain explicit A record // Wildcard A record breaks when used with MX // Ref: https://github.com/is-a-dev/register/issues/2365 - if (data.record.URL && data.record.MX) { + if ((data.record.URL && data.record.MX) || data.name === '@') { data.record.A = [ DOMAIN_HOST_IP ] } diff --git a/tests/cpanel.test.js b/tests/cpanel.test.js index d2fbd35a3..162a9a5d3 100644 --- a/tests/cpanel.test.js +++ b/tests/cpanel.test.js @@ -12,7 +12,7 @@ describe('Cpanel client', () => { describe('fetchzonerecords', () => { it('should make the correct request', async () => { const fetch = mockFetch((url, request) => { - expect(url).toBe('https://example.com:2000/json-api/cpanel?customonly=1&domain=a.b&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&cpanel_jsonapi_apiversion=2'); + expect(url).toBe('https://example.com:2000/json-api/cpanel?customonly=0&domain=a.b&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&cpanel_jsonapi_apiversion=2'); expect(request).toEqual({ headers: { Authorization: 'cpanel boy:boybyebye', @@ -35,7 +35,7 @@ describe('Cpanel client', () => { it('should make the correct request with query', async () => { const fetch = mockFetch((url, request) => { - expect(url).toBe('https://example.com:2000/json-api/cpanel?customonly=1&domain=foobar.boeey&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&cpanel_jsonapi_apiversion=2'); + expect(url).toBe('https://example.com:2000/json-api/cpanel?customonly=0&domain=foobar.boeey&cpanel_jsonapi_user=boy&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&cpanel_jsonapi_apiversion=2'); expect(request).toEqual({ headers: { Authorization: 'cpanel boy:boybyebye', diff --git a/tests/domain-service.test.js b/tests/domain-service.test.js index 79ea1d1e2..077aa4ea2 100644 --- a/tests/domain-service.test.js +++ b/tests/domain-service.test.js @@ -263,8 +263,8 @@ describe('Domain service', () => { ]); expect(removeZone).toHaveBeenCalledTimes(2); expect(getRecordCalls(removeZone)).toEqual([ - { line: 2 }, { line: 3 }, + { line: 2 }, ]); }); @@ -277,6 +277,9 @@ describe('Domain service', () => { { line: 5, name: 'c', type: 'MX', address: 'mx1.hello.com', priority: 20 }, { line: 6, name: 'c', type: 'MX', address: 'mx2.hello.com', priority: 21 }, { line: 7, name: 'b', type: 'MX', address: 'foo.bar', priority: 20 }, + { line: 101, name: 'x', type: 'A', address: '1' }, + { line: 99, name: 'y', type: 'A', address: '2' }, + { line: 100, name: 'z', type: 'A', address: '3' }, ]; const redirections = [ { domain: `b.${DOMAIN_DOMAIN}`, destination: 'https://foobar.com' }, @@ -306,8 +309,11 @@ describe('Domain service', () => { { name: 'b', type: 'A', address: '3' }, { name: 'd', type: 'CNAME', address: 'helo.com' }, ]); - expect(removeZone).toHaveBeenCalledTimes(1); + expect(removeZone).toHaveBeenCalledTimes(4); expect(getRecordCalls(removeZone)).toEqual([ + { line: 101 }, + { line: 100 }, + { line: 99 }, { line: 1 }, ]); @@ -317,8 +323,8 @@ describe('Domain service', () => { ]); expect(removeEmail).toHaveBeenCalledTimes(2); expect(getRecordCalls(removeEmail)).toEqual([ - { domain: 'c.is-a.dev', exchanger: 'mx1.hello.com', priority: 20 }, { domain: 'b.is-a.dev', exchanger: 'foo.bar', priority: 20 }, + { domain: 'c.is-a.dev', exchanger: 'mx1.hello.com', priority: 20 }, ]); expect(addRedir).toHaveBeenCalledTimes(3); diff --git a/utils/domain-service.js b/utils/domain-service.js index 81421f7aa..f5385cc2a 100644 --- a/utils/domain-service.js +++ b/utils/domain-service.js @@ -1,6 +1,6 @@ const R = require('ramda'); const { cpanel } = require('./lib/cpanel'); -const { DOMAIN_DOMAIN } = require('./constants'); +const { DOMAIN_DOMAIN, VALID_RECORD_TYPES } = require('./constants'); const { then, log, print, lazyTask, batchLazyTasks } = require('./helpers'); const BATCH_SIZE = 1; @@ -23,7 +23,7 @@ const recordToZone = ({ name, type, address, id, priority }) => ({ }); const cleanName = name => - name === DOMAIN_DOMAIN ? '@' : `${name}`.replace(new RegExp(`\\.${DOMAIN_DOMAIN}\\.?$`), '').toLowerCase(); + [DOMAIN_DOMAIN, `${DOMAIN_DOMAIN}.`].includes(name) ? '@' : `${name}`.replace(new RegExp(`\\.${DOMAIN_DOMAIN}\\.?$`), '').toLowerCase(); const zoneToRecord = ({ name, @@ -63,8 +63,7 @@ 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)); + const add = R.differenceWith(isMatchingRecord, newRecords, oldRecords); return { add, remove }; }; @@ -85,9 +84,20 @@ const executeBatch = (batches) => batches.reduce((promise, batch, index) => { }); }, Promise.resolve()); +const isReserved = (domain) => + domain.name.startsWith('*') || + !VALID_RECORD_TYPES.includes(domain.type) + 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 fetchZoneRecords = R.compose( + then(R.filter(R.complement(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'), @@ -120,15 +130,20 @@ const getDomainService = ({ cpanel }) => { 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 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 ] ])), + R.sort((a, b) => b.id - a.id) + ); const updateHosts = async hosts => { const remoteHostList = await getHosts(); diff --git a/utils/lib/cpanel.js b/utils/lib/cpanel.js index 5c098f281..e61b15a0f 100644 --- a/utils/lib/cpanel.js +++ b/utils/lib/cpanel.js @@ -37,7 +37,7 @@ const CpanelClient = (options) => { // -> [{ class, ttl, name, line, Line, cname, type, record }] fetch: R.compose( p => p.then(R.pathOr([], ['cpanelresult', 'data'])), - api2('ZoneEdit', 'fetchzone_records', { customonly: 1, domain: options.domain }) + api2('ZoneEdit', 'fetchzone_records', { customonly: 0, domain: options.domain }) ), // { name, type(A|CNAME), cname, address, ttl }