diff --git a/.gitignore b/.gitignore index 6d32cd8..83e780f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ +# DNSControl files creds.json -types-dnscontrol.d.ts \ No newline at end of file +types-dnscontrol.d.ts + +# Zone files +result* +part-of.my.id.txt \ No newline at end of file diff --git a/docs/example.nix b/docs/example.nix new file mode 100644 index 0000000..31378e9 --- /dev/null +++ b/docs/example.nix @@ -0,0 +1,76 @@ +{ dns, ... }: { + metadata = { + description = "Example domain configuration for dns.nix"; # optional, description of use + proxy = false; # optional, defaults to false. proxy through Cloudflare + owner = { # add extra contacts if needed + username = "satr14washere"; # required, github username + email = "admin@satr14.my.id"; + }; + }; + records = with dns.lib.combinators; { # full list of records supported: https://github.com/nix-community/dns.nix/tree/master/dns/types/records + # dns.lib.combinators is optional but provides a lot of useful shortcuts: + # https://github.com/nix-community/dns.nix/blob/master/dns/combinators.nix + + A = [ + "203.0.113.50" + "198.51.100.50" + + # or: + + { address = "203.0.113.50"; ttl = 60 * 60; } # TTL is optional + { address = "198.51.100.50"; ttl = 60 * 60; } + + # using dns.lib.combinators: + + (ttl (60 * 60) (a "203.0.113.50")) # standalone A record + (ttl (60 * 60) (a "2198.51.100.50")) # record with TTL + ]; + + AAAA = [ # mostly same as above + "2001:db8::1" + "2001:db8::2" + + # or: + + { address = "2001:db8::1"; ttl = 60 * 60; } + { address = "2001:db8::2"; ttl = 60 * 60; } + + # using dns.lib.combinators: + + (ttl (60 * 60) (aaaa "2001:db8::1")) + (ttl (60 * 60) (aaaa "2001:db8::2")) + ]; + + TXT = [ + "v=spf1 include:mailgun.org ~all" + "dh=some-long-random-string" + ]; + + MX = [ + { + preference = 10; + exchange = "mail.protonmail.ch."; + } + { + preference = 20; + exchange = "mailsec.protonmail.ch."; + } + + # using dns.lib.combinators: + + (mx.mx 10 "mail.protonmail.ch.") + (mx.mx 20 "mailsec.protonmail.ch.") + ]; + + # a few notes about CNAME records: + # - value must end with a dot (.) + # - cannot coexist with other record types (e.g. A, AAAA, MX) for the same subdomain + # - can only be one despite being a list (this example defined multiple only for demonstrating valid values) + CNAME = [ + "edge.redirect.pizza." + "username.github.io." + "site.pages.dev." + ]; + + }; +} 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/part-of.my.id/_discord.colin.nix b/domains/part-of.my.id/_discord.colin.nix new file mode 100644 index 0000000..f6f4a78 --- /dev/null +++ b/domains/part-of.my.id/_discord.colin.nix @@ -0,0 +1,12 @@ +{ dns, ... }: { + metadata = { + description = "Discord verification"; + proxy = false; + owner = { + username = "ColinLeDev"; + }; + }; + records = with dns.lib.combinators; { + TXT = [ "dh=279643a6f8677dedb1c5c63d007fc4516149679c" ]; + }; +} diff --git a/domains/part-of.my.id/_discord.cutedog5695.nix b/domains/part-of.my.id/_discord.cutedog5695.nix new file mode 100644 index 0000000..6b41ac4 --- /dev/null +++ b/domains/part-of.my.id/_discord.cutedog5695.nix @@ -0,0 +1,13 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "CuteDog5695"; + email = "cutedog5695@gmail.com"; + repo = "https://github.com/CuteDog5695/cutedog5695.github.io"; + }; + }; + records = with dns.lib.combinators; { + TXT = [ "dh=a7c19efb0f6bc38b97a33760f6c1ee84df4151b1" ]; + }; +} diff --git a/domains/part-of.my.id/_discord.justdeveloper.nix b/domains/part-of.my.id/_discord.justdeveloper.nix new file mode 100644 index 0000000..ceae91a --- /dev/null +++ b/domains/part-of.my.id/_discord.justdeveloper.nix @@ -0,0 +1,13 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "JustDeveloper1"; + email = "justdeveloper@juststudio.is-a.dev"; + repo = "https://github.com/JustDeveloper1/Website"; + }; + }; + records = with dns.lib.combinators; { + TXT = [ "dh=6024027bc233825451e290ac37a4b4a1f838ee70" ]; + }; +} diff --git a/domains/part-of.my.id/_discord.nix b/domains/part-of.my.id/_discord.nix new file mode 100644 index 0000000..efcc1e7 --- /dev/null +++ b/domains/part-of.my.id/_discord.nix @@ -0,0 +1,11 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "satr14washere"; + }; + }; + records = with dns.lib.combinators; { + TXT = [ "dh=d509fc9014e196311ed887c2e410cdefa833436e" ]; + }; +} diff --git a/domains/part-of.my.id/_discord.roki.nix b/domains/part-of.my.id/_discord.roki.nix new file mode 100644 index 0000000..bb55110 --- /dev/null +++ b/domains/part-of.my.id/_discord.roki.nix @@ -0,0 +1,11 @@ +{ dns, ... }: { + metadata = { + owner = { + username = "Roki100"; + discord = "289479495444987904"; + }; + }; + records = with dns.lib.combinators; { + TXT = [ "dh=5633078cd5bfd347a896ddb0f0de017c5423aa06" ]; + }; +} diff --git a/domains/part-of.my.id/batman.nix b/domains/part-of.my.id/batman.nix new file mode 100644 index 0000000..b3683bb --- /dev/null +++ b/domains/part-of.my.id/batman.nix @@ -0,0 +1,11 @@ +{ dns, ... }: { + metadata = { + proxy = true; + owner = { + username = "shadowe1ite"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "shadowe1ite.github.io." ]; + }; +} diff --git a/domains/part-of.my.id/c.nix b/domains/part-of.my.id/c.nix new file mode 100644 index 0000000..c56ef7a --- /dev/null +++ b/domains/part-of.my.id/c.nix @@ -0,0 +1,12 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "orangci"; + email = "c@orangc.xyz"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; + }; +} diff --git a/domains/part-of.my.id/colin.nix b/domains/part-of.my.id/colin.nix new file mode 100644 index 0000000..9bcb1c3 --- /dev/null +++ b/domains/part-of.my.id/colin.nix @@ -0,0 +1,12 @@ +{ dns, ... }: { + metadata = { + description = "My personal portfolio hosted on my server"; + proxy = false; + owner = { + username = "ColinLeDev"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "proxy.col1n.fr." ]; + }; +} diff --git a/domains/part-of.my.id/cutedog5695.nix b/domains/part-of.my.id/cutedog5695.nix new file mode 100644 index 0000000..be93952 --- /dev/null +++ b/domains/part-of.my.id/cutedog5695.nix @@ -0,0 +1,13 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "CuteDog5695"; + email = "cutedog5695@gmail.com"; + repo = "https://github.com/CuteDog5695/cutedog5695.github.io"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; + }; +} diff --git a/domains/part-of.my.id/elkaff.nix b/domains/part-of.my.id/elkaff.nix new file mode 100644 index 0000000..682bf6e --- /dev/null +++ b/domains/part-of.my.id/elkaff.nix @@ -0,0 +1,10 @@ +{ dns, ... }: { + metadata = { + owner = { + username = "elkhaff"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "portofolio-pixel.pages.dev." ]; + }; +} diff --git a/domains/part-of.my.id/j.nix b/domains/part-of.my.id/j.nix new file mode 100644 index 0000000..75598f9 --- /dev/null +++ b/domains/part-of.my.id/j.nix @@ -0,0 +1,13 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "JustDeveloper1"; + email = "support@juststudio.is-a.dev"; + repo = "https://github.com/JustStudio7/Website"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; + }; +} diff --git a/domains/part-of.my.id/jacob.nix b/domains/part-of.my.id/jacob.nix new file mode 100644 index 0000000..cc7f30d --- /dev/null +++ b/domains/part-of.my.id/jacob.nix @@ -0,0 +1,11 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "jacobrdale"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "hexon404.onrender.com." ]; + }; +} diff --git a/domains/part-of.my.id/jd.nix b/domains/part-of.my.id/jd.nix new file mode 100644 index 0000000..ebfc556 --- /dev/null +++ b/domains/part-of.my.id/jd.nix @@ -0,0 +1,13 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "JustDeveloper1"; + email = "justdeveloper@juststudio.is-a.dev"; + repo = "https://github.com/JustDeveloper1/Website"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; + }; +} diff --git a/domains/part-of.my.id/job.nix b/domains/part-of.my.id/job.nix new file mode 100644 index 0000000..4e380cf --- /dev/null +++ b/domains/part-of.my.id/job.nix @@ -0,0 +1,11 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "FWEEaaaa1"; + }; + }; + records = with dns.lib.combinators; { + A = [ "128.204.223.115" ]; + }; +} diff --git a/domains/part-of.my.id/joel.nix b/domains/part-of.my.id/joel.nix new file mode 100644 index 0000000..0513cd2 --- /dev/null +++ b/domains/part-of.my.id/joel.nix @@ -0,0 +1,19 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "joestr"; + email = "strasser999@gmail.com"; + }; + }; + records = with dns.lib.combinators; { + A = [ "142.132.173.34" ]; + AAAA = [ "2a01:4f8:1c0c:6cc0::1" ]; + MX = [ + { + preference = 10; + exchange = "achlys.infra.joestr.at."; + } + ]; + }; +} diff --git a/domains/part-of.my.id/js.nix b/domains/part-of.my.id/js.nix new file mode 100644 index 0000000..75598f9 --- /dev/null +++ b/domains/part-of.my.id/js.nix @@ -0,0 +1,13 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "JustDeveloper1"; + email = "support@juststudio.is-a.dev"; + repo = "https://github.com/JustStudio7/Website"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; + }; +} diff --git a/domains/part-of.my.id/just.nix b/domains/part-of.my.id/just.nix new file mode 100644 index 0000000..ebfc556 --- /dev/null +++ b/domains/part-of.my.id/just.nix @@ -0,0 +1,13 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "JustDeveloper1"; + email = "justdeveloper@juststudio.is-a.dev"; + repo = "https://github.com/JustDeveloper1/Website"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; + }; +} diff --git a/domains/part-of.my.id/justdev.nix b/domains/part-of.my.id/justdev.nix new file mode 100644 index 0000000..ebfc556 --- /dev/null +++ b/domains/part-of.my.id/justdev.nix @@ -0,0 +1,13 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "JustDeveloper1"; + email = "justdeveloper@juststudio.is-a.dev"; + repo = "https://github.com/JustDeveloper1/Website"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; + }; +} diff --git a/domains/part-of.my.id/justdeveloper.nix b/domains/part-of.my.id/justdeveloper.nix new file mode 100644 index 0000000..ebfc556 --- /dev/null +++ b/domains/part-of.my.id/justdeveloper.nix @@ -0,0 +1,13 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "JustDeveloper1"; + email = "justdeveloper@juststudio.is-a.dev"; + repo = "https://github.com/JustDeveloper1/Website"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; + }; +} diff --git a/domains/part-of.my.id/juststudio.nix b/domains/part-of.my.id/juststudio.nix new file mode 100644 index 0000000..75598f9 --- /dev/null +++ b/domains/part-of.my.id/juststudio.nix @@ -0,0 +1,13 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "JustDeveloper1"; + email = "support@juststudio.is-a.dev"; + repo = "https://github.com/JustStudio7/Website"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; + }; +} diff --git a/domains/part-of.my.id/katz.nix b/domains/part-of.my.id/katz.nix new file mode 100644 index 0000000..09f3469 --- /dev/null +++ b/domains/part-of.my.id/katz.nix @@ -0,0 +1,11 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "Bananalolok"; + }; + }; + records = with dns.lib.combinators; { + A = [ "69.197.135.205" ]; + }; +} diff --git a/domains/part-of.my.id/no-one-is.nix b/domains/part-of.my.id/no-one-is.nix new file mode 100644 index 0000000..ac78aba --- /dev/null +++ b/domains/part-of.my.id/no-one-is.nix @@ -0,0 +1,12 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "EducatedSuddenBucket"; + email = "me@esb.is-a.dev"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "educatedsuddenbucket-github-io.onrender.com." ]; + }; +} diff --git a/domains/part-of.my.id/pxl.nix b/domains/part-of.my.id/pxl.nix new file mode 100644 index 0000000..57446eb --- /dev/null +++ b/domains/part-of.my.id/pxl.nix @@ -0,0 +1,11 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "heypxl"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "heypxl.github.io." ]; + }; +} diff --git a/domains/part-of.my.id/rchessauth.nix b/domains/part-of.my.id/rchessauth.nix new file mode 100644 index 0000000..20ca546 --- /dev/null +++ b/domains/part-of.my.id/rchessauth.nix @@ -0,0 +1,11 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "vortexprime24"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "fire.hackclub.app." ]; + }; +} diff --git a/domains/part-of.my.id/roki.nix b/domains/part-of.my.id/roki.nix new file mode 100644 index 0000000..0cbf5b4 --- /dev/null +++ b/domains/part-of.my.id/roki.nix @@ -0,0 +1,12 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "Roki100"; + discord = "289479495444987904"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "edge.redirect.pizza." ]; + }; +} diff --git a/domains/part-of.my.id/satr14.nix b/domains/part-of.my.id/satr14.nix new file mode 100644 index 0000000..17817fc --- /dev/null +++ b/domains/part-of.my.id/satr14.nix @@ -0,0 +1,10 @@ +{ dns, ... }: { + metadata = { + owner = { + username = "satr14washere"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "5th-site.pages.dev." ]; + }; +} diff --git a/domains/part-of.my.id/stef.nix b/domains/part-of.my.id/stef.nix new file mode 100644 index 0000000..dfe1827 --- /dev/null +++ b/domains/part-of.my.id/stef.nix @@ -0,0 +1,12 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "Stef-00012"; + email = "admin@stefdp.lol"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "proxy.stefdp.lol." ]; + }; +} diff --git a/domains/part-of.my.id/ukriu.nix b/domains/part-of.my.id/ukriu.nix new file mode 100644 index 0000000..138199b --- /dev/null +++ b/domains/part-of.my.id/ukriu.nix @@ -0,0 +1,13 @@ +{ dns, ... }: { + metadata = { + description = "my website"; + proxy = false; + owner = { + username = "ukriu"; + email = "partofmyid@ukriu.com"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "ukriu.pages.dev." ]; + }; +} diff --git a/domains/part-of.my.id/you-are.nix b/domains/part-of.my.id/you-are.nix new file mode 100644 index 0000000..94278bb --- /dev/null +++ b/domains/part-of.my.id/you-are.nix @@ -0,0 +1,12 @@ +{ dns, ... }: { + metadata = { + proxy = false; + owner = { + username = "Stef-00012"; + email = "admin@stefdp.com"; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "proxy.stefdp.com." ]; + }; +} diff --git a/flake.nix b/flake.nix index b35d8b3..3dd9f31 100644 --- a/flake.nix +++ b/flake.nix @@ -1,32 +1,59 @@ { - description = "Zone File Generator For part-of.my.id"; + description = "Zone File Generator"; inputs.dns.url = "github:nix-community/dns.nix"; - - outputs = { dns, ... }: let - email = "admin@satr14.my.id"; - domains."0" = { - domain = "part-of.my.id"; - nameservers = [ - "adele.ns.cloudflare.com" - "fattouche.ns.cloudflare.com" + outputs = + { dns, ... }: + let + email = "admin@satr14.my.id"; + domains = [ + "0" = { + domain = "part-of.my.id"; + nameservers = [ + "adele.ns.cloudflare.com" + "fattouche.ns.cloudflare.com" + ]; + }; + "1" = { + domain = "is-my.id"; + nameservers = [ + "adele.ns.cloudflare.com" + "fattouche.ns.cloudflare.com" + ]; + }; ]; - }; - in { - packages.x86_64-linux = builtins.mapAttrs (_: domain: - dns.util.x86_64-linux.writeZone domain.domain ( - with dns.lib.combinators; { - SOA = { - adminEmail = email; - nameServer = builtins.head domain.nameservers; - 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" ]; + inherit (import { }) lib; + domainsFolder = builtins.readDir ./domains; + domainFiles = lib.filterAttrs ( + name: type: type == "regular" && builtins.match ".*\\.nix" name != null + ) domainsFolder; + subdomains = lib.mapAttrs' ( + name: _: + let + key = builtins.replaceStrings [ ".nix" ] [ "" ] name; + in + { + name = key; + value = (import (./domains + "/${name}") { inherit dns; }).records; } - ) - ) domains; - }; + ) domainFiles; + in + { + packages.x86_64-linux = builtins.mapAttrs ( + _: domain: + dns.util.x86_64-linux.writeZone domain.domain ( + with dns.lib.combinators; + { + SOA = { + adminEmail = email; + nameServer = builtins.head domain.nameservers; + serial = builtins.currentTime; + }; + NS = domain.nameservers; + # note: Cloudflare ignores SOA and NS records uploaded via Zone File, they are included just so that dns.nix builds a valid zone file. + CNAME = [ "website-e7n.pages.dev." ]; + inherit subdomains; + } + ) + ) domains; + }; } diff --git a/is-my.id.txt b/is-my.id.txt new file mode 100644 index 0000000..690dc91 --- /dev/null +++ b/is-my.id.txt @@ -0,0 +1,72 @@ +;; +;; Domain: is-my.id. +;; Exported: 2026-03-21 23:44:57 +;; +;; This file is intended for use for informational and archival +;; purposes ONLY and MUST be edited before use on a production +;; DNS server. In particular, you must: +;; -- update the SOA record with the correct authoritative name server +;; -- update the SOA record with the contact e-mail address information +;; -- update the NS record(s) with the authoritative name servers for this domain. +;; +;; For further information, please consult the BIND documentation +;; located on the following website: +;; +;; http://www.isc.org/ +;; +;; And RFC 1035: +;; +;; http://www.ietf.org/rfc/rfc1035.txt +;; +;; Please note that we do NOT offer technical support for any use +;; of this zone data, the BIND name server, or any other third-party +;; DNS software. +;; +;; Use at your own risk. +;; SOA Record +is-my.id 3600 IN SOA adele.ns.cloudflare.com. dns.cloudflare.com. 2052580329 10000 2400 604800 3600 + +;; NS Records +is-my.id. 86400 IN NS adele.ns.cloudflare.com. +is-my.id. 86400 IN NS fattouche.ns.cloudflare.com. + +;; A Records +job.is-my.id. 1 IN A 128.204.223.115 ; cf_tags=cf-proxied:false +joel.is-my.id. 1 IN A 142.132.173.34 ; cf_tags=cf-proxied:false +katz.is-my.id. 1 IN A 69.197.135.205 ; cf_tags=cf-proxied:false + +;; AAAA Records +joel.is-my.id. 1 IN AAAA 2a01:4f8:1c0c:6cc0::1 ; cf_tags=cf-proxied:false + +;; CNAME Records +batman.is-my.id. 1 IN CNAME shadowe1ite.github.io. ; cf_tags=cf-proxied:true +colin.is-my.id. 1 IN CNAME proxy.col1n.fr. ; cf_tags=cf-proxied:false +c.is-my.id. 1 IN CNAME edge.redirect.pizza. ; cf_tags=cf-proxied:false +cutedog5695.is-my.id. 1 IN CNAME edge.redirect.pizza. ; cf_tags=cf-proxied:false +elkaff.is-my.id. 1 IN CNAME portofolio-pixel.pages.dev. ; cf_tags=cf-proxied:false +jacob.is-my.id. 1 IN CNAME hexon404.onrender.com. ; cf_tags=cf-proxied:false +jd.is-my.id. 1 IN CNAME edge.redirect.pizza. ; cf_tags=cf-proxied:false +j.is-my.id. 1 IN CNAME edge.redirect.pizza. ; cf_tags=cf-proxied:false +js.is-my.id. 1 IN CNAME edge.redirect.pizza. ; cf_tags=cf-proxied:false +justdeveloper.is-my.id. 1 IN CNAME edge.redirect.pizza. ; cf_tags=cf-proxied:false +justdev.is-my.id. 1 IN CNAME edge.redirect.pizza. ; cf_tags=cf-proxied:false +just.is-my.id. 1 IN CNAME edge.redirect.pizza. ; cf_tags=cf-proxied:false +juststudio.is-my.id. 1 IN CNAME edge.redirect.pizza. ; cf_tags=cf-proxied:false +no-one-is.is-my.id. 1 IN CNAME educatedsuddenbucket-github-io.onrender.com. ; cf_tags=cf-proxied:false +is-my.id. 1 IN CNAME website-e7n.pages.dev. ; cf_tags=cf-proxied:false +pxl.is-my.id. 1 IN CNAME heypxl.github.io. ; cf_tags=cf-proxied:false +rchessauth.is-my.id. 1 IN CNAME fire.hackclub.app. ; cf_tags=cf-proxied:false +roki.is-my.id. 1 IN CNAME edge.redirect.pizza. ; cf_tags=cf-proxied:false +stef.is-my.id. 1 IN CNAME proxy.stefdp.lol. ; cf_tags=cf-proxied:false +ukriu.is-my.id. 1 IN CNAME ukriu.pages.dev. ; cf_tags=cf-proxied:false +you-are.is-my.id. 1 IN CNAME proxy.stefdp.com. ; cf_tags=cf-proxied:false + +;; MX Records +joel.is-my.id. 1 IN MX 10 achlys.infra.joestr.at. + +;; TXT Records +_discord.colin.is-my.id. 1 IN TXT "dh=279643a6f8677dedb1c5c63d007fc4516149679c" +_discord.cutedog5695.is-my.id. 1 IN TXT "dh=a7c19efb0f6bc38b97a33760f6c1ee84df4151b1" +_discord.justdeveloper.is-my.id. 1 IN TXT "dh=6024027bc233825451e290ac37a4b4a1f838ee70" +_discord.is-my.id. 1 IN TXT "dh=d509fc9014e196311ed887c2e410cdefa833436e" +_discord.roki.is-my.id. 1 IN TXT "dh=5633078cd5bfd347a896ddb0f0de017c5423aa06" diff --git a/scripts/compare-zones.sh b/scripts/compare-zones.sh new file mode 100755 index 0000000..4bf234f --- /dev/null +++ b/scripts/compare-zones.sh @@ -0,0 +1,200 @@ +#!/usr/bin/env bash +# +# compare-zones.sh — Compare two BIND-format zone files +# +# Normalizes both files (strips comments, TTLs, SOA records, and whitespace +# differences) then performs a record-by-record comparison. +# +# Usage: +# ./scripts/compare-zones.sh +# +# Examples: +# ./scripts/compare-zones.sh expected.zone generated.zone +# ./scripts/compare-zones.sh part-of.my.id.txt result + +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +CYAN='\033[0;36m' +BOLD='\033[1m' +RESET='\033[0m' + +usage() { + echo "Usage: $0 " + echo "" + echo "Compare two BIND-format zone files." + echo "" + echo "Arguments:" + echo " zone-file-a Path to the first zone file" + echo " zone-file-b Path to the second zone file" + exit 1 +} + +if [[ $# -lt 2 ]]; then + usage +fi + +FILE_A="$1" +FILE_B="$2" + +for f in "$FILE_A" "$FILE_B"; do + resolved="$f" + # Resolve symlinks (e.g. nix store results) + if [[ -L "$resolved" ]]; then + resolved="$(readlink -f "$resolved")" + fi + if [[ ! -f "$resolved" ]]; then + echo -e "${RED}Error:${RESET} File not found: $f" + exit 1 + fi +done + +# Resolve symlinks for display +RESOLVED_A="$FILE_A" +RESOLVED_B="$FILE_B" +[[ -L "$FILE_A" ]] && RESOLVED_A="$(readlink -f "$FILE_A")" +[[ -L "$FILE_B" ]] && RESOLVED_B="$(readlink -f "$FILE_B")" + +TMPDIR="$(mktemp -d)" +trap 'rm -rf "$TMPDIR"' EXIT + +# normalize_zone +# +# Extracts resource records, strips comments, normalizes whitespace and TTLs, +# ensures FQDNs have trailing dots, skips SOA (which always differs), and sorts. +normalize_zone() { + local input="$1" + local output="$2" + + # Resolve symlinks + if [[ -L "$input" ]]; then + input="$(readlink -f "$input")" + fi + + grep -E '^\S+' "$input" \ + | grep -v '^\s*;' \ + | grep -v '^\s*$' \ + | grep -v '^\$' \ + | sed 's/\s*;.*$//' \ + | sed 's/\t\+/ /g; s/ \+/ /g' \ + | awk ' + { + # Expected formats after cleanup: + # name TTL IN TYPE rdata... + # name IN TYPE rdata... + # + # We output: name TYPE rdata... + + name = $1 + idx = 2 + + # Skip TTL if present (a number) + if ($idx ~ /^[0-9]+$/) idx++ + + # Skip class (IN, CS, CH, HS) + if (toupper($idx) == "IN" || toupper($idx) == "CS" || toupper($idx) == "CH" || toupper($idx) == "HS") idx++ + + rtype = toupper($idx) + idx++ + + # Skip SOA — serial and timers will always differ + if (rtype == "SOA") next + + rdata = "" + for (i = idx; i <= NF; i++) { + val = $i + # Ensure trailing dot on targets for NS, CNAME, MX + if ((rtype == "NS" || rtype == "CNAME") && i == idx) { + if (val !~ /\.$/) val = val "." + } + if (rtype == "MX" && i == NF) { + if (val !~ /\.$/) val = val "." + } + if (rdata != "") rdata = rdata " " + rdata = rdata val + } + + print name " " rtype " " rdata + } + ' \ + | sort > "$output" +} + +LABEL_A="$(basename "$FILE_A")" +LABEL_B="$(basename "$FILE_B")" + +echo -e "${BOLD}Comparing zones${RESET}" +echo -e " A: ${CYAN}${RESOLVED_A}${RESET}" +echo -e " B: ${CYAN}${RESOLVED_B}${RESET}" +echo "" + +normalize_zone "$FILE_A" "$TMPDIR/a.norm" +normalize_zone "$FILE_B" "$TMPDIR/b.norm" + +COUNT_A=$(wc -l < "$TMPDIR/a.norm") +COUNT_B=$(wc -l < "$TMPDIR/b.norm") + +echo -e " A records: ${BOLD}$COUNT_A${RESET} (excluding SOA)" +echo -e " B records: ${BOLD}$COUNT_B${RESET} (excluding SOA)" +echo "" + +# Compute differences +comm -23 "$TMPDIR/a.norm" "$TMPDIR/b.norm" > "$TMPDIR/only-a.txt" +comm -13 "$TMPDIR/a.norm" "$TMPDIR/b.norm" > "$TMPDIR/only-b.txt" +comm -12 "$TMPDIR/a.norm" "$TMPDIR/b.norm" > "$TMPDIR/matching.txt" + +MATCH_COUNT=$(wc -l < "$TMPDIR/matching.txt") +ONLY_A_COUNT=$(wc -l < "$TMPDIR/only-a.txt") +ONLY_B_COUNT=$(wc -l < "$TMPDIR/only-b.txt") + +echo -e "${BOLD}Results${RESET}" +echo -e " ${GREEN}✓ Matching:${RESET} $MATCH_COUNT" +echo -e " ${RED}✗ Only in A:${RESET} $ONLY_A_COUNT" +echo -e " ${YELLOW}+ Only in B:${RESET} $ONLY_B_COUNT" +echo "" + +if [[ "$ONLY_A_COUNT" -gt 0 ]]; then + echo -e "${RED}${BOLD}Records only in A (${LABEL_A}):${RESET}" + while IFS= read -r line; do + echo -e " ${RED}-${RESET} $line" + done < "$TMPDIR/only-a.txt" + echo "" +fi + +if [[ "$ONLY_B_COUNT" -gt 0 ]]; then + echo -e "${YELLOW}${BOLD}Records only in B (${LABEL_B}):${RESET}" + while IFS= read -r line; do + echo -e " ${YELLOW}+${RESET} $line" + done < "$TMPDIR/only-b.txt" + echo "" +fi + +if [[ "$ONLY_A_COUNT" -eq 0 && "$ONLY_B_COUNT" -eq 0 ]]; then + echo -e "${GREEN}${BOLD}✓ Zones are identical!${RESET}" + exit 0 +else + # Unified diff + echo -e "${BOLD}Diff (unified):${RESET}" + diff -u \ + --label "$LABEL_A" "$TMPDIR/a.norm" \ + --label "$LABEL_B" "$TMPDIR/b.norm" \ + | head -80 || true + echo "" + + # Summary by record type + echo -e "${BOLD}Summary by record type:${RESET}" + echo -e " ${BOLD}Type A-only B-only Matching${RESET}" + { + cat "$TMPDIR/only-a.txt" "$TMPDIR/only-b.txt" "$TMPDIR/matching.txt" + } | awk '{print $2}' | sort -u | while read -r rtype; do + a_only=$(grep -c "^[^ ]* ${rtype} " "$TMPDIR/only-a.txt" || true) + b_only=$(grep -c "^[^ ]* ${rtype} " "$TMPDIR/only-b.txt" || true) + matching=$(grep -c "^[^ ]* ${rtype} " "$TMPDIR/matching.txt" || true) + printf " %-6s %6d %6d %8d\n" "$rtype" "$a_only" "$b_only" "$matching" + done + echo "" + + exit 1 +fi \ No newline at end of file diff --git a/scripts/deploy-apex.sh b/scripts/deploy-apex.sh deleted file mode 100644 index c2ad7a1..0000000 --- a/scripts/deploy-apex.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash - -# script to deploy the APEX domain to Cloudflare with CNAME flattening - -set -euo pipefail - -ZONE_ID="${CF_ZONE_ID:?}" -TOKEN="${CF_API_TOKEN:?}" -TARGET="website-e7n.pages.dev" - -EXISTING=$(curl -s \ - "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records?type=CNAME&name=@" \ - -H "Authorization: Bearer ${TOKEN}" \ - | jq -r '.result[0] // empty') - -EXISTING_CONTENT=$(echo "$EXISTING" | jq -r '.content // empty') -EXISTING_ID=$(echo "$EXISTING" | jq -r '.id // empty') - -if [[ "$EXISTING_CONTENT" == "$TARGET" ]]; then - echo "Apex CNAME unchanged, skipping." - exit 0 -fi - -if [[ -z "$EXISTING_ID" ]]; then - echo "No apex CNAME found, creating..." - METHOD="POST" - URL="https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" -else - echo "Apex CNAME changed ($EXISTING_CONTENT → $TARGET), updating..." - METHOD="PUT" - URL="https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${EXISTING_ID}" -fi - -curl -s -X "$METHOD" "$URL" \ - -H "Authorization: Bearer ${TOKEN}" \ - -H "Content-Type: application/json" \ - --data "{ - \"type\": \"CNAME\", - \"name\": \"@\", - \"content\": \"${TARGET}\", - \"proxied\": true - }" | jq -e '.success' \ No newline at end of file diff --git a/scripts/migrate-nix.py b/scripts/migrate-nix.py new file mode 100755 index 0000000..986595a --- /dev/null +++ b/scripts/migrate-nix.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python3 +""" +Migrate domains/*.json to domains/*.nix + +Converts each JSON domain config into a .nix file matching the format +from docs/example.nix: + + { dns, ... }: { + metadata = { + description = "..."; + proxy = false; + owner = { + username = "..."; + }; + }; + records = with dns.lib.combinators; { + CNAME = [ "example.com." ]; + }; + } + +Usage: + python3 scripts/migrate-nix.py [--dry-run] [--delete-json] + +Options: + --dry-run Print generated .nix to stdout without writing files + --delete-json Delete the .json files after successful conversion +""" + +import json +import sys +from pathlib import Path + +DOMAINS_DIR = Path(__file__).resolve().parent.parent / "domains" + + +# --- Nix string helpers --- + +def escape(s: str) -> str: + """Escape a string for use inside Nix double quotes.""" + return s.replace("\\", "\\\\").replace('"', '\\"').replace("${", "\\${") + + +def fqdn(s: str) -> str: + """Ensure a domain string ends with a trailing dot.""" + return s if s.endswith(".") else s + "." + + +# --- Block builders --- + +def build_metadata(data: dict) -> list[str]: + """Build the metadata = { ... }; block.""" + owner = data.get("owner", {}) + description = data.get("description") + proxy = data.get("proxied", data.get("proxy")) + + lines = [" metadata = {"] + + if description is not None: + lines.append(f' description = "{escape(description)}";') + + if proxy is not None: + lines.append(f" proxy = {'true' if proxy else 'false'};") + + owner_keys = ["username", "email", "discord", "repo"] + owner_fields = [(k, owner[k]) for k in owner_keys if owner.get(k)] + + if owner_fields: + lines.append(" owner = {") + for key, val in owner_fields: + lines.append(f' {key} = "{escape(val)}";') + lines.append(" };") + + lines.append(" };") + return lines + + +def build_records(record: dict) -> list[str]: + """Build the records = with dns.lib.combinators; { ... }; block.""" + entries = [] + + # A records + if "A" in record: + entries.extend(string_list_record("A", as_list(record["A"]))) + + # AAAA records + if "AAAA" in record: + entries.extend(string_list_record("AAAA", as_list(record["AAAA"]))) + + # CNAME (also handles ALIAS → CNAME) + cname = record.get("CNAME") or record.get("ALIAS") + if cname is not None: + val = cname[0] if isinstance(cname, list) else cname + entries.append(f' CNAME = [ "{fqdn(val)}" ];') + + # MX records + if "MX" in record: + entries.extend(build_mx(as_list(record["MX"]))) + + # TXT records + if "TXT" in record: + escaped = [escape(v) for v in as_list(record["TXT"])] + entries.extend(string_list_record("TXT", escaped)) + + # NS records + if "NS" in record: + fqdns = [fqdn(v) for v in as_list(record["NS"])] + entries.extend(string_list_record("NS", fqdns)) + + # SRV records + if "SRV" in record: + entries.extend(build_srv(as_list(record["SRV"]))) + + # CAA records + if "CAA" in record: + entries.extend(build_caa(as_list(record["CAA"]))) + + if not entries: + return [" records = with dns.lib.combinators; {};"] + + lines = [" records = with dns.lib.combinators; {"] + lines.extend(entries) + lines.append(" };") + return lines + + +# --- Record type formatters --- + +def as_list(value) -> list: + """Wrap a scalar in a list if it isn't one already.""" + return value if isinstance(value, list) else [value] + + +def string_list_record(rtype: str, values: list[str]) -> list[str]: + """Format a record type whose values are plain strings.""" + if len(values) == 1: + return [f' {rtype} = [ "{values[0]}" ];'] + + lines = [f" {rtype} = ["] + for v in values: + lines.append(f' "{v}"') + lines.append(" ];") + return lines + + +def build_mx(values: list) -> list[str]: + """Format MX records as attrsets with preference + exchange.""" + lines = [" MX = ["] + for i, v in enumerate(values): + pref = (i + 1) * 10 + lines.append(" {") + lines.append(f" preference = {pref};") + lines.append(f' exchange = "{fqdn(v)}";') + lines.append(" }") + lines.append(" ];") + return lines + + +def build_srv(values: list[dict]) -> list[str]: + """Format SRV records.""" + lines = [" SRV = ["] + for srv in values: + lines.append(" {") + for key in ("service", "proto"): + if key in srv: + lines.append(f' {key} = "{srv[key]}";') + for key in ("priority", "weight", "port"): + if key in srv: + lines.append(f" {key} = {srv[key]};") + if "target" in srv: + lines.append(f' target = "{fqdn(srv["target"])}";') + lines.append(" }") + lines.append(" ];") + return lines + + +def build_caa(values: list[dict]) -> list[str]: + """Format CAA records.""" + lines = [" 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(caa["value"])}";') + lines.append(" }") + lines.append(" ];") + return lines + + +# --- Top-level conversion --- + +def json_to_nix(data: dict) -> str: + """Convert a parsed JSON domain config to a complete .nix file string.""" + lines = ["{ dns, ... }: {"] + lines.extend(build_metadata(data)) + lines.extend(build_records(data.get("record", {}))) + lines.append("}") + lines.append("") + return "\n".join(lines) + + +# --- File operations --- + +def migrate_file(path: Path, *, dry_run: bool, delete_json: bool) -> bool: + """Migrate a single .json file. Returns True on success.""" + try: + data = json.loads(path.read_text()) + except json.JSONDecodeError as e: + print(f" ERROR: {path.name}: {e}", file=sys.stderr) + return False + + nix = json_to_nix(data) + nix_path = path.with_suffix(".nix") + + if dry_run: + print(f"--- {nix_path.name} ---") + print(nix) + return True + + nix_path.write_text(nix) + print(f" Created {nix_path.name}") + + if delete_json: + path.unlink() + print(f" Deleted {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_DIR} not found", file=sys.stderr) + sys.exit(1) + + files = sorted(DOMAINS_DIR.glob("*.json")) + if not files: + print("No .json files found in domains/") + sys.exit(0) + + print(f"Found {len(files)} JSON file(s) to migrate") + if dry_run: + print("(dry run — no files will be written)\n") + + success = 0 + failed = 0 + + for f in files: + print(f"Migrating {f.name}...") + if migrate_file(f, dry_run=dry_run, delete_json=delete_json): + success += 1 + else: + failed += 1 + + print(f"\nDone: {success} succeeded, {failed} failed") + if failed: + sys.exit(1) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/upload-zone.sh b/scripts/upload-zone.sh new file mode 100644 index 0000000..5372311 --- /dev/null +++ b/scripts/upload-zone.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +curl \ No newline at end of file