From e4335d3eb06b9b39e381b30698938856541029e3 Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Fri, 17 Feb 2023 19:09:22 +0100 Subject: [PATCH] migrate to an SSG Signed-off-by: Sam Therapy --- .editorconfig | 12 + .gitignore | 88 ++++++ .vscode/settings.json | 11 + _config.ts | 60 ++++ _werc/config.json | 5 - _werc/pub/style.css | 13 - awl/index.html | 138 --------- awl/man.txt | 309 --------------------- awl/repo.html | 4 - custom/toml/toml.ts | 6 + deno.json | 42 +++ resolver.md | 28 -- server.ts | 21 ++ src/_data.toml | 35 +++ src/_includes/layouts/base.pug | 14 + src/_includes/layouts/footer.html | 8 + src/_includes/layouts/meta.pug | 10 + src/_includes/layouts/nav.pug | 9 + src/_includes/styles/base.scss | 32 +++ src/_includes/styles/external/nord.min.css | 1 + src/_includes/styles/external/sakura.scss | 264 ++++++++++++++++++ src/_includes/styles/sakura.theme.scss | 15 + src/_includes/types.ts | 0 src/awl/index.mdx | 133 +++++++++ src/css/style.scss | 1 + index.md => src/index.mdx | 8 +- nameservers.md => src/nameservers.mdx | 8 +- src/pomme.mdx | 11 + src/resolver.mdx | 32 +++ src/static/img/favicon.png | Bin 0 -> 16762 bytes src/static/robots.txt | 5 + 31 files changed, 823 insertions(+), 500 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 _config.ts delete mode 100644 _werc/config.json delete mode 100644 _werc/pub/style.css delete mode 100644 awl/index.html delete mode 100644 awl/man.txt delete mode 100644 awl/repo.html create mode 100644 custom/toml/toml.ts create mode 100644 deno.json delete mode 100644 resolver.md create mode 100644 server.ts create mode 100644 src/_data.toml create mode 100644 src/_includes/layouts/base.pug create mode 100644 src/_includes/layouts/footer.html create mode 100644 src/_includes/layouts/meta.pug create mode 100644 src/_includes/layouts/nav.pug create mode 100644 src/_includes/styles/base.scss create mode 100644 src/_includes/styles/external/nord.min.css create mode 100644 src/_includes/styles/external/sakura.scss create mode 100644 src/_includes/styles/sakura.theme.scss create mode 100644 src/_includes/types.ts create mode 100644 src/awl/index.mdx create mode 100644 src/css/style.scss rename index.md => src/index.mdx (54%) rename nameservers.md => src/nameservers.mdx (89%) create mode 100644 src/pomme.mdx create mode 100644 src/resolver.mdx create mode 100644 src/static/img/favicon.png create mode 100644 src/static/robots.txt diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..64d3d6a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b4b6374 --- /dev/null +++ b/.gitignore @@ -0,0 +1,88 @@ +# Lume generated site +dist/ + +# DS Store +.DS_Store +._.DS_Store +**/.DS_Store +**/._.DS_Store + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless + +# FuseBox cache +.fusebox/ + +# Snyk +.dccache diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3c2d050 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "deno.enable": true, + "deno.lint": true, + "deno.unstable": true, + "[typescript]": { + "editor.defaultFormatter": "denoland.vscode-deno" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "denoland.vscode-deno" + } +} diff --git a/_config.ts b/_config.ts new file mode 100644 index 0000000..c9a347b --- /dev/null +++ b/_config.ts @@ -0,0 +1,60 @@ +import lume from "lume/mod.ts" + +// Stable plugins +import attributes from "lume/plugins/attributes.ts" +import codeHighlight from "lume/plugins/code_highlight.ts" +import esbuild from "lume/plugins/esbuild.ts" +import jsx from "lume/plugins/jsx_preact.ts" +import katex from "lume/plugins/katex.ts" +import lightningcss from "lume/plugins/lightningcss.ts" +import metas from "lume/plugins/metas.ts" +import minifyHTML from "lume/plugins/minify_html.ts" +import mdx from "lume/plugins/mdx.ts" +import pug from "lume/plugins/pug.ts" +import remark from "lume/plugins/remark.ts" +import sass from "lume/plugins/sass.ts" +import sitemap from "lume/plugins/sitemap.ts" +import sourceMaps from "lume/plugins/source_maps.ts" +import svgo from "lume/plugins/svgo.ts" + +// Experimental plugins + +// Custom plugins +import toml from "./custom/toml/toml.ts" + +const site = lume({ + src: "./src", + dest: "./dist", + location: new URL("https://samtherapy.net"), +}) + +site + .copy("static", ".") + .copy("static/.well-known", ".well-known") + .copy(".domains") + .loadData([".toml"], toml) + .use(attributes()) + .use(codeHighlight()) + .use(katex()) + .use(metas()) + .use(jsx()) + .use(mdx()) + .use(remark()) + .use(pug()) + .use(sitemap()) + .use(svgo()) + .remoteFile( + "_includes/styles/external/nord.min.css", + "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.6.0/build/styles/nord.min.css", + ) + .use(esbuild({ + extensions: [".ts", ".js"], + })) + .use(lightningcss()) + .use(sass()) + .use(minifyHTML()) + .use(sourceMaps({ + sourceContent: true, + })) + +export default site diff --git a/_werc/config.json b/_werc/config.json deleted file mode 100644 index 20fc637..0000000 --- a/_werc/config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "title": "dns.froth.zone", - "subtitle": "DNS can be frothworthy too!", - "lang": "en" -} diff --git a/_werc/pub/style.css b/_werc/pub/style.css deleted file mode 100644 index e1e1ae7..0000000 --- a/_werc/pub/style.css +++ /dev/null @@ -1,13 +0,0 @@ -body { display: flex; flex-wrap: wrap; font-family: sans; background: black; color: white} -header { flex-basis: 100%; flex-shrink: 0; } -article { flex-basis: 60%; padding-left: 1em; } -article {padding: 0.5ex 0 5vh 1vw;} -footer { flex-basis: 100%; flex-shrink: 0; } -header nav { display: flex; justify-content: space-between; } -nav a, header a { text-decoration: none ; color: #c0c0c0; } -a { color: #a0a0a0} -header h1 span { margin-left: 1em; font-size: 50%; font-style: italic; } -body > nav { flex-basis: content; padding-right: 1vw; min-width: 16em; } -nav ul { display: flex; flex-direction: column; list-style-type: none; list-style-position: outside; padding-left: 0; } -nav li ul { padding-left: 0.6em } -footer { display: flex; justify-content: space-between; } diff --git a/awl/index.html b/awl/index.html deleted file mode 100644 index 2ac0700..0000000 --- a/awl/index.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - -

