diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b8b3bc3..377d553 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -8,7 +8,7 @@ - Github Pull Requests - How DNS Works 4. When in doubt, read the docs before asking in PR - 5. **PREVIEWS ARE REQUIRED FOR WEBSITES.** Must be a link. If it's not a website then please state the use of the subdomain. + 5. **PREVIEWS ARE REQUIRED FOR WEBSITES.** Can be a screenshot/link. If it's not a website then please state the use of the subdomain. --> ## Requirements @@ -38,5 +38,5 @@ _None provided..._ diff --git a/.gitignore b/.gitignore index 6d32cd8..0ff973d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ creds.json -types-dnscontrol.d.ts \ No newline at end of file +types-dnscontrol.d.ts +result \ No newline at end of file diff --git a/README.md b/README.md index 34139ae..78bb9c6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ > [!IMPORTANT] -> We are currently rewriting our registration process, CI/CD pipeline, documentation, and website. Due to time constraints, **pull requests are still welcome** and will be migrated to the new syntax after the rewrite. We will document the new registration process in this repository once it's ready. In the meantime, you can join our [discord server](https://discord.gg/rFyRF3MMhc) to get updates and support. +> We are currently rewriting our registration process, CI/CD pipeline, documentation, and website. Pull requests are temporarily paused until the new system is ready. We will document the new registration process in this repository once it's ready. In the meantime, you can join our [discord server](https://discord.gg/rFyRF3MMhc) to get updates and support. > [!CAUTION] > We currently **DO NOT** support Vercel, Netlify, and other services that requires us to be on the [PSL](https://github.com/publicsuffix/list). _We will apply to be on the list [only if theres high demand](https://publicsuffix.org/submit/#:~:text=We%20will%20generally%20decline%20small%20projects)_, so be patient and invite some of your friends! diff --git a/docs/example.nix b/docs/example.nix new file mode 100644 index 0000000..5a58fe9 --- /dev/null +++ b/docs/example.nix @@ -0,0 +1,78 @@ +{ dns, ... }: let + owner = { + username = "satr14washere"; + email = "admin@satr14.my.id"; + }; + proxy = false; +in with dns.lib.combinators; { + A = [ + { address = "203.0.113.1"; ttl = 60 * 60; } + "203.0.113.2" + (ttl (60 * 60) (a "203.0.113.3")) + ]; + + AAAA = [ + "4321:0:1:2:3:4:567:89ab" + ]; + + MX = mx.google; + + TXT = [ + (with spf; strict [ "a:mail.example.com" google ]) + ]; + + DMARC = [ (dmarc.postmarkapp "mailto:re+abcdefghijk@dmarc.postmarkapp.com") ]; + + CAA = letsEncrypt "admin@example.com"; + + SRV = [ + { + service = "sip"; + proto = "tcp"; + port = 5060; + target = "sip.example.com"; + } + ]; + + SSHFP = [ + { + algorithm = "ed25519"; + fingerprintType = "sha256"; + fingerprint = "899EB4AC9285578AFDA3CCBE152EE78D8618B8F3862FEF2703E1FC7011E9B8AA"; + } + ]; + OPENPGPKEY = [ + "very long base64 text" + ]; + HTTPS = [ + { + svcPriority = 1; + targetName = "."; + alpn = [ "http/1.1" "h2" "h3" ]; + ipv4hint = [ "203.0.113.1" "203.0.113.2" "203.0.113.3" ]; + ipv6hint = [ "4321:0:1:2:3:4:567:89ab" ]; + } + ]; + TLSA = [ + { + certUsage = "dane-ee"; + selector = "spki"; + matchingType = "sha256"; + certificate = "899EB4AC9285578AFDA3CCBE152EE78D8618B8F3862FEF2703E1FC7011E9B8AA"; + } + ]; + + subdomains = rec { + www.A = [ "203.0.113.4" ]; + www2 = host "203.0.113.5" "4321:0:1:2:3:4:567:89bb"; + www3 = host "203.0.113.6" null; + www4 = www3; + + staging = delegateTo [ + "ns1.another.com." + "ns2.another.com." + ]; + + foo.subdomains.www.CNAME = [ "foo.test.com." ]; + }; +} diff --git a/domains/@.json b/domains/@.json deleted file mode 100644 index 9c9947c..0000000 --- a/domains/@.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "description": "dashboard and main website", - "owner": { - "username": "partofmyid" - }, - "record": { - "ALIAS": "website-e7n.pages.dev" - } -} diff --git a/domains/_discord.colin.nix b/domains/_discord.colin.nix new file mode 100644 index 0000000..e107e70 --- /dev/null +++ b/domains/_discord.colin.nix @@ -0,0 +1,9 @@ +{ dns, ... }: let + owner = { + username = "ColinLeDev"; + }; + description = "Discord verification"; + proxy = false; +in with dns.lib.combinators; { + TXT = [ "dh=279643a6f8677dedb1c5c63d007fc4516149679c" ]; +} diff --git a/domains/_discord.cutedog5695.nix b/domains/_discord.cutedog5695.nix new file mode 100644 index 0000000..8b6e78e --- /dev/null +++ b/domains/_discord.cutedog5695.nix @@ -0,0 +1,10 @@ +{ dns, ... }: let + owner = { + username = "CuteDog5695"; + email = "cutedog5695@gmail.com"; + repo = "https://github.com/CuteDog5695/cutedog5695.github.io"; + }; + proxy = false; +in with dns.lib.combinators; { + TXT = [ "dh=a7c19efb0f6bc38b97a33760f6c1ee84df4151b1" ]; +} diff --git a/domains/_discord.justdeveloper.nix b/domains/_discord.justdeveloper.nix new file mode 100644 index 0000000..c1025bb --- /dev/null +++ b/domains/_discord.justdeveloper.nix @@ -0,0 +1,10 @@ +{ dns, ... }: let + owner = { + username = "JustDeveloper1"; + email = "justdeveloper@juststudio.is-a.dev"; + repo = "https://github.com/JustDeveloper1/Website"; + }; + proxy = false; +in with dns.lib.combinators; { + TXT = [ "dh=6024027bc233825451e290ac37a4b4a1f838ee70" ]; +} diff --git a/domains/_discord.nix b/domains/_discord.nix new file mode 100644 index 0000000..ee3009d --- /dev/null +++ b/domains/_discord.nix @@ -0,0 +1,8 @@ +{ dns, ... }: let + owner = { + username = "satr14washere"; + }; + proxy = false; +in with dns.lib.combinators; { + TXT = [ "dh=d509fc9014e196311ed887c2e410cdefa833436e" ]; +} diff --git a/domains/_discord.roki.nix b/domains/_discord.roki.nix new file mode 100644 index 0000000..5b1d452 --- /dev/null +++ b/domains/_discord.roki.nix @@ -0,0 +1,8 @@ +{ dns, ... }: let + owner = { + username = "Roki100"; + discord = "289479495444987904"; + }; +in with dns.lib.combinators; { + TXT = [ "dh=5633078cd5bfd347a896ddb0f0de017c5423aa06" ]; +} diff --git a/domains/_vercel.reallunarisx.json b/domains/_vercel.reallunarisx.json deleted file mode 100644 index 132bee7..0000000 --- a/domains/_vercel.reallunarisx.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "owner": { - "username": "LunarisXOffical" - }, - "record": { - "TXT": [ "vc-domain-verify=reallunarisx.part-of.my.id,eb89acab3adcd3ee3acd" ] - }, - "proxy": false -} diff --git a/domains/batman.nix b/domains/batman.nix new file mode 100644 index 0000000..0ad04bd --- /dev/null +++ b/domains/batman.nix @@ -0,0 +1,8 @@ +{ dns, ... }: let + owner = { + username = "shadowe1ite"; + }; + proxy = true; +in with dns.lib.combinators; { + CNAME = [ "shadowe1ite.github.io." ]; +} diff --git a/domains/c.nix b/domains/c.nix new file mode 100644 index 0000000..bed0b01 --- /dev/null +++ b/domains/c.nix @@ -0,0 +1,9 @@ +{ dns, ... }: let + owner = { + username = "orangci"; + email = "c@orangc.xyz"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; +} diff --git a/domains/colin.nix b/domains/colin.nix new file mode 100644 index 0000000..277aba7 --- /dev/null +++ b/domains/colin.nix @@ -0,0 +1,9 @@ +{ dns, ... }: let + owner = { + username = "ColinLeDev"; + }; + description = "My personal portfolio hosted on my server"; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "proxy.col1n.fr." ]; +} diff --git a/domains/cutedog5695.nix b/domains/cutedog5695.nix new file mode 100644 index 0000000..f20329f --- /dev/null +++ b/domains/cutedog5695.nix @@ -0,0 +1,10 @@ +{ dns, ... }: let + owner = { + username = "CuteDog5695"; + email = "cutedog5695@gmail.com"; + repo = "https://github.com/CuteDog5695/cutedog5695.github.io"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; +} diff --git a/domains/elkaff.nix b/domains/elkaff.nix new file mode 100644 index 0000000..9ead547 --- /dev/null +++ b/domains/elkaff.nix @@ -0,0 +1,7 @@ +{ dns, ... }: let + owner = { + username = "elkhaff"; + }; +in with dns.lib.combinators; { + CNAME = [ "portofolio-pixel.pages.dev." ]; +} diff --git a/domains/j.nix b/domains/j.nix new file mode 100644 index 0000000..8cd38c6 --- /dev/null +++ b/domains/j.nix @@ -0,0 +1,10 @@ +{ dns, ... }: let + owner = { + username = "JustDeveloper1"; + email = "support@juststudio.is-a.dev"; + repo = "https://github.com/JustStudio7/Website"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; +} diff --git a/domains/jacob.nix b/domains/jacob.nix new file mode 100644 index 0000000..46839f9 --- /dev/null +++ b/domains/jacob.nix @@ -0,0 +1,8 @@ +{ dns, ... }: let + owner = { + username = "jacobrdale"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "hexon404.onrender.com." ]; +} diff --git a/domains/jd.nix b/domains/jd.nix new file mode 100644 index 0000000..4555867 --- /dev/null +++ b/domains/jd.nix @@ -0,0 +1,10 @@ +{ dns, ... }: let + owner = { + username = "JustDeveloper1"; + email = "justdeveloper@juststudio.is-a.dev"; + repo = "https://github.com/JustDeveloper1/Website"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; +} diff --git a/domains/job.nix b/domains/job.nix new file mode 100644 index 0000000..c2623ab --- /dev/null +++ b/domains/job.nix @@ -0,0 +1,8 @@ +{ dns, ... }: let + owner = { + username = "FWEEaaaa1"; + }; + proxy = false; +in with dns.lib.combinators; { + A = [ "128.204.223.115" ]; +} diff --git a/domains/joel.nix b/domains/joel.nix new file mode 100644 index 0000000..f1ad335 --- /dev/null +++ b/domains/joel.nix @@ -0,0 +1,16 @@ +{ dns, ... }: let + owner = { + username = "joestr"; + email = "strasser999@gmail.com"; + }; + proxy = false; +in with dns.lib.combinators; { + A = [ "142.132.173.34" ]; + AAAA = [ "2a01:4f8:1c0c:6cc0::1" ]; + MX = [ + { + exchange = "achlys.infra.joestr.at."; + preference = 10; + } + ]; +} diff --git a/domains/js.nix b/domains/js.nix new file mode 100644 index 0000000..8cd38c6 --- /dev/null +++ b/domains/js.nix @@ -0,0 +1,10 @@ +{ dns, ... }: let + owner = { + username = "JustDeveloper1"; + email = "support@juststudio.is-a.dev"; + repo = "https://github.com/JustStudio7/Website"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; +} diff --git a/domains/just.nix b/domains/just.nix new file mode 100644 index 0000000..4555867 --- /dev/null +++ b/domains/just.nix @@ -0,0 +1,10 @@ +{ dns, ... }: let + owner = { + username = "JustDeveloper1"; + email = "justdeveloper@juststudio.is-a.dev"; + repo = "https://github.com/JustDeveloper1/Website"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; +} diff --git a/domains/justdev.nix b/domains/justdev.nix new file mode 100644 index 0000000..4555867 --- /dev/null +++ b/domains/justdev.nix @@ -0,0 +1,10 @@ +{ dns, ... }: let + owner = { + username = "JustDeveloper1"; + email = "justdeveloper@juststudio.is-a.dev"; + repo = "https://github.com/JustDeveloper1/Website"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; +} diff --git a/domains/justdeveloper.nix b/domains/justdeveloper.nix new file mode 100644 index 0000000..4555867 --- /dev/null +++ b/domains/justdeveloper.nix @@ -0,0 +1,10 @@ +{ dns, ... }: let + owner = { + username = "JustDeveloper1"; + email = "justdeveloper@juststudio.is-a.dev"; + repo = "https://github.com/JustDeveloper1/Website"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; +} diff --git a/domains/juststudio.nix b/domains/juststudio.nix new file mode 100644 index 0000000..8cd38c6 --- /dev/null +++ b/domains/juststudio.nix @@ -0,0 +1,10 @@ +{ dns, ... }: let + owner = { + username = "JustDeveloper1"; + email = "support@juststudio.is-a.dev"; + repo = "https://github.com/JustStudio7/Website"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; +} diff --git a/domains/katz.nix b/domains/katz.nix new file mode 100644 index 0000000..c369c96 --- /dev/null +++ b/domains/katz.nix @@ -0,0 +1,8 @@ +{ dns, ... }: let + owner = { + username = "Bananalolok"; + }; + proxy = false; +in with dns.lib.combinators; { + A = [ "69.197.135.205" ]; +} diff --git a/domains/no-one-is.nix b/domains/no-one-is.nix new file mode 100644 index 0000000..8fcb680 --- /dev/null +++ b/domains/no-one-is.nix @@ -0,0 +1,9 @@ +{ dns, ... }: let + owner = { + username = "EducatedSuddenBucket"; + email = "me@esb.is-a.dev"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "educatedsuddenbucket-github-io.onrender.com." ]; +} diff --git a/domains/pxl.nix b/domains/pxl.nix new file mode 100644 index 0000000..2b3bce0 --- /dev/null +++ b/domains/pxl.nix @@ -0,0 +1,8 @@ +{ dns, ... }: let + owner = { + username = "heypxl"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "heypxl.github.io." ]; +} diff --git a/domains/rchessauth.nix b/domains/rchessauth.nix new file mode 100644 index 0000000..6ddd4c4 --- /dev/null +++ b/domains/rchessauth.nix @@ -0,0 +1,8 @@ +{ dns, ... }: let + owner = { + username = "vortexprime24"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "fire.hackclub.app." ]; +} diff --git a/domains/reallunarisx.json b/domains/reallunarisx.json deleted file mode 100644 index ed58b6a..0000000 --- a/domains/reallunarisx.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "owner": { - "username": "LunarisXOffical" - }, - "record": { - "CNAME": "3bdbf404a94a1470.vercel-dns-017.com" - }, - "proxy": false -} diff --git a/domains/roki.nix b/domains/roki.nix new file mode 100644 index 0000000..43d30e6 --- /dev/null +++ b/domains/roki.nix @@ -0,0 +1,9 @@ +{ dns, ... }: let + owner = { + username = "Roki100"; + discord = "289479495444987904"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; +} diff --git a/domains/satr14.nix b/domains/satr14.nix new file mode 100644 index 0000000..fa6e690 --- /dev/null +++ b/domains/satr14.nix @@ -0,0 +1,7 @@ +{ dns, ... }: let + owner = { + username = "satr14washere"; + }; +in with dns.lib.combinators; { + CNAME = [ "5th-site.pages.dev." ]; +} diff --git a/domains/stef.json b/domains/stef.json index 7598880..7046320 100644 --- a/domains/stef.json +++ b/domains/stef.json @@ -1,10 +1,10 @@ { "owner": { - "email": "me@stefdp.com", + "email": "admin@stefdp.lol", "username": "Stef-00012" }, "record": { - "CNAME": "proxy.stefdp.com" + "CNAME": "proxy.stefdp.lol" }, "proxied": false } diff --git a/domains/stef.nix b/domains/stef.nix new file mode 100644 index 0000000..88e3870 --- /dev/null +++ b/domains/stef.nix @@ -0,0 +1,9 @@ +{ dns, ... }: let + owner = { + username = "Stef-00012"; + email = "admin@stefdp.lol"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "proxy.stefdp.lol." ]; +} diff --git a/domains/ukriu.nix b/domains/ukriu.nix new file mode 100644 index 0000000..bab016a --- /dev/null +++ b/domains/ukriu.nix @@ -0,0 +1,10 @@ +{ dns, ... }: let + owner = { + username = "ukriu"; + email = "partofmyid@ukriu.com"; + }; + description = "my website"; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "ukriu.pages.dev." ]; +} diff --git a/domains/you-are.json b/domains/you-are.json index 7598880..151b83d 100644 --- a/domains/you-are.json +++ b/domains/you-are.json @@ -1,6 +1,6 @@ { "owner": { - "email": "me@stefdp.com", + "email": "admin@stefdp.com", "username": "Stef-00012" }, "record": { diff --git a/domains/you-are.nix b/domains/you-are.nix new file mode 100644 index 0000000..3cad0e8 --- /dev/null +++ b/domains/you-are.nix @@ -0,0 +1,9 @@ +{ dns, ... }: let + owner = { + username = "Stef-00012"; + email = "admin@stefdp.com"; + }; + proxy = false; +in with dns.lib.combinators; { + CNAME = [ "proxy.stefdp.com." ]; +} diff --git a/flake.nix b/flake.nix index b35d8b3..9dd8906 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "Zone File Generator For part-of.my.id"; + description = "Zone File Generator"; inputs.dns.url = "github:nix-community/dns.nix"; outputs = { dns, ... }: let @@ -21,12 +21,13 @@ serial = builtins.currentTime; }; NS = domain.nameservers; - - # note: Cloudflare ignores SOA and NS records uploaded via Zone File, they are just so that dns.nix builds a valid zone file. - - A = [ "1.1.1.1" ]; + + # note: Cloudflare ignores SOA and NS records uploaded via Zone File, they are included just so that dns.nix builds a valid zone file. + + #subdomains = ; + # ^^ todo: implement file imports from ./domains } ) ) domains; }; -} +} \ No newline at end of file diff --git a/scripts/migrate-json-to-nix.py b/scripts/migrate-json-to-nix.py new file mode 100755 index 0000000..b85649f --- /dev/null +++ b/scripts/migrate-json-to-nix.py @@ -0,0 +1,279 @@ +#!/usr/bin/env python3 +""" +Migration script to convert domains/*.json to domains/*.nix + +Reads each JSON domain config and generates a corresponding .nix file +following the format from docs/example.nix. + +Usage: + python3 scripts/migrate-json-to-nix.py [--dry-run] [--delete-json] + +Options: + --dry-run Print generated .nix content to stdout without writing files + --delete-json Delete the original .json files after successful conversion +""" + +import json +import sys +import os +from pathlib import Path + +DOMAINS_DIR = Path(__file__).resolve().parent.parent / "domains" + + +def json_to_nix(data: dict) -> str: + """Convert a single domain JSON config to a .nix file string.""" + owner = data.get("owner", {}) + description = data.get("description") + record = data.get("record", {}) + # Some files use "proxy", others use "proxied" + proxy = data.get("proxied", data.get("proxy")) + + lines = [] + + # Header + lines.append("{ dns, ... }: let") + + # Owner block + owner_lines = [] + if owner.get("username"): + owner_lines.append(f' username = "{owner["username"]}";') + if owner.get("email"): + owner_lines.append(f' email = "{owner["email"]}";') + if owner.get("discord"): + owner_lines.append(f' discord = "{owner["discord"]}";') + if owner.get("repo"): + owner_lines.append(f' repo = "{owner["repo"]}";') + + lines.append(" owner = {") + for ol in owner_lines: + lines.append(ol) + lines.append(" };") + + if description is not None: + lines.append(f' description = "{escape_nix_string(description)}";') + + if proxy is not None: + lines.append(f" proxy = {'true' if proxy else 'false'};") + + lines.append("in with dns.lib.combinators; {") + + # Records + record_lines = build_record_lines(record) + for rl in record_lines: + lines.append(rl) + + lines.append("}") + lines.append("") + + return "\n".join(lines) + + +def escape_nix_string(s: str) -> str: + """Escape special characters for a Nix double-quoted string.""" + s = s.replace("\\", "\\\\") + s = s.replace('"', '\\"') + s = s.replace("${", "\\${") + return s + + +def build_record_lines(record: dict) -> list[str]: + """Build the Nix record lines from the JSON record dict.""" + lines = [] + + if "A" in record: + values = record["A"] + if isinstance(values, list): + if len(values) == 1: + lines.append(f' A = [ "{values[0]}" ];') + else: + lines.append(" A = [") + for v in values: + lines.append(f' "{v}"') + lines.append(" ];") + else: + lines.append(f' A = [ "{values}" ];') + + if "AAAA" in record: + values = record["AAAA"] + if isinstance(values, list): + if len(values) == 1: + lines.append(f' AAAA = [ "{values[0]}" ];') + else: + lines.append(" AAAA = [") + for v in values: + lines.append(f' "{v}"') + lines.append(" ];") + else: + lines.append(f' AAAA = [ "{values}" ];') + + if "CNAME" in record: + value = record["CNAME"] + if isinstance(value, list): + value = value[0] + lines.append(f' CNAME = [ "{value}." ];') + + if "ALIAS" in record: + value = record["ALIAS"] + if isinstance(value, list): + value = value[0] + # ALIAS is typically handled as CNAME in dns.nix + lines.append(f' CNAME = [ "{value}." ];') + + if "MX" in record: + values = record["MX"] + if isinstance(values, list): + lines.append(" MX = [") + for i, v in enumerate(values): + # MX records need priority; default to (i+1)*10 + priority = (i + 1) * 10 + lines.append(" {") + lines.append(f' exchange = "{ensure_fqdn(v)}";') + lines.append(f" preference = {priority};") + lines.append(" }") + lines.append(" ];") + else: + lines.append(" MX = [") + lines.append(" {") + lines.append(f' exchange = "{ensure_fqdn(values)}";') + lines.append(" preference = 10;") + lines.append(" }") + lines.append(" ];") + + if "TXT" in record: + values = record["TXT"] + if isinstance(values, list): + if len(values) == 1: + lines.append(f' TXT = [ "{escape_nix_string(values[0])}" ];') + else: + lines.append(" TXT = [") + for v in values: + lines.append(f' "{escape_nix_string(v)}"') + lines.append(" ];") + else: + lines.append(f' TXT = [ "{escape_nix_string(values)}" ];') + + if "NS" in record: + values = record["NS"] + if isinstance(values, list): + if len(values) == 1: + lines.append(f' NS = [ "{ensure_fqdn(values[0])}" ];') + else: + lines.append(" NS = [") + for v in values: + lines.append(f' "{ensure_fqdn(v)}"') + lines.append(" ];") + else: + lines.append(f' NS = [ "{ensure_fqdn(values)}" ];') + + if "SRV" in record: + values = record["SRV"] + if isinstance(values, list): + lines.append(" SRV = [") + for srv in values: + lines.append(" {") + if "service" in srv: + lines.append(f' service = "{srv["service"]}";') + if "proto" in srv: + lines.append(f' proto = "{srv["proto"]}";') + if "port" in srv: + lines.append(f" port = {srv['port']};") + if "priority" in srv: + lines.append(f" priority = {srv['priority']};") + if "weight" in srv: + lines.append(f" weight = {srv['weight']};") + if "target" in srv: + lines.append(f' target = "{ensure_fqdn(srv["target"])}";') + lines.append(" }") + lines.append(" ];") + + if "CAA" in record: + values = record["CAA"] + if isinstance(values, list): + lines.append(" CAA = [") + for caa in values: + lines.append(" {") + if "flags" in caa: + lines.append(f" flags = {caa['flags']};") + if "tag" in caa: + lines.append(f' tag = "{caa["tag"]}";') + if "value" in caa: + lines.append(f' value = "{escape_nix_string(caa["value"])}";') + lines.append(" }") + lines.append(" ];") + + return lines + + +def ensure_fqdn(domain: str) -> str: + """Ensure a domain name ends with a dot (FQDN).""" + if not domain.endswith("."): + return domain + "." + return domain + + +def migrate_file(json_path: Path, dry_run: bool = False, delete_json: bool = False) -> bool: + """Migrate a single JSON file to .nix. Returns True on success.""" + try: + with open(json_path, "r") as f: + data = json.load(f) + except json.JSONDecodeError as e: + print(f" ERROR: Failed to parse {json_path.name}: {e}", file=sys.stderr) + return False + + nix_content = json_to_nix(data) + nix_filename = json_path.stem + ".nix" + nix_path = json_path.parent / nix_filename + + if dry_run: + print(f"--- {nix_filename} ---") + print(nix_content) + return True + + with open(nix_path, "w") as f: + f.write(nix_content) + + print(f" Created {nix_path.name}") + + if delete_json: + json_path.unlink() + print(f" Deleted {json_path.name}") + + return True + + +def main(): + dry_run = "--dry-run" in sys.argv + delete_json = "--delete-json" in sys.argv + + if not DOMAINS_DIR.exists(): + print(f"Error: domains directory not found at {DOMAINS_DIR}", file=sys.stderr) + sys.exit(1) + + json_files = sorted(DOMAINS_DIR.glob("*.json")) + + if not json_files: + print("No JSON files found in domains/") + sys.exit(0) + + print(f"Found {len(json_files)} JSON file(s) to migrate") + if dry_run: + print("(dry run — no files will be written)\n") + + success = 0 + failed = 0 + + for json_path in json_files: + print(f"Migrating {json_path.name}...") + if migrate_file(json_path, dry_run=dry_run, delete_json=delete_json): + success += 1 + else: + failed += 1 + + print(f"\nDone: {success} succeeded, {failed} failed") + if failed > 0: + sys.exit(1) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/stats/count.txt b/stats/count.txt index f5c8955..e85087a 100644 --- a/stats/count.txt +++ b/stats/count.txt @@ -1 +1 @@ -32 +31