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/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/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/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.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.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