- awl is a simple DNS query client, much like dig and - drill. -

-
❯ awl NS froth.zone @https://dns.froth.zone/dns-query 
-;; opcode: QUERY, status: NOERROR, id: 46274
-;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 8
-
-;; QUESTION SECTION:
-;froth.zone.    IN       NS
-
-;; ANSWER SECTION:
-froth.zone.     1650    IN      NS      illya.froth.zone.
-froth.zone.     1650    IN      NS      rin.froth.zone.
-froth.zone.     1650    IN      NS      sakura.froth.zone.
-froth.zone.     1650    IN      NS      saber.froth.zone.
-
-;; ADDITIONAL SECTION:
-rin.froth.zone. 1650    IN      AAAA    2607:5300:201:3100::931b
-sakura.froth.zone.      1650    IN      AAAA    2001:41d0:304:200::d12b
-saber.froth.zone.       1650    IN      AAAA    2602:fe90:100:2::164d:4c70
-illya.froth.zone.       1650    IN      AAAA    2603:c020:4004:62ee::8888
-rin.froth.zone. 1650    IN      A       158.69.1.114
-sakura.froth.zone.      1650    IN      A       141.94.206.97
-saber.froth.zone.       1650    IN      A       45.13.232.162
-illya.froth.zone.       1650    IN      A       129.213.157.255
-
-;; Query time: 404.9936ms
-;; SERVER: https://dns.froth.zone/dns-query
-;; WHEN: Never
-;; MSG SIZE  rcvd: 489
-
-

- awl understands DNSSEC, like - drill(1): -

-
❯ awl brokendnssec.net @1.1.1.1 --tcp
-;; opcode: QUERY, status: SERVFAIL, id: 45766
-;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
-
-;; QUESTION SECTION:
-;brokendnssec.net.      IN       A
-
-;; Query time: 6.0461ms
-;; SERVER: 1.1.1.1:53 (TCP)
-;; WHEN: Never
-;; MSG SIZE  rcvd: 34
-❯ awl brokendnssec.net @1.1.1.1 --cd +tcp
-;; opcode: QUERY, status: NOERROR, id: 37917
-;; flags: qr rd ra cd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0
-
-;; QUESTION SECTION:
-;brokendnssec.net.      IN       A
-
-;; ANSWER SECTION:
-brokendnssec.net.       294     IN      A       172.67.36.129
-brokendnssec.net.       294     IN      A       104.22.35.212
-brokendnssec.net.       294     IN      A       104.22.34.212
-
-;; Query time: 8.4461ms
-;; SERVER: 1.1.1.1:53 (TCP)
-;; WHEN: Never
-;; MSG SIZE  rcvd: 130
-
-

- It supports many of the flags that - dig(1) does: -

-
❯ awl +noquestion +noauthority +nostats cat-v.org
-;; opcode: QUERY, status: NOERROR, id: 39675
-;; flags: qr rd ra; QUERY: 0, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
-
-;; ANSWER SECTION:
-cat-v.org.      9418    IN      A       168.235.69.224
-
-

And some new features, too!

-
-
❯ awl +quic --xml codeberg.org
- <Message>
-  <opcode>QUERY</opcode>
-  <status>NOERROR</status>
-  <id>51837</id>
-  <response>true</response>
-  <authoritative>false</authoritative>
-  <truncated>false</truncated>
-  <recursionDesired>true</recursionDesired>
-  <recursionAvailable>true</recursionAvailable>
-  <zero>false</zero>
-  <authenticatedData>false</authenticatedData>
-  <checkingDisabled>false</checkingDisabled>
-  <opt>
-    <name>Version</name>
-  <value>0</value>
-  </opt>
-  <opt>
-    <name>Flags</name>
-    <value></value>
-  </opt>
-  <opt>
-    <name>UDP Buffer Size</name>
-    <value>1232</value>
-  </opt>
-  <question>
-    <name>codeberg.org.</name>
-    <class>IN</class>
-    <type>A</type>
-  </question>
-  <answer>
-    <response>193.26.156.135</response>
-    <name>codeberg.org.</name>
-    <ttl>449</ttl>
-    <class>IN</class>
-    <type>A</type>
-  </answer>
-  <queryTime>128.726593ms</queryTime>
-  <server>dns.adguard.com:853 (QUIC)</server>
-  <when>Thu, 13 Oct 2022 15:29:58 +0200</when>
-  <msgSize>69</msgSize>
-</Message>
-
-
-

