diff --git a/.github/workflows/validation.yml b/.github/workflows/validation.yml index 6d9efe54d..0da107478 100644 --- a/.github/workflows/validation.yml +++ b/.github/workflows/validation.yml @@ -9,7 +9,7 @@ on: - "domains/*" - "tests/*" - "utils/*" - - ".github/workflows/validate.yml" + - ".github/workflows/validation.yml" - "dnsconfig.js" workflow_dispatch: @@ -21,7 +21,6 @@ concurrency: jobs: dns: name: DNS - if: "!contains(github.event.head_commit.message, '[skip-ci]')" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -33,7 +32,6 @@ jobs: tests: name: Tests - if: "!contains(github.event.head_commit.message, '[skip-ci]')" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/README.md b/README.md index 5f54b53fb..6629d8c78 100644 --- a/README.md +++ b/README.md @@ -18,30 +18,27 @@ Please join our [Discord server](https://discord.gg/is-a-dev-830872854677422150) for announcements, updates & upgrades, and downtimes regarding the service. Not all of these will be posted on GitHub, however they will always be posted in our Discord server. -Discord Server +Discord Server # Register ### NS Records -When applying for NS records, please consider if you *actually* need them. Please provide a detailed reason (with evidence) of why you require NS records. - -9 times out of 10, you do not actually need NS records, and merely want them. We already support a wide range of records, so it is extremely unlikely you will actually require them. +When applying for NS records, please consider if you *actually* need them. +In your PR, please provide *extensive* reasoning, with evidence/examples, of why you need NS records. +You can see a good example of this [here](https://github.com/is-a-dev/register/pull/17592). ***Pull requests adding NS records without valid reasoning will be closed.*** **Instant Denials**: - I don't want to create a pull request everytime I want to update my DNS records. - - Over 3,000 people have to, you can too. -- I want to use Cloudflare's proxy for DDoS protection. - - Simply add `"proxied": true` to your file, to enable Cloudflare's Enterprise-level DDoS protection for your subdomain. - - Note: We *may* allow certain users to register NS records if it would expose their home IP. - -For any questions regarding NS records, feel free to email support@is-a.dev. + - Over 3,000 people have to create PRs for updating their records, you can too. +- It is easier to manage DNS records on *(insert DNS provider name)*. + - It isn't too difficult to open a pull request to update your DNS records. We aim for PRs to be merged with-in 24 hours. +- I want to use DDoS protection. + - You can simply add `"proxied": true` to your file, to enable Cloudflare's Enterprise-level DDoS protection for your subdomain. > [!NOTE] > You can skip these requirements by [supporting the service](https://wdh.gg/pvNCdvs) with a small amount of $2. > -> All donations go towards supporting the service's running costs. -> > If you decide to donate, please email william@is-a.dev with your payment confirmation and your pull request link to receive your NS records. ## Manual Registration diff --git a/domains/_discord.anirbaaaan.json b/domains/_discord.anirbaaaan.json new file mode 100644 index 000000000..f7a613430 --- /dev/null +++ b/domains/_discord.anirbaaaan.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "anirbaaaan183", + "email": "anirbanrc@proton.me" + }, + "record": { + "TXT": "dh=ef12f3d26e5f641e4a2f956c2a208b8da58b5c47" + } +} diff --git a/domains/_discord.sabbir.json b/domains/_discord.sabbir.json new file mode 100644 index 000000000..1ae6b2940 --- /dev/null +++ b/domains/_discord.sabbir.json @@ -0,0 +1,8 @@ +{ + "owner": { + "username": "s8rr" + }, + "record": { + "TXT": ["dh=02cb947f7961bbd1816cb3aa4d2d47854665515c"] + } +} diff --git a/domains/_discord.wizardsword.json b/domains/_discord.wizardsword.json new file mode 100644 index 000000000..e2b1afbb4 --- /dev/null +++ b/domains/_discord.wizardsword.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "MukuJhansi", + "email": "mukundagrawal800@gmail.com" + }, + "record": { + "TXT": "dh=4df0e55d42947236385030525829593c2e5cbd62" + } +} diff --git a/domains/_github-pages-challenge-anirbaaaan183.anirbaaaan.json b/domains/_github-pages-challenge-anirbaaaan183.anirbaaaan.json new file mode 100644 index 000000000..aef4a9302 --- /dev/null +++ b/domains/_github-pages-challenge-anirbaaaan183.anirbaaaan.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "anirbaaaan183", + "email": "anirbanrc@proton.me" + }, + "record": { + "TXT": "5f8c296170c0f2cd8aa1089b065900" + } +} diff --git a/domains/_github-pages-challenge-wizcann.wizcann.json b/domains/_github-pages-challenge-wizcann.wizcann.json new file mode 100644 index 000000000..f95b28284 --- /dev/null +++ b/domains/_github-pages-challenge-wizcann.wizcann.json @@ -0,0 +1,8 @@ +{ + "owner": { + "username": "wizcann" + }, + "record": { + "TXT": ["76bdb40edcf54ac449921a71e3e388"] + } +} diff --git a/domains/_vercel.kushdhingra.json b/domains/_vercel.kushdhingra.json new file mode 100644 index 000000000..350a577a1 --- /dev/null +++ b/domains/_vercel.kushdhingra.json @@ -0,0 +1,8 @@ +{ + "owner": { + "username": "kushdhingra" + }, + "record": { + "TXT": ["vc-domain-verify=kushdhingra.is-a.dev,a0343a97ff791537efcd"] + } +} diff --git a/domains/_vercel.shubh.json b/domains/_vercel.shubh.json new file mode 100644 index 000000000..79802a66f --- /dev/null +++ b/domains/_vercel.shubh.json @@ -0,0 +1,8 @@ +{ + "owner": { + "username": "R3tr0LastKnight" + }, + "record": { + "TXT": ["vc-domain-verify=shubh.is-a.dev,ec236054bf6268762e7e"] + } +} diff --git a/domains/_vercel.wizardsword.json b/domains/_vercel.wizardsword.json new file mode 100644 index 000000000..9d6d33c4a --- /dev/null +++ b/domains/_vercel.wizardsword.json @@ -0,0 +1,11 @@ +{ + "owner": { + "username": "MukuJhansi", + "discord": "1115658967012626542" + }, + "record": { + "TXT": [ + "vc-domain-verify=wizardsword.is-a.dev,bf203775c8ab97f0aa24" + ] + } +} diff --git a/domains/alan-huynh.json b/domains/alan-huynh.json new file mode 100644 index 000000000..660323ea1 --- /dev/null +++ b/domains/alan-huynh.json @@ -0,0 +1,11 @@ +{ + "owner": { + "username": "hdmquan", + "email": "hdmquan@gmail.com" + }, + "record": { + "A": [ + "75.2.60.5" + ] + } +} \ No newline at end of file diff --git a/domains/anhnguyen94.json b/domains/anhnguyen94.json new file mode 100644 index 000000000..498cfa301 --- /dev/null +++ b/domains/anhnguyen94.json @@ -0,0 +1,10 @@ +{ + "description": "Portfolio redirect for anhnguyen1907", + "owner": { + "username": "anhnguyen1907", + "email": "cau2tomy@gmail.com" + }, + "record": { + "CNAME": "anhnguyen1907.github.io" + } +} diff --git a/domains/anirbaaaan.json b/domains/anirbaaaan.json new file mode 100644 index 000000000..336288c99 --- /dev/null +++ b/domains/anirbaaaan.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "anirbaaaan183", + "email": "anirbanrc@proton.me" + }, + "record": { + "CNAME": "anirbaaaan183.github.io" + } +} diff --git a/domains/api-peantastic.json b/domains/api-peantastic.json new file mode 100644 index 000000000..97223312a --- /dev/null +++ b/domains/api-peantastic.json @@ -0,0 +1,11 @@ +{ + "owner": { + "username": "peantastic", + "email": "leostark.vndev@gmail.com" + }, + "description": "Hello, my name is Peantastic, and I am a dedicated developer with a passion for creating innovative solutions and enhancing user experiences. It's a pleasure to connect with you!", + "repo": "https://github.com/peantastic/", + "record": { + "CNAME": "tantinh.onrender.com" + } +} \ No newline at end of file diff --git a/domains/api.acronical.json b/domains/api.acronical.json new file mode 100644 index 000000000..335fb70a7 --- /dev/null +++ b/domains/api.acronical.json @@ -0,0 +1,11 @@ +{ + "owner": { + "username": "AcronicalYT", + "email": "acronicalbusiness@gmail.com" + }, + "record": { + "A": [ + "138.68.133.84" + ] + } +} diff --git a/domains/aynonyme.json b/domains/aynonyme.json index 7801d201a..a7e2709de 100644 --- a/domains/aynonyme.json +++ b/domains/aynonyme.json @@ -6,6 +6,6 @@ "email": "aynonyme@proton.me" }, "record": { - "CNAME": "ayn0nyme.github.io" + "A": ["93.95.117.183"] } } diff --git a/domains/blog.anirbaaaan.json b/domains/blog.anirbaaaan.json new file mode 100644 index 000000000..4651dcda5 --- /dev/null +++ b/domains/blog.anirbaaaan.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "anirbaaaan183", + "email": "anirbanrc@proton.me" + }, + "record": { + "URL": "https://anirbaaaan183.github.io/blog" + } +} diff --git a/domains/chez1s.json b/domains/chez1s.json new file mode 100644 index 000000000..b60527bfe --- /dev/null +++ b/domains/chez1s.json @@ -0,0 +1,11 @@ +{ + "description": "chez1s's is-a-dev domain", + "repo": "https://github.com/TRIBUI106", + "owner": { + "username": "TRIBUI106", + "discord": "1250225950268850312" + }, + "record": { + "URL": "https://yeume-enterprise.edu.vn" + } +} diff --git a/domains/chromesec.json b/domains/chromesec.json new file mode 100644 index 000000000..5554d73f5 --- /dev/null +++ b/domains/chromesec.json @@ -0,0 +1,12 @@ +{ + "description": "Website for ChromeSEC a guide for admins to secure their chromeOS devices", + "repo": "https://github.com/CaenJones/ChromeSEC", + "owner": { + "username": "CaenJones", + "email": "cj@caenjones.com", + "twitter": "" + }, + "record": { + "URL": "https://caenjones.is-a.dev/ChromeSEC" + } +} diff --git a/domains/dash.bernardonogueira8.json b/domains/dash.bernardonogueira8.json new file mode 100644 index 000000000..1b5d366c3 --- /dev/null +++ b/domains/dash.bernardonogueira8.json @@ -0,0 +1,11 @@ +{ + "owner": { + "username": "Samuraiflamesf", + "email": "Samuraiflamesf@gmail.com" + }, + "record": { + "A": [ + "100.42.189.124" + ] + } +} diff --git a/domains/dyikes.json b/domains/dyikes.json new file mode 100644 index 000000000..670fb7662 --- /dev/null +++ b/domains/dyikes.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "dyikes", + "email": "f@power.slmail.me" + }, + "record": { + "CNAME": "dyikes.duckdns.org" + } +} diff --git a/domains/erdogan.json b/domains/erdogan.json index 5120b3239..99c66a948 100644 --- a/domains/erdogan.json +++ b/domains/erdogan.json @@ -4,6 +4,6 @@ "email": "erdogan.sad@windowslive.com" }, "record": { - "A": ["194.87.246.134"] + "CNAME": "erdogans.space" } } diff --git a/domains/haolamnm.json b/domains/haolamnm.json new file mode 100644 index 000000000..3187a2d5e --- /dev/null +++ b/domains/haolamnm.json @@ -0,0 +1,10 @@ +{ + "description": "Portfolio redirect for haolamnm", + "owner": { + "username": "haolamnm", + "email": "haolamnm.work@gmail.com" + }, + "record": { + "CNAME": "haolamnm.github.io" + } +} diff --git a/domains/kushdhingra.json b/domains/kushdhingra.json index 755d91af3..94d0dfea3 100644 --- a/domains/kushdhingra.json +++ b/domains/kushdhingra.json @@ -1,6 +1,6 @@ { "owner": { - "username": "Kush-Dhingra", + "username": "kushdhingra", "email": "kushdhingra3112@gmail.com", "discord": "1035869217796403220", "OWL": "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBLU9BRVAiLCJraWQiOiJaa1VsRmRqVThiUEstLXVVM2JJR09PVHFYYVFFS1ZINFVXOW53MTR6WTJnIn0.gJTReSXxF-8BxDH8rn6i_DIx0XuVrc4yYnOEN1Awfp7mZcYPIgcPt4mvsScPbJYhZLN5XtDlxl0DcdU8Za12GCk-aIWegJ9-_32bdjw3agBzaJ-ltaslRoVpNJ3zfLaQ9V-BrhwVMEJhpz5-0eYeEiikL4tHAoPzzKMlYIf8f5l7IL6aMfhBZvIFf0B3upGg2FeOpFCXAsttU5QiKkjUt4BjetuYb29UJoNzDco1ZExLdudlKd_fz6IcaAsw2K1u6MMDDs2khsic9M42AE3pRFCP_hacdllY5Z66ATlmKhYAxFryJYWrgbfpGX2PxSrQalAao_IP1TIGgiq7ugiBDw.Lg-WIZSH_nzEfnLZct78Cg.soPtqjy32EA2Ew63dHQ8P8-dt4ZihvZpipBVkHyX_rlnv-y5YpS5v2-2d3O5hGHRfCKnfxQsvewDfcPSGa4_Oq8BN01Kzhbet8AXRCK6uOM.ouoa2YqHKAeX9lKWZoFaog" diff --git a/domains/ldt.json b/domains/ldt.json new file mode 100644 index 000000000..0a4eadf66 --- /dev/null +++ b/domains/ldt.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "luongductrong", + "email": "luongductrong2004@gmail.com" + }, + "record": { + "CNAME": "luongductrong.github.io" + } +} diff --git a/domains/minitor.json b/domains/minitor.json new file mode 100644 index 000000000..e10eeb8af --- /dev/null +++ b/domains/minitor.json @@ -0,0 +1,11 @@ +{ + "description": "Self hosted service", + "repo": "https://github.com/MinitorMHS/register", + "owner": { + "username": "minitormhs", + "email": "me@minitor.eu.org" + }, + "record": { + "CNAME": "cf-repl.pages.dev" + } +} diff --git a/domains/mod.s4il.json b/domains/mod.s4il.json new file mode 100644 index 000000000..6d450044b --- /dev/null +++ b/domains/mod.s4il.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "S4IL21", + "email": "s4il.is.a.dev@gmail.com" + }, + "record": { + "CNAME": "s4il21.github.io" + } +} diff --git a/domains/needrose.json b/domains/needrose.json new file mode 100644 index 000000000..525238ca6 --- /dev/null +++ b/domains/needrose.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "Needrose", + "email": "neeedrose@gmail.com" + }, + "record": { + "A": ["155.248.226.206"] + } +} diff --git a/domains/nh.json b/domains/nh.json new file mode 100644 index 000000000..9a5666540 --- /dev/null +++ b/domains/nh.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "nonxistnt", + "email": "khtyiscooler@gmail.com" + }, + "record": { + "CNAME": "nonxistnt.github.io" + } +} diff --git a/domains/phamthanhnghia.json b/domains/phamthanhnghia.json new file mode 100644 index 000000000..2378ce50d --- /dev/null +++ b/domains/phamthanhnghia.json @@ -0,0 +1,11 @@ +{ + "description": "phamthanhnghia.is-a.dev", + "repo": "https://github.com/phamthanhnghia/phamthanhnghia.github.io", + "owner": { + "username": "phamthanhnghia", + "email": "phamthanhnghia.it@gmail.com" + }, + "record": { + "CNAME": "phamthanhnghia.github.io" + } +} diff --git a/domains/proficode.json b/domains/proficode.json new file mode 100644 index 000000000..ab1555bf5 --- /dev/null +++ b/domains/proficode.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "profic0de", + "email": "anonymou3489@gmail.com" + }, + "record": { + "CNAME": "239581.site.bot-hosting.net" + } +} diff --git a/domains/redirect.akk1to.json b/domains/redirect.akk1to.json new file mode 100644 index 000000000..2b819373c --- /dev/null +++ b/domains/redirect.akk1to.json @@ -0,0 +1,12 @@ +{ + "description": "This website is for redirect server", + "repo": "https://github.com/akk1to/redirect", + "owner": { + "username": "akk1to", + "email": "akk1to.dev@gmail.com", + "discord": "727497287777124414" + }, + "record": { + "CNAME": "akk1to.github.io" + } +} diff --git a/domains/redolent.json b/domains/redolent.json new file mode 100644 index 000000000..009222c74 --- /dev/null +++ b/domains/redolent.json @@ -0,0 +1,12 @@ +{ + "owner": { + "username": "RedolentHalo", + "discord": "755718159641608202", + "OWL": "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBLU9BRVAiLCJraWQiOiJaa1VsRmRqVThiUEstLXVVM2JJR09PVHFYYVFFS1ZINFVXOW53MTR6WTJnIn0.c0d7ul7E-LX1qEJeFPuXgcpGJvLDeqnotDBs20mJdeqQzpzoMFTGzEYQ3QExt5XVjdX9SzRFpdgGaY04gN_c_Nrx7WaG47ZJL1rNAapJYCUGmysp3W6g2Trwi45_cIaWUEwKGnjBIgp3BTLKO6yentnHJXKjMb5AUkDcZVj69Nly4sCpdUTSjj1Xw6hVtmapS-lGaIlm6iaftk1pEJm1q1uIuDSsrufH2oRF3lVwkmjJDIwD_tf7PXuHRUGgxvt0FK_bLZKnoqbFtpTh9nNg_RBf8H1YnzHhOc6dkjf6ikBBzcQDutiKxelMupFmatlU5eAGCBmKkEUwxtSKsp5jQQ.R8WtRKhJLz0P5qUPEEdXZg.oEOJF1FocJn0FqQ1P0RaKVK4G1RoQT6E3oyRUpMg_lv0UXDwn16BlIUcKowUWxiObofg1YKRgB1OM4uIZPjxbAYJwkS-Q4Hf0CTmeZCjCaVpaeNc20Z9dREuphe4He30.jP6ZbMY8WRHngmBV6kRBsg" + }, + + "record": { + "CNAME": "redolent.pages.dev" + } + } + \ No newline at end of file diff --git a/domains/riskyprsty.json b/domains/riskyprsty.json new file mode 100644 index 000000000..a79f3f3e4 --- /dev/null +++ b/domains/riskyprsty.json @@ -0,0 +1,8 @@ +{ + "owner": { + "username": "riskyprsty" + }, + "record": { + "CNAME": "riskyprsty.github.io" + } +} diff --git a/domains/sabbir.json b/domains/sabbir.json index 9f02d817e..2c4dd5094 100644 --- a/domains/sabbir.json +++ b/domains/sabbir.json @@ -4,6 +4,6 @@ "email": "sabbir.apbn.edu@gmail.com" }, "record": { - "CNAME": "sabbir1.pages.dev" + "NS": ["clarissa.ns.cloudflare.com", "harley.ns.cloudflare.com"] } } diff --git a/domains/saftec.json b/domains/saftec.json new file mode 100644 index 000000000..1b5d366c3 --- /dev/null +++ b/domains/saftec.json @@ -0,0 +1,11 @@ +{ + "owner": { + "username": "Samuraiflamesf", + "email": "Samuraiflamesf@gmail.com" + }, + "record": { + "A": [ + "100.42.189.124" + ] + } +} diff --git a/domains/shubh.json b/domains/shubh.json index 2b950ba61..b4321f5ef 100644 --- a/domains/shubh.json +++ b/domains/shubh.json @@ -1,12 +1,8 @@ { - "description": "personal portfolio website", - "repo": "https://github.com/R3tr0LastKnight/Portfoio_v2.git", "owner": { - "username": "R3tr0LastKnight", - "email": "r3tr0lastknight@gmail.com", - "twitter": "R3tr0LastKnight" + "username": "R3tr0LastKnight" }, "record": { - "CNAME": "r3tr0lastknight.github.io" + "CNAME": "portfoio-v2.vercel.app" } } diff --git a/domains/somaiyalbakhet.json b/domains/somaiyalbakhet.json new file mode 100644 index 000000000..2e4f4b519 --- /dev/null +++ b/domains/somaiyalbakhet.json @@ -0,0 +1,10 @@ +{ + "description": "my portfolio website on Netlify", + "owner": { + "username": "Somaiya-XI", + "discord": "Sen!#0577" + }, + "record": { + "A": ["75.2.60.5"] + } +} diff --git a/domains/tan.json b/domains/tan.json new file mode 100644 index 000000000..85ecae11e --- /dev/null +++ b/domains/tan.json @@ -0,0 +1,10 @@ +{ + "description": "tan.is-a.dev", + "owner": { + "username": "cuno92", + "email": "hoangvannhattan@gmail.com" + }, + "record": { + "CNAME": "white-hill-030c00b00.4.azurestaticapps.net" + } +} diff --git a/domains/tortilla.json b/domains/tortilla.json new file mode 100644 index 000000000..977c39694 --- /dev/null +++ b/domains/tortilla.json @@ -0,0 +1,11 @@ +{ + "description": "tortilla.is-a.dev", + "owner": { + "username": "Colbster937", + "email": "colbster937@colbster937.dev" + }, + "record": { + "CNAME": "dns.tortillagames.org" + }, + "proxied": true +} diff --git a/domains/trungz.json b/domains/trungz.json new file mode 100644 index 000000000..911383c15 --- /dev/null +++ b/domains/trungz.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "lehuutrung1412", + "email": "me@lehuutrung.dev" + }, + "record": { + "URL": "https://lehuutrung.dev" + } +} diff --git a/domains/tsu.json b/domains/tsu.json new file mode 100644 index 000000000..29c62defb --- /dev/null +++ b/domains/tsu.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "dot-tsu", + "email": "lucanahtsu@gmail.com" + }, + "record": { + "CNAME": "dot-tsu.github.io" + } +} diff --git a/domains/tweets.clyron.json b/domains/tweets.clyron.json new file mode 100644 index 000000000..284409427 --- /dev/null +++ b/domains/tweets.clyron.json @@ -0,0 +1,11 @@ +{ + "description": "Personal website of Clyron", + "repo": "https://github.com/theclyron/theclyron.github.io", + "owner": { + "username": "theclyron", + "email": "onenonlyclyron@gmail.com" + }, + "record": { + "CNAME": "theclyron.github.io" + } +} diff --git a/domains/vasanth.json b/domains/vasanth.json new file mode 100644 index 000000000..07f5aa342 --- /dev/null +++ b/domains/vasanth.json @@ -0,0 +1,13 @@ +{ + "description": "Vasanth personal Blog", + "repo": "https://github.com/itsparser", + "owner": { + "username": "62d2", + "email": "itsparser@gmail.com", + "twitter": "itsparser", + "discord": "itsparser" + }, + "record": { + "CNAME": "itsparser.github.io" + } +} diff --git a/domains/vdevanarayan.json b/domains/vdevanarayan.json new file mode 100644 index 000000000..91adbc61a --- /dev/null +++ b/domains/vdevanarayan.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "CodeTitan7", + "email": "octavius1983bc@gmail.com" + }, + "record": { + "URL": "https://vdevanarayan.vercel.app" + } +} diff --git a/domains/vurihuang.json b/domains/vurihuang.json new file mode 100644 index 000000000..fc7d3838f --- /dev/null +++ b/domains/vurihuang.json @@ -0,0 +1,9 @@ +{ + "owner": { + "username": "vurihuang", + "email": "vengeancehuang@gmail.com" + }, + "record": { + "CNAME": "vurihuang.github.io" + } +} diff --git a/domains/wizardsword.json b/domains/wizardsword.json index 2d669e7e2..a8bf76bee 100644 --- a/domains/wizardsword.json +++ b/domains/wizardsword.json @@ -4,6 +4,6 @@ "discord": "1115658967012626542" }, "record": { - "A": ["216.24.57.1"] + "A": ["76.76.21.21"] } } diff --git a/domains/wizcann.json b/domains/wizcann.json new file mode 100644 index 000000000..cc4df51b5 --- /dev/null +++ b/domains/wizcann.json @@ -0,0 +1,8 @@ +{ + "owner": { + "username": "wizcann" + }, + "record": { + "CNAME": "wizcann.github.io" + } +} diff --git a/domains/youssef-lmouden.json b/domains/youssef-lmouden.json new file mode 100644 index 000000000..62c8bf2dc --- /dev/null +++ b/domains/youssef-lmouden.json @@ -0,0 +1,8 @@ +{ + "owner": { + "username": "YoussefDevPro" + }, + "record": { + "CNAME": "youssefdevpro.github.io" + } +} diff --git a/domains/zalex.json b/domains/zalex.json new file mode 100644 index 000000000..1fafd707a --- /dev/null +++ b/domains/zalex.json @@ -0,0 +1,12 @@ +{ + "owner": { + "username": "Orange-Figure", + "discord": "1212435420776890368", + "OWL": "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBLU9BRVAiLCJraWQiOiJaa1VsRmRqVThiUEstLXVVM2JJR09PVHFYYVFFS1ZINFVXOW53MTR6WTJnIn0.N0_TVOrIKQHS-dgU5KDDcREwJDAtW1AfGLARAqAZyT-UjrmJz8IUFHg8zYxe6Spa4faQVR_emyVZKwqwlFvdIZZr4yQrWQSED-2WZJ5Z5DXpti6930sHO4CVgszWKjc5LqPW1764QP39iQrC7VtNSFxEmZTWJnmK0i8X_2AOSgu8BTH45vjMVKU4oBRzHjvoBZ-Z_17DCk0Awy2FxWSscZPDVF1-_1ETfQ60Ahcv8gYcMt3ZVs2vrUZR_sopt8LaPf6AQQ9zSgDtRiwBHogpiYWsAZVN_5EHTKosomGhUcxuHfUjhaHFauscXQjHZM6kDOEAEvY8MryV7jRy_gm7fQ.sZCseMTkxIHnGcrpk7SARA.SHzeybFf2FVb38kuLc62-X3zAK2SMpZiL2FlnaU77-UR5bsGoINyEnbJNdvCRd4nLItafWpWtCqu0NyvjBh8BWCNrCUhYMmmLRzg6sBgolI.kE2nsD9RtiZGMS-KFaXmZw" + }, + + "record": { + "CNAME": "zalex.betteruptime.com" + } + } + \ No newline at end of file diff --git a/tests/domains.test.js b/tests/domains.test.js index fa8988590..071f2c534 100644 --- a/tests/domains.test.js +++ b/tests/domains.test.js @@ -5,49 +5,73 @@ const path = require("path"); const domainsPath = path.resolve("domains"); const files = fs.readdirSync(domainsPath); +function getParentSubdomain(subdomain) { + const parts = subdomain.split("."); + + if (parts.length <= 1) return null; // No parent for top-level subdomains + + // Attempt to find the parent subdomain by removing the last part + for (let i = parts.length - 1; i > 0; i--) { + const potentialParent = parts.slice(i - 1).join("."); + if (files.includes(`${potentialParent}.json`)) { + return potentialParent; // Return the parent subdomain if it exists + } + } + + return null; // Return null if no valid parent is found +} + + +function getDomainData(subdomain) { + try { + return fs.readJsonSync(path.join(domainsPath, `${subdomain}.json`)); + } catch (error) { + throw new Error(`Failed to read JSON for ${subdomain}: ${error.message}`); + } +} + t("Nested subdomains should not exist without a parent subdomain", (t) => { - files.forEach((file) => { + for (const file of files) { const subdomain = file.replace(/\.json$/, ""); if (subdomain.split(".").length > 1) { - const parentSubdomain = subdomain.split(".").pop(); - + const parentSubdomain = getParentSubdomain(subdomain); t.true(files.includes(`${parentSubdomain}.json`), `${file}: Parent subdomain does not exist`); } - }); + } t.pass(); }); t("Nested subdomains should not exist if the parent subdomain has NS records", (t) => { - files.forEach((file) => { + for (const file of files) { const subdomain = file.replace(/\.json$/, ""); if (subdomain.split(".").length > 1) { - const parentSubdomain = subdomain.split(".").pop(); - const parentDomain = fs.readJsonSync(path.join(domainsPath, `${parentSubdomain}.json`)); + const parentSubdomain = getParentSubdomain(subdomain); + const parentDomain = getDomainData(parentSubdomain); - t.is(parentDomain.record.NS, undefined, `${file}: Parent subdomain has NS records`); + t.true(!parentDomain.record.NS, `${file}: Parent subdomain has NS records`); } - }); + } t.pass(); }); t("Nested subdomains should be owned by the parent subdomain's owner", (t) => { - files.forEach((file) => { - const subdomain = file.replace(/\.json$/, ""); + for (const file of files) { + const subdomain = file.replace(/\.json$/, ""); if (subdomain.split(".").length > 1) { - const data = fs.readJsonSync(path.join(domainsPath, file)); + const data = getDomainData(subdomain); - const parentSubdomain = subdomain.split(".").pop(); - const parentDomain = fs.readJsonSync(path.join(domainsPath, `${parentSubdomain}.json`)); + const parentSubdomain = getParentSubdomain(subdomain); + const parentDomain = getDomainData(parentSubdomain); t.true( data.owner.username.toLowerCase() === parentDomain.owner.username.toLowerCase(), - `${file}: owner.username is not the same as the parent subdomain` + `${file}: Owner does not match the parent subdomain` ); } - }); + } }); diff --git a/tests/json.test.js b/tests/json.test.js index 1a737900e..1bf3e3f3d 100644 --- a/tests/json.test.js +++ b/tests/json.test.js @@ -59,8 +59,9 @@ t("All files should have valid file names", (t) => { // Ignore root domain if (file !== "@.json") { + const subdomain = file.replace(/\.json$/, ""); t.regex( - file.replace(/\.json$/, "") + ".is-a.dev", + subdomain + ".is-a.dev", hostnameRegex, `${file}: FQDN must be 1-253 characters, use letters, numbers, dots, or hyphens, and not start or end with a hyphen.` ); @@ -72,9 +73,13 @@ t("All files should have the required fields", (t) => { files.forEach((file) => { const data = fs.readJsonSync(path.join(domainsPath, file)); + // Validate top-level required fields validateRequiredFields(t, data, requiredFields, file); + + // Validate owner object fields validateRequiredFields(t, data.owner, requiredOwnerFields, file); + // Ensure 'record' field is not empty unless reserved if (!data.reserved) { t.true(Object.keys(data.record).length > 0, `${file}: No record types found`); } @@ -85,11 +90,24 @@ t("All files should have valid optional fields", (t) => { files.forEach((file) => { const data = fs.readJsonSync(path.join(domainsPath, file)); + // Validate optional fields at top level validateOptionalFields(t, data, optionalFields, file); + + // Validate optional fields for owner object validateOptionalFields(t, data.owner, optionalOwnerFields, file); + // Email validation (if provided) if (data.owner.email) { t.regex(data.owner.email, emailRegex, `${file}: Owner email should be a valid email address`); } }); }); + +const ignoredJSONFiles = ["package-lock.json", "package.json"]; + +t("JSON files should not be in the root directory", (t) => { + const rootFiles = fs + .readdirSync(path.resolve()) + .filter((file) => file.endsWith(".json") && !ignoredJSONFiles.includes(file)); + t.is(rootFiles.length, 0, "JSON files should not be in the root directory"); +}); diff --git a/tests/proxy.test.js b/tests/proxy.test.js index 7e8299c70..2bb64b48c 100644 --- a/tests/proxy.test.js +++ b/tests/proxy.test.js @@ -2,21 +2,27 @@ const t = require("ava"); const fs = require("fs-extra"); const path = require("path"); -const requiredRecordsToProxy = ["A", "AAAA", "CNAME"]; +const requiredRecordsToProxy = new Set(["A", "AAAA", "CNAME"]); // URL records are not listed here because they are proxied by default, so they don't need the proxied flag function validateProxiedRecords(t, data, file) { - if (data.proxied) { - const hasProxiedRecord = Object.keys(data.record).some((key) => requiredRecordsToProxy.includes(key)); + // Convert the Set to an array for message display + const recordTypes = Array.from(requiredRecordsToProxy).join(", "); - t.true(hasProxiedRecord, `${file}: Proxied is true but there are no records that can be proxied`); + if (data.proxied) { + const hasProxiedRecord = Object.keys(data.record).some((key) => requiredRecordsToProxy.has(key)); + + t.true( + hasProxiedRecord, + `${file}: Proxied is true but there are no records that can be proxied (${recordTypes} expected)` + ); } } const domainsPath = path.resolve("domains"); -const files = fs.readdirSync(domainsPath); +const files = fs.readdirSync(domainsPath).filter((file) => file.endsWith(".json")); -t("Domains with proxy enabled should have have at least one record that can be proxied", (t) => { +t("Domains with proxy enabled should have at least one record that can be proxied", (t) => { files.forEach((file) => { const domain = fs.readJsonSync(path.join(domainsPath, file)); diff --git a/tests/records.test.js b/tests/records.test.js index 2970b629e..d819fa9b9 100644 --- a/tests/records.test.js +++ b/tests/records.test.js @@ -2,7 +2,7 @@ const t = require("ava"); const fs = require("fs-extra"); const path = require("path"); -const validRecordTypes = ["A", "AAAA", "CAA", "CNAME", "DS", "MX", "NS", "SRV", "TXT", "URL"]; +const validRecordTypes = new Set(["A", "AAAA", "CAA", "CNAME", "DS", "MX", "NS", "SRV", "TXT", "URL"]); const hostnameRegex = /^(?=.{1,253}$)(?:(?:[_a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)\.)+[a-zA-Z]{2,63}$/; const ipv4Regex = /^(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}$/; @@ -13,17 +13,14 @@ const domainsPath = path.resolve("domains"); const files = fs.readdirSync(domainsPath); function expandIPv6(ip) { - // Split into segments by ":" let segments = ip.split(":"); - // Count the number of segments that are empty due to "::" shorthand const emptyIndex = segments.indexOf(""); + if (emptyIndex !== -1) { - // Calculate how many "0000" segments are missing const nonEmptySegments = segments.filter((seg) => seg !== ""); const missingSegments = 8 - nonEmptySegments.length; - // Insert the missing "0000" segments into the position of the empty segment segments = [ ...nonEmptySegments.slice(0, emptyIndex), ...Array(missingSegments).fill("0000"), @@ -31,76 +28,66 @@ function expandIPv6(ip) { ]; } - // Expand each segment to 4 characters, padding with leading zeros - const expandedSegments = segments.map((segment) => segment.padStart(4, "0")); - - // Join the segments back together - return expandedSegments.join(":"); + return segments.map((segment) => segment.padStart(4, "0")).join(":"); } -function isPublicIPv4(ip, proxied) { +function validateIPv4(ip, proxied, file, index) { const parts = ip.split(".").map(Number); - // Validate IPv4 address format - if (parts.length !== 4 || parts.some((part) => isNaN(part) || part < 0 || part > 255)) { - return false; - } + if (parts.length !== 4 || parts.some((part) => isNaN(part) || part < 0 || part > 255)) return false; + if (ip === "192.0.2.1" && proxied) return true; - // Exception for 192.0.2.1, assuming the domain is proxied - if (ip === "192.0.2.1" && proxied) { - return true; - } - - // Check for private and reserved IPv4 ranges return !( - // Private ranges - ( - parts[0] === 10 || - (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) || - (parts[0] === 192 && parts[1] === 168) || - // Reserved or special-use ranges - (parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127) || // Carrier-grade NAT - (parts[0] === 169 && parts[1] === 254) || // Link-local - (parts[0] === 192 && parts[1] === 0 && parts[2] === 0) || // IETF Protocol Assignments - (parts[0] === 192 && parts[1] === 0 && parts[2] === 2) || // Documentation (TEST-NET-1) - (parts[0] === 198 && parts[1] === 18) || // Network Interconnect Devices - (parts[0] === 198 && parts[1] === 51 && parts[2] === 100) || // Documentation (TEST-NET-2) - (parts[0] === 203 && parts[1] === 0 && parts[2] === 113) || // Documentation (TEST-NET-3) - parts[0] >= 224 - ) // Multicast and reserved ranges + parts[0] === 10 || + (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) || + (parts[0] === 192 && parts[1] === 168) || + (parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127) || + (parts[0] === 169 && parts[1] === 254) || + (parts[0] === 192 && parts[1] === 0 && parts[2] === 0) || + (parts[0] === 192 && parts[1] === 0 && parts[2] === 2) || + (parts[0] === 198 && parts[1] === 18) || + (parts[0] === 198 && parts[1] === 51 && parts[2] === 100) || + (parts[0] === 203 && parts[1] === 0 && parts[2] === 113) || + parts[0] >= 224 ); } -function isPublicIPv6(ip) { - const normalizedIP = ip.toLowerCase(); - - // Check for private or special-use IPv6 ranges +function validateIPv6(ip) { return !( - ( - normalizedIP.startsWith("fc") || // Unique Local Address (ULA) - normalizedIP.startsWith("fd") || // Unique Local Address (ULA) - normalizedIP.startsWith("fe80") || // Link-local - normalizedIP.startsWith("::1") || // Loopback address (::1) - normalizedIP.startsWith("2001:db8") - ) // Documentation range + ip.toLowerCase().startsWith("fc") || + ip.toLowerCase().startsWith("fd") || + ip.toLowerCase().startsWith("fe80") || + ip.toLowerCase().startsWith("::1") || + ip.toLowerCase().startsWith("2001:db8") ); } +function validateRecordType(recordType) { + return validRecordTypes.has(recordType); +} + +function isValidHostname(hostname) { + return hostnameRegex.test(hostname); +} + +function isValidHexadecimal(value) { + return /^[0-9a-fA-F]+$/.test(value); +} + t("All files should have valid record types", (t) => { files.forEach((file) => { const data = fs.readJsonSync(path.join(domainsPath, file)); const recordKeys = Object.keys(data.record); recordKeys.forEach((key) => { - t.true(validRecordTypes.includes(key), `${file}: Invalid record type: ${key}`); + t.true(validateRecordType(key), `${file}: Invalid record type: ${key}`); }); - // CNAME records cannot be combined with any other record type unless it is proxied + // Specific record rules for CNAME, NS, and DS if (recordKeys.includes("CNAME") && !data.proxied) { - t.is(recordKeys.length, Number(1), `${file}: CNAME records cannot be combined with other records`); + t.is(recordKeys.length, 1, `${file}: CNAME records cannot be combined with other records unless proxied`); } - // NS records cannot be combined with any other record type, except for DS records if (recordKeys.includes("NS")) { t.true( recordKeys.length === 1 || (recordKeys.length === 2 && recordKeys.includes("DS")), @@ -108,7 +95,6 @@ t("All files should have valid record types", (t) => { ); } - // DS records must be combined with NS records if (recordKeys.includes("DS")) { t.true(recordKeys.includes("NS"), `${file}: DS records must be combined with NS records`); } @@ -118,7 +104,6 @@ t("All files should have valid record types", (t) => { t("All files should not have duplicate record keys", (t) => { files.forEach((file) => { const data = fs.readJsonSync(path.join(domainsPath, file)); - const recordKeys = Object.keys(data.record); const uniqueRecordKeys = new Set(recordKeys); @@ -132,230 +117,95 @@ t("All files should have valid record values", (t) => { Object.keys(data.record).forEach((key) => { const value = data.record[key]; + const subdomain = file.replace(/\.json$/, ""); // Get the subdomain from the filename - // *: string[] + // Validate A, AAAA, MX, NS records: Array of strings if (["A", "AAAA", "MX", "NS"].includes(key)) { - t.true(Array.isArray(value), `${file}: Record value should be an array for ${key}`); + t.true(Array.isArray(value), `${file}: Record value for ${key} should be an array`); - value.forEach((record) => { + value.forEach((record, idx) => { t.true( typeof record === "string", - `${file}: Record value should be a string for ${key} at index ${value.indexOf(record)}` + `${file}: Record value for ${key} should be a string at index ${idx}` ); - }); - - // A: string[] - if (key === "A") { - value.forEach((record) => { - t.regex( - record, - ipv4Regex, - `${file}: Record value should be a valid IPv4 address for ${key} at index ${value.indexOf( - record - )}` - ); + if (key === "A") { + t.regex(record, ipv4Regex, `${file}: Invalid IPv4 address for ${key} at index ${idx}`); t.true( - isPublicIPv4(record, data.proxied), - `${file}: Record value should be a public IPv4 address for ${key} at index ${value.indexOf( - record - )}` + validateIPv4(record, data.proxied, file, idx), + `${file}: Invalid IPv4 address for ${key} at index ${idx}` ); - }); - } + } - // AAAA: string[] - if (key === "AAAA") { - value.forEach((record) => { + if (key === "AAAA") { t.regex( expandIPv6(record), ipv6Regex, - `${file}: Record value should be a valid IPv6 address for ${key} at index ${value.indexOf( - record - )}` + `${file}: Invalid IPv6 address for ${key} at index ${idx}` ); + t.true(validateIPv6(record), `${file}: Invalid IPv6 address for ${key} at index ${idx}`); + } - t.true( - isPublicIPv6(record), - `${file}: Record value should be a public IPv6 address for ${key} at index ${value.indexOf( - record - )}` - ); - }); - } - - // *: string[] - if (["MX", "NS"].includes(key)) { - value.forEach((record) => { - t.regex( - record, - hostnameRegex, - `${file}: Record value should be a valid hostname for ${key} at index ${value.indexOf( - record - )}` - ); - }); - } + if (["MX", "NS"].includes(key)) { + t.true(isValidHostname(record), `${file}: Invalid hostname for ${key} at index ${idx}`); + } + }); } - // *: string + // Validate CNAME and URL records: Single string if (["CNAME", "URL"].includes(key)) { - t.true(typeof value === "string", `${file}: Record value should be a string for ${key}`); + t.true(typeof value === "string", `${file}: Record value for ${key} should be a string`); if (key === "CNAME") { - t.regex(value, hostnameRegex, `${file}: Record value should be a valid hostname for ${key}`); - - if(file === "@.json") { - t.false( - value === "is-a.dev", - `${file}: Record value should not reference itself for ${key}` - ); - } else { - t.false( - value === file.replace(/\.json$/, "") + ".is-a.dev", - `${file}: Record value should not reference itself for ${key}` - ); + t.true(isValidHostname(value), `${file}: Invalid hostname for ${key}`); + t.true(value !== file, `${file}: CNAME cannot point to itself`); + if (file === "@.json") { + t.true(value !== "is-a.dev", `${file}: CNAME cannot point to itself`); } } if (key === "URL") { - t.notThrows(() => new URL(value), `${file}: Record value should be a valid URL for ${key}`); - - if(file === "@.json") { - t.false( - value === "http://is-a.dev", - `${file}: Record value should not reference itself for ${key}` - ); - t.false( - value === "https://is-a.dev", - `${file}: Record value should not reference itself for ${key}` - ); - } else { - t.false( - value === "http://" + file.replace(/\.json$/, "") + ".is-a.dev", - `${file}: Record value should not reference itself for ${key}` - ); - t.false( - value === "https://" + file.replace(/\.json$/, "") + ".is-a.dev", - `${file}: Record value should not reference itself for ${key}` - ); + t.notThrows(() => new URL(value), `${file}: Invalid URL for ${key}`); + try { + const urlObj = new URL(value); + t.true(urlObj.hostname !== subdomain, `${file}: URL cannot point to itself`); + } catch { + t.fail(`${file}: Invalid URL for ${key}`); } } } - // *: {}[] + // Validate CAA, DS, SRV records: Array of objects if (["CAA", "DS", "SRV"].includes(key)) { - t.true(Array.isArray(value), `${file}: Record value should be an array for ${key}`); + t.true(Array.isArray(value), `${file}: Record value for ${key} should be an array`); - value.forEach((record) => { + value.forEach((record, idx) => { t.true( typeof record === "object", - `${file}: Record value should be an object for ${key} at index ${value.indexOf(record)}` + `${file}: Record value for ${key} should be an object at index ${idx}` ); + + if (key === "DS") { + t.true( + Number.isInteger(record.key_tag) && record.key_tag >= 0 && record.key_tag <= 65535, + `${file}: Invalid key_tag for DS at index ${idx}` + ); + t.true(isValidHexadecimal(record.digest), `${file}: Invalid digest for DS at index ${idx}`); + } }); - - // CAA: { flags: number, tag: string, value: string }[] - if (key === "CAA") { - value.forEach((record) => { - t.true( - typeof record.flags === "number", - `${file}: CAA record value should have a number for flags at index ${value.indexOf(record)}` - ); - - t.true( - typeof record.tag === "string", - `${file}: CAA record value should have a string for tag at index ${value.indexOf(record)}` - ); - - t.true( - typeof record.value === "string", - `${file}: CAA record value should have a string for value at index ${value.indexOf(record)}` - ); - }); - } - - // DS: { key_tag: number, algorithm: number, digest_type: number, digest: string }[] - if (key === "DS") { - value.forEach((record) => { - t.true( - typeof record.key_tag === "number", - `${file}: DS record value should have a number for key_tag at index ${value.indexOf( - record - )}` - ); - - t.true( - typeof record.algorithm === "number", - `${file}: DS record value should have a number for algorithm at index ${value.indexOf( - record - )}` - ); - - t.true( - typeof record.digest_type === "number", - `${file}: DS record value should have a number for digest_type at index ${value.indexOf( - record - )}` - ); - - t.true( - typeof record.digest === "string", - `${file}: DS record value should have a string for digest at index ${value.indexOf(record)}` - ); - }); - } - - // SRV: { priority: number, weight: number, port: number, target: string }[] - if (key === "SRV") { - value.forEach((record) => { - t.true( - typeof record.priority === "number", - `${file}: SRV record value should have a number for priority at index ${value.indexOf( - record - )}` - ); - - t.true( - typeof record.weight === "number", - `${file}: SRV record value should have a number for weight at index ${value.indexOf( - record - )}` - ); - - t.true( - typeof record.port === "number", - `${file}: SRV record value should have a number for port at index ${value.indexOf(record)}` - ); - - t.true( - typeof record.target === "string", - `${file}: SRV record value should have a string for target at index ${value.indexOf( - record - )}` - ); - - t.regex( - value.target, - hostnameRegex, - `${file}: SRV record value should be a valid hostname for target at index ${value.indexOf( - record - )}` - ); - }); - } } - // TXT: string | string[] + // TXT: Single string or array of strings if (key === "TXT") { if (Array.isArray(value)) { - value.forEach((record) => { + value.forEach((record, idx) => { t.true( typeof record === "string", - `${file}: Record value should be a string for ${key} at index ${value.indexOf(record)}` + `${file}: TXT record value should be a string at index ${idx}` ); }); } else { - t.true(typeof value === "string", `${file}: Record value should be a string for ${key}`); + t.true(typeof value === "string", `${file}: TXT record value should be a string`); } } });