- A full list of the features awl supports can be found - here. -

diff --git a/awl/man.txt b/awl/man.txt deleted file mode 100644 index 1609d90..0000000 --- a/awl/man.txt +++ /dev/null @@ -1,309 +0,0 @@ -NAME - -awl - DNS lookup tool - -SYNOPSIS - -awl [ OPTIONS ] name [ @server ] [ type ], where - -name is the query to make (example: froth.zone) -@server is the server to query (example: dns.froth.zone) -type is the DNS resource type (example: AAAA) - -DESCRIPTION - -awl (awls want licorice) is a simple tool designed to make DNS queries, -much like the venerable dig(1). An awl is a tool used to make small -holes, typically used in leatherworking. - -awl is designed to be a more "modern" version of drill(1) by including -some more recent RFCs and output options. - -When no arguments are given, awl will perform an NS query on the root -('.'). - -When a nameserver is not given, awl will query a random system -nameserver. If one cannot be found, awl will query localhost. - -OPTIONS - -Anything in [brackets] is optional. - --D, --dnssec, +dnssec - - Enable DNSSEC. This needs to be manually enabled. - --v[=int] - - Set verbosity Accepted values are as follows: - - - 0: Only log errors. - - - 1: Log warnings. This is the default. - - - 2: Log information Default when specifying just -v. - - - 3: Log information useful for debugging. - - Setting a value lower than 0 disables logging entirely. - - By default, specifying just -v sets the verbosity to 2 (info). - --V - - Print the version and exit. - --h - - Show a "short" help message. - -Query Options - --4 - - Only make query over IPv4 - --6 - - Only make query over IPv6 - --p, --port port - - Sets the port to query. Default ports listed below. - - - 53 for UDP and TCP - - - 853 for TLS and QUIC - - - 443 for HTTPS - --q, --query domain - - Domain to query (eg. example.com) - --c, --class class - - DNS class to query (eg. IN, CH) The default is IN. - --t, --qType type - - DNS type to query (eg. A, AAAA, NS) The default is A. - ---no-truncate, +ignore - - Ignore UDP truncation (by default, awl retries with TCP). - ---no-bad-cookie, +[no]badcookie - - [Do not] ignore BADCOOKIE responses - ---tcp, +tcp, +vc - - Use TCP for the query (see RFC 7766). - ---dnscrypt, +dnscrypt - - Use DNSCrypt. - --T, --tls, +tls - - Use DNS-over-TLS, implies --tcp (see RFC 7858) - ---tls-host string - - Set hostname to use for TLS certificate validation. Default is the - name of the domain when querying over TLS, and empty for IPs. - ---tls-no-verify - - Ignore TLS validation when performing a DNS query. - --H. --https, +https - - Use DNS-over-HTTPS (see RFC 8484). - --Q. --quic, +quic - - Use DNS-over-QUIC (see RFC 9250). - --x, --reverse - - Do a reverse lookup. Sets default type to PTR. awl automatically makes - an IP or phone number canonical. - ---timeout seconds, +timeout=seconds - - Set the timeout period. Floating point numbers are accepted. 0.5 - seconds is the minimum. - ---retries int, +tries=int, +retry=int - - Set the number of retries. Retry is one more than tries, dig style. - -DNS Flags - ---aa[=bool], +[no]aaflag - - (Set, Unset) AA (Authoritative Answer) flag. - ---ad[=bool], +[no]adflag - - (Set, Unset) AD (Authenticated Data) flag. - ---tc[=bool], +[no]tcflag - - (Set, Unset) TC (TrunCated) flag - --z[=bool], +[no]zflag - - (Set, Unset) Z (Zero) flag. - ---cd[=bool], +[no]cdflag - - (Set, Unset) CD (Checking Disabled) flag. - ---qr[=bool], +[no]qrflag - - (Set, Unset) QR (QueRy) flag. - ---rd[=bool], +[no]rdflag - - (Set, Unset) RD (Recursion Desired) flag. - ---ra[=bool], +[no]raflag - - (Set, Unset) RA (Recursion Available) flag. - -EDNS - -All of these options except disabling EDNS imply +edns. - ---no-edns, +noedns - - Disable EDNS. - ---edns-ver, +edns[=int] - - Enable EDNS and set EDNS version. The maximum value is 255, and the - minimum (default) value is 0. - ---expire. +[no]expire - - Send an EDNS Expire. - ---nsid, +[no]nsid - - Send an EDNS name server ID request. - ---no-cookie, +[no]cookie[=string] - - Send an EDNS cookie. This is enabled by default with a random string. - ---keep-alive, +[no]keepalive, +[no]keepopen - - Send an EDNS keep-alive. This does nothing unless using TCP. - ---buffer-size int, +bufize=int - - Set the UDP message buffer size, using EDNS. Max is 65535, minimum is - zero. The default value is 1232. - ---zflag int, +ednsflags=int - - Set the must-be-zero EDNS flags. Decimal, hexadecimal and octal are - supported. Trying to set DO will be ignored. - ---subnet ip[/prefix], +[no]subnet=ip[/prefix] - - Send an EDNS Client Subnet option with the specified address. - - Like dig(1), setting the IP to 0.0.0.0/0, ::/0 or 0 will signal the - resolver to not use any client information when returning the query. - -Output Display - ---no-question, +[no]question - - Toggle the display of the Question section. - ---no-answer, +[no]answer - - Toggle the display of the Answer section. - ---no-answer, +[no]answer - - Toggle the display of the Answer section. - ---no-authority, +[no]authority - - Toggle the display of the Authority section. - ---no-additional, +[no]additional - - Toggle the display of the Additional section. - ---no-statistics, +[no]stats - - Toggle the display of the Statistics (additional comments) section. - -Output Formats - --j, --json, +json - - Print the query results as JSON. - --X, --xml, +xml - - Print the query results as XML. - --y, --yaml, +yaml - - Print the query results as YAML. - --s, --short, +short - - Print just the address of the answer. - -EXIT STATUS - -The exit code is 0 when a query is successfully made and received. This -includes SERVFAILs, NOTIMPL among others. - -EXAMPLES - - awl grumbulon.xyz -j +cd - -Run a query of your local resolver for the A records of grumbulon.xyz, -print them as JSON and disable DNSSEC verification. - - awl +short example.com AAAA @1.1.1.1 - -Query 1.1.1.1 for the AAAA records of example.com, print just the -answers - - awl -xT PTR 8.8.4.4 @dns.google - -Query dns.google over TLS for the PTR record to the IP address 8.8.4.4 - -SEE ALSO - -drill(1), dig(1) - -STANDARDS - -RFC 1034,1035 (UDP), 7766 (TCP), 7858 (TLS), 8484 (HTTPS), 9230 (QUIC) - -Probably more, https://www.statdns.com/rfc - -BUGS - -OPT records are only printed when using a standard output, not -JSON/XML/YAML. - -Full parity with dig(1) is not complete. - -This man page is probably not complete. - -Likely numerous more, report them either to the tracker -https://git.froth.zone/sam/awl/issues or via email -~sammefishe/awl-dev@lists.sr.ht diff --git a/awl/repo.html b/awl/repo.html deleted file mode 100644 index 9bc7fae..0000000 --- a/awl/repo.html +++ /dev/null @@ -1,4 +0,0 @@ -https://git.froth.zone/sam/awl - \ No newline at end of file diff --git a/custom/toml/toml.ts b/custom/toml/toml.ts new file mode 100644 index 0000000..7789bc0 --- /dev/null +++ b/custom/toml/toml.ts @@ -0,0 +1,6 @@ +import { parse } from "std/encoding/toml.ts" + +export default async function toml(path: string | URL) { + const content = await Deno.readTextFile(path) + return parse(content) +} diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..3fb6bd5 --- /dev/null +++ b/deno.json @@ -0,0 +1,42 @@ +{ + "tasks": { + "lume": "echo \"import 'lume/cli.ts'\" | deno run --unstable -A -", + "build": "deno task lume", + "serve": "deno task lume -s" + }, + "lock": false, + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "npm:preact", + "lib": [ + "dom", + "dom.iterable", + "dom.asynciterable", + "deno.ns" + ] + }, + "imports": { + "lume/": "https://deno.land/x/lume@v1.15.2/", + "experimental/": "https://raw.githubusercontent.com/lumeland/experimental-plugins/main/", + "std/": "https://deno.land/std/" + }, + "lint": { + "files": { + "exclude": [ + "src/_includes/styles/external/", + "dist/" + ] + } + }, + "fmt": { + "options": { + "semiColons": false + }, + "files": { + "exclude": [ + "src/_includes/styles/external/", + "dist" + ] + } + } +} diff --git a/resolver.md b/resolver.md deleted file mode 100644 index d71f71f..0000000 --- a/resolver.md +++ /dev/null @@ -1,28 +0,0 @@ -# Froth.zone DNS resolving service - -I also host an [OpenNIC](https://www.opennic.org/)-compatible DNS resolving server. - -### Never asked questions (NAQs) - -- *Why OpenNIC?* - : Why not? The root servers seamlessly connect to ICANN space, so it's just free extra domains. - -- *Where is it?* - : Right here. This domain you're looking at right now. - -- *What about DNS-over-TCP?* - : Yes. - -- *DNS-over-TLS?* - : Yes, DNS-over-TLS too. - -- *DNSCrypt?* - : Nope. ¯\\\_(ツ)\_/¯ \ -Maybe when it becomes an RFC. - -- *What about DNS-over-HTTPS?* - : Yes! Use __https://dns.froth.zone/dns-query__ as the endpoint URL. - -- *What about QUIC?* - : Since the software I use doesn't support QUIC yet - (I don't think _any_ do yet), no. Maybe soon(tm) \ No newline at end of file diff --git a/server.ts b/server.ts new file mode 100644 index 0000000..70cb8d2 --- /dev/null +++ b/server.ts @@ -0,0 +1,21 @@ +import Server from "lume/core/server.ts" +import expires from "lume/middlewares/expires.ts" +import not_found from "lume/middlewares/not_found.ts" + +const port = 8000 + +const server = new Server({ + port: port, + root: `${Deno.cwd()}/dist`, +}) + +server.use(expires()) + +server.use(not_found({ + root: `${Deno.cwd()}/dist`, + page404: "404.html", +})) + +server.start() + +console.log(`Listening on http://localhost:${port}`) diff --git a/src/_data.toml b/src/_data.toml new file mode 100644 index 0000000..12db5ea --- /dev/null +++ b/src/_data.toml @@ -0,0 +1,35 @@ +# Site metas from Lume metas plugin +[metas] +site = "Froth DNS" +lang = "en" +description = "All you need to know about the services provided by dns.froth.zone" +# icon = "img/favicon.png" +keywords = ["DNS", "nameserver", "DNS server", "froth.zone", "froth DNS", "froth zone"] +generator = true + +[[menu.left]] +name = "/" +url = "/" + +[[menu.left]] +name = "Nameservers" +url = "/nameservers" + +[[menu.left]] +name = "Resolver" +url = "/resolver" + +[[menu.left]] +name = "awl" +url = "/awl" + +[[menu.left]] +name = "pomme" +url = "/pomme" + +[[menu.right]] +name = "Status" +url = "https://status.froth.zone/status/dns" + +[mergedKeys] +metas = "object" diff --git a/src/_includes/layouts/base.pug b/src/_includes/layouts/base.pug new file mode 100644 index 0000000..36d45e1 --- /dev/null +++ b/src/_includes/layouts/base.pug @@ -0,0 +1,14 @@ +doctype html +html(lang=metas.lang) + head + title= title + include meta.pug + include nav.pug + body + header + h1= title + h2= subtitle + main + | !{content} + footer + include footer.html diff --git a/src/_includes/layouts/footer.html b/src/_includes/layouts/footer.html new file mode 100644 index 0000000..5bed6ac --- /dev/null +++ b/src/_includes/layouts/footer.html @@ -0,0 +1,8 @@ + + + Built with Lume + • + Source + • + Owner + diff --git a/src/_includes/layouts/meta.pug b/src/_includes/layouts/meta.pug new file mode 100644 index 0000000..b08f1b2 --- /dev/null +++ b/src/_includes/layouts/meta.pug @@ -0,0 +1,10 @@ +if goimport + meta(name="go-import", content=`${goimport.url} ${goimport.vcs} ${goimport.repo}`) + if goimport.gitea + meta(name="go-source", content=`${goimport.url} ${goimport.repo} ${goimport.repo}/tree/${goimport.branch}\{/dir\} ${goimport.repo}/src/branch/${goimport.branch}\{/dir\}/\{file\}\#L\{line\}`) + else + meta(name="go-source", content=`${goimport.url} ${goimport.repo} ${goimport.repo}/tree/${goimport.branch}\{/dir\} ${goimport.repo}/blob/${goimport.branch}\{/dir\}/\{file\}\#L\{line\}`) +meta(charset="utf-8") +meta(name='viewport', content='width=device-width, initial-scale=1') +link(rel="shortcut icon", href="/img/favicon.png", type="image/png") +link(rel="stylesheet", href="/css/style.css") diff --git a/src/_includes/layouts/nav.pug b/src/_includes/layouts/nav.pug new file mode 100644 index 0000000..7277013 --- /dev/null +++ b/src/_includes/layouts/nav.pug @@ -0,0 +1,9 @@ +nav + ul(class= "main-nav") + each val in menu.left + li + a(href=val.url)= val.name + + each val in menu.right + li(class= "push") + a(href=val.url)= val.name diff --git a/src/_includes/styles/base.scss b/src/_includes/styles/base.scss new file mode 100644 index 0000000..280bcd2 --- /dev/null +++ b/src/_includes/styles/base.scss @@ -0,0 +1,32 @@ +@use "styles/sakura.theme"; + +// Highlight.js theme +@use "styles/external/nord.min"; + + +:root { + font-family: -apple-system, BlinkMacSystemFont, "Avenir Next", Avenir, "Nimbus Sans L", Roboto, Noto, "Segoe UI", Arial, Helvetica, "Helvetica Neue", sans-serif; +} + +.row { + display: flex; +} + +.column { + flex: 50%; +} + +.main-nav { + display: flex; + list-style: none; + max-width: 75%; +} + +nav li { + margin: 0.5em; +} + +.push { + justify-content: flex-end; + margin-left: auto; +} diff --git a/src/_includes/styles/external/nord.min.css b/src/_includes/styles/external/nord.min.css new file mode 100644 index 0000000..efbb0c1 --- /dev/null +++ b/src/_includes/styles/external/nord.min.css @@ -0,0 +1 @@ +pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#2e3440}.hljs,.hljs-subst{color:#d8dee9}.hljs-selector-tag{color:#81a1c1}.hljs-selector-id{color:#8fbcbb;font-weight:700}.hljs-selector-attr,.hljs-selector-class{color:#8fbcbb}.hljs-property,.hljs-selector-pseudo{color:#88c0d0}.hljs-addition{background-color:rgba(163,190,140,.5)}.hljs-deletion{background-color:rgba(191,97,106,.5)}.hljs-built_in,.hljs-class,.hljs-type{color:#8fbcbb}.hljs-function,.hljs-function>.hljs-title,.hljs-title.hljs-function{color:#88c0d0}.hljs-keyword,.hljs-literal,.hljs-symbol{color:#81a1c1}.hljs-number{color:#b48ead}.hljs-regexp{color:#ebcb8b}.hljs-string{color:#a3be8c}.hljs-title{color:#8fbcbb}.hljs-params{color:#d8dee9}.hljs-bullet{color:#81a1c1}.hljs-code{color:#8fbcbb}.hljs-emphasis{font-style:italic}.hljs-formula{color:#8fbcbb}.hljs-strong{font-weight:700}.hljs-link:hover{text-decoration:underline}.hljs-comment,.hljs-quote{color:#4c566a}.hljs-doctag{color:#8fbcbb}.hljs-meta,.hljs-meta .hljs-keyword{color:#5e81ac}.hljs-meta .hljs-string{color:#a3be8c}.hljs-attr{color:#8fbcbb}.hljs-attribute{color:#d8dee9}.hljs-name{color:#81a1c1}.hljs-section{color:#88c0d0}.hljs-tag{color:#81a1c1}.hljs-template-variable,.hljs-variable{color:#d8dee9}.hljs-template-tag{color:#5e81ac}.language-abnf .hljs-attribute{color:#88c0d0}.language-abnf .hljs-symbol{color:#ebcb8b}.language-apache .hljs-attribute{color:#88c0d0}.language-apache .hljs-section{color:#81a1c1}.language-arduino .hljs-built_in{color:#88c0d0}.language-aspectj .hljs-meta{color:#d08770}.language-aspectj>.hljs-title{color:#88c0d0}.language-bnf .hljs-attribute{color:#8fbcbb}.language-clojure .hljs-name{color:#88c0d0}.language-clojure .hljs-symbol{color:#ebcb8b}.language-coq .hljs-built_in{color:#88c0d0}.language-cpp .hljs-meta .hljs-string{color:#8fbcbb}.language-css .hljs-built_in{color:#88c0d0}.language-css .hljs-keyword{color:#d08770}.language-diff .hljs-meta,.language-ebnf .hljs-attribute{color:#8fbcbb}.language-glsl .hljs-built_in{color:#88c0d0}.language-groovy .hljs-meta:not(:first-child),.language-haxe .hljs-meta,.language-java .hljs-meta{color:#d08770}.language-ldif .hljs-attribute{color:#8fbcbb}.language-lisp .hljs-name,.language-lua .hljs-built_in,.language-moonscript .hljs-built_in,.language-nginx .hljs-attribute{color:#88c0d0}.language-nginx .hljs-section{color:#5e81ac}.language-pf .hljs-built_in,.language-processing .hljs-built_in{color:#88c0d0}.language-scss .hljs-keyword,.language-stylus .hljs-keyword{color:#81a1c1}.language-swift .hljs-meta{color:#d08770}.language-vim .hljs-built_in{color:#88c0d0;font-style:italic}.language-yaml .hljs-meta{color:#d08770} \ No newline at end of file diff --git a/src/_includes/styles/external/sakura.scss b/src/_includes/styles/external/sakura.scss new file mode 100644 index 0000000..5aa9bc5 --- /dev/null +++ b/src/_includes/styles/external/sakura.scss @@ -0,0 +1,264 @@ +/* Sakura.css v1.4.1 + * ================ + * Minimal css theme. + * Project: https://github.com/oxalorg/sakura/ + */ + +/* Body */ + +html { + font-size: 62.5%; // So that root size becomes 10px + font-family: $font-family-base; +} + +body { + // $font-size-base must be a rem value + font-size: $font-size-base; + line-height: 1.618; + max-width: 60em; + margin: auto; + color: $color-text; + background-color: $color-bg; + padding: 13px; +} + +@media (max-width: 684px) { + body { + font-size: $font-size-base * 0.85; + } +} + +@media (max-width: 382px) { + body { + font-size: $font-size-base * 0.75; + } +} + +@mixin word-wrap() { + overflow-wrap: break-word; + word-wrap: break-word; + -ms-word-break: break-all; + word-break: break-word; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + line-height: 1.1; + font-family: $font-family-heading; + font-weight: 700; + margin-top: 3rem; + margin-bottom: 1.5rem; + @include word-wrap; +} + +h1 { + font-size: 2.35em +} + +h2 { + font-size: 2.00em +} + +h3 { + font-size: 1.75em +} + +h4 { + font-size: 1.5em +} + +h5 { + font-size: 1.25em +} + +h6 { + font-size: 1em +} + +p { + margin-top: 0px; + margin-bottom: 2.5rem; +} + +small, +sub, +sup { + font-size: 75%; +} + +hr { + border-color: $color-blossom; +} + +a { + text-decoration: none; + color: $color-blossom; + + &:visited { + color: darken($color-blossom, 10%); + } + + &:hover { + color: $color-fade; + border-bottom: 2px solid $color-text; + } + +} + +ul { + padding-left: 1.4em; + margin-top: 0px; + margin-bottom: 2.5rem; +} + +li { + margin-bottom: 0.4em; +} + +blockquote { + margin-left: 0px; + margin-right: 0px; + padding-left: 1em; + padding-top: 0.8em; + padding-bottom: 0.8em; + padding-right: 0.8em; + border-left: 5px solid $color-blossom; + margin-bottom: 2.5rem; + background-color: $color-bg-alt; +} + +blockquote p { + margin-bottom: 0; +} + +img, +video { + height: auto; + max-width: 100%; + margin-top: 0px; + margin-bottom: 2.5rem; +} + +/* Pre and Code */ + +pre { + background-color: $color-bg-alt; + display: block; + padding: 1em; + overflow-x: auto; + margin-top: 0px; + margin-bottom: 2.5rem; + font-size: 0.9em; +} + +code, +kbd, +samp { + font-size: 0.9em; + padding: 0 0.5em; + background-color: $color-bg-alt; + white-space: pre-wrap; +} + +pre>code { + padding: 0; + background-color: transparent; + white-space: pre; + font-size: 1em; +} + +/* Tables */ + +table { + text-align: justify; + width: 100%; + border-collapse: collapse; +} + +td, +th { + padding: 0.5em; + border-bottom: 1px solid $color-bg-alt; +} + +/* Buttons, forms and input */ + +input, +textarea { + border: 1px solid $color-text; + + &:focus { + border: 1px solid $color-blossom; + } +} + +textarea { + width: 100%; +} + +.button, +button, +input[type="submit"], +input[type="reset"], +input[type="button"] { + display: inline-block; + padding: 5px 10px; + text-align: center; + text-decoration: none; + white-space: nowrap; + + background-color: $color-blossom; + color: $color-bg; + border-radius: 1px; + border: 1px solid $color-blossom; + cursor: pointer; + box-sizing: border-box; + + &[disabled] { + cursor: default; + opacity: .5; + } + + &:focus:enabled, + &:hover:enabled { + background-color: $color-fade; + border-color: $color-fade; + color: $color-bg; + outline: 0; + } +} + +textarea, +select, +input { + color: $color-text; + padding: 6px 10px; + /* The 6px vertically centers text on FF, ignored by Webkit */ + margin-bottom: 10px; + background-color: $color-bg-alt; + border: 1px solid $color-bg-alt; + border-radius: 4px; + box-shadow: none; + box-sizing: border-box; + + &:focus { + border: 1px solid $color-blossom; + outline: 0; + } +} + +input[type="checkbox"]:focus { + outline: 1px dotted $color-blossom; +} + +label, +legend, +fieldset { + display: block; + margin-bottom: .5rem; + font-weight: 600; +} diff --git a/src/_includes/styles/sakura.theme.scss b/src/_includes/styles/sakura.theme.scss new file mode 100644 index 0000000..faf575a --- /dev/null +++ b/src/_includes/styles/sakura.theme.scss @@ -0,0 +1,15 @@ +// Settings for SakuraCSS +$color-blossom: #ffffff; +$color-fade: #c9c9c9; + +$color-bg: #222222; +$color-bg-alt: #4a4a4a; + +/* $color-text: #dedce5; */ +$color-text: #c9c9c9; +$font-size-base: 1.8rem; + +$font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; +$font-family-heading: $font-family-base; + +@import "styles/external/sakura"; diff --git a/src/_includes/types.ts b/src/_includes/types.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/awl/index.mdx b/src/awl/index.mdx new file mode 100644 index 0000000..426f69b --- /dev/null +++ b/src/awl/index.mdx @@ -0,0 +1,133 @@ +--- +title: Froth DNS Service +subtitle: Welcome to the Froth DNS service! +goimport: + url: dns.froth.zone/awl + vcs: git + repo: https://git.froth.zone/sam/awl + branch: master + gitea: true +layout: layouts/base.pug +--- + +[awl](https://git.froth.zone/sam/awl) is a simple DNS query client, much like dig and drill. + + +``` +❯ awl NS froth.zone @https://dns.froth.zone/dns-query +;; opcode: QUERY, status: NOERROR, id: 46274 +;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 8 + +;; QUESTION SECTION: +;froth.zone. IN NS + +;; ANSWER SECTION: +froth.zone. 1650 IN NS illya.froth.zone. +froth.zone. 1650 IN NS rin.froth.zone. +froth.zone. 1650 IN NS sakura.froth.zone. +froth.zone. 1650 IN NS saber.froth.zone. + +;; ADDITIONAL SECTION: +rin.froth.zone. 1650 IN AAAA 2607:5300:201:3100::931b +sakura.froth.zone. 1650 IN AAAA 2001:41d0:304:200::d12b +saber.froth.zone. 1650 IN AAAA 2602:fe90:100:2::164d:4c70 +illya.froth.zone. 1650 IN AAAA 2603:c020:4004:62ee::8888 +rin.froth.zone. 1650 IN A 158.69.1.114 +sakura.froth.zone. 1650 IN A 141.94.206.97 +saber.froth.zone. 1650 IN A 45.13.232.162 +illya.froth.zone. 1650 IN A 129.213.157.255 + +;; Query time: 404.9936ms +;; SERVER: https://dns.froth.zone/dns-query +;; WHEN: Never +;; MSG SIZE rcvd: 489 +``` + +--- + +`awl` understands DNSSEC, like [`drill(1)`](https://linux.die.net/man/1/drill): + +``` +❯ awl brokendnssec.net @1.1.1.1 --tcp +;; opcode: QUERY, status: SERVFAIL, id: 45766 +;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;brokendnssec.net. IN A + +;; Query time: 6.0461ms +;; SERVER: 1.1.1.1:53 (TCP) +;; WHEN: Never +;; MSG SIZE rcvd: 34 +❯ awl brokendnssec.net @1.1.1.1 --cd +tcp +;; opcode: QUERY, status: NOERROR, id: 37917 +;; flags: qr rd ra cd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;brokendnssec.net. IN A + +;; ANSWER SECTION: +brokendnssec.net. 294 IN A 172.67.36.129 +brokendnssec.net. 294 IN A 104.22.35.212 +brokendnssec.net. 294 IN A 104.22.34.212 + +;; Query time: 8.4461ms +;; SERVER: 1.1.1.1:53 (TCP) +;; WHEN: Never +;; MSG SIZE rcvd: 130 +``` + +--- + +It supports many of the flags that [`dig(1)`](https://man.openbsd.org/dig.1) +does: + +``` +❯ awl +noquestion +noauthority +nostats cat-v.org +;; opcode: QUERY, status: NOERROR, id: 39675 +;; flags: qr rd ra; QUERY: 0, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; ANSWER SECTION: +cat-v.org. 9418 IN A 168.235.69.224 +``` + +--- + +And [some new features](./man), too! + +```xml +❯ awl +quic --xml codeberg.org + + 22108 + true + 0 + false + false + true + true + false + false + false + 0 + false + + codeberg.org. + 1 + 1 + + + + codeberg.org. + 1 + 1 + 3600 + 4 + + 193.26.156.135 + + +``` + +------ + +A full list of the features `awl` supports can be found [here](https://git.froth.zone/sam/awl/wiki/Supported). diff --git a/src/css/style.scss b/src/css/style.scss new file mode 100644 index 0000000..619e85f --- /dev/null +++ b/src/css/style.scss @@ -0,0 +1 @@ +@use "styles/base"; diff --git a/index.md b/src/index.mdx similarity index 54% rename from index.md rename to src/index.mdx index cbdb162..45c8bad 100644 --- a/index.md +++ b/src/index.mdx @@ -1,5 +1,11 @@ -# Welcome to the Froth DNS service! --- +title: Froth DNS Service +subtitle: Welcome to the Froth DNS service! +layout: layouts/base.pug +--- + +#### Hello there. + This is a landing page for the DNS services that I host. \ diff --git a/nameservers.md b/src/nameservers.mdx similarity index 89% rename from nameservers.md rename to src/nameservers.mdx index 389d257..b31573d 100644 --- a/nameservers.md +++ b/src/nameservers.mdx @@ -1,10 +1,14 @@ -# Froth.zone Nameservers +--- +title: Froth.zone Nameservers +subtitle: +layout: layouts/base.pug +--- I host four nameservers in servers all over ~~NATO~~ the world, so anybody ~~in the US or Western Europe~~ can more easily access my services: -The IP addresses are found by using [awl](./awl/): +The IP addresses are found by using [awl](/awl/): - [rin.froth.zone](https://rin.froth.zone) (Hosted in Canada)\ ❯ awl +short rin.froth.zone && awl +short AAAA rin.froth.zone diff --git a/src/pomme.mdx b/src/pomme.mdx new file mode 100644 index 0000000..3926bb4 --- /dev/null +++ b/src/pomme.mdx @@ -0,0 +1,11 @@ +--- +title: Pomme - A web interface +subtitle: Coming soon :) +goimport: + url: dns.froth.zone/pomme + vcs: git + repo: https://git.freecumextremist.com/grumbulon/pomme + branch: master + gitea: true +layout: layouts/base.pug +--- diff --git a/src/resolver.mdx b/src/resolver.mdx new file mode 100644 index 0000000..489db3c --- /dev/null +++ b/src/resolver.mdx @@ -0,0 +1,32 @@ +--- +title: Froth.zone DNS resolving service +subtitle: +layout: layouts/base.pug +--- + +I also host an [OpenNIC](https://www.opennic.org/)-compatible DNS resolving server. + +### Never asked questions (NAQs) + +- *Why OpenNIC?* \ + Why not? The root servers seamlessly connect to ICANN space, so it's just free extra domains. + +- *Where is it?* \ + Right here. This domain you're looking at right now. + +- *What about DNS-over-TCP?* \ + Yes. + +- *DNS-over-TLS?* \ + Yes, DNS-over-TLS too. + +- *DNSCrypt?* \ + Nope. ¯\\\_(ツ)\_/¯ \ +Maybe when it becomes an RFC. + +- *What about DNS-over-HTTPS?* \ + Yes! Use __https://dns.froth.zone/dns-query__ as the endpoint URL. + +- *What about QUIC?* \ + Since the software I use doesn't support QUIC yet \ + (I don't think _any_ do yet), no. Maybe soon(tm) diff --git a/src/static/img/favicon.png b/src/static/img/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..6e027bf76f3a7c10ebe63a199d0e3dd2651db212 GIT binary patch literal 16762 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 literal 0 HcmV?d00001 diff --git a/src/static/robots.txt b/src/static/robots.txt new file mode 100644 index 0000000..37bb545 --- /dev/null +++ b/src/static/robots.txt @@ -0,0 +1,5 @@ +User-agent: * +Allow: / +Disallow: /dns-query/ + +Sitemap: http://localhost:3000/sitemap.xml