commit d00fb41f3f04a16fe935c12e5a4132e761050c91 Author: Jannik Date: Wed Feb 25 16:50:38 2026 +0100 chore: init diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..50b148e --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# IONOS IPv6 DynDNS + +Env vars: +- IONOS_TOKEN +- IPV4 diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..737f061 --- /dev/null +++ b/bun.lock @@ -0,0 +1,25 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "dynv6", + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.3.9", "http://10.10.1.10:4873/@types/bun/-/bun-1.3.9.tgz", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="], + + "@types/node": ["@types/node@25.3.0", "http://10.10.1.10:4873/@types/node/-/node-25.3.0.tgz", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A=="], + + "bun-types": ["bun-types@1.3.9", "http://10.10.1.10:4873/bun-types/-/bun-types-1.3.9.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="], + + "typescript": ["typescript@5.9.3", "http://10.10.1.10:4873/typescript/-/typescript-5.9.3.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@7.18.2", "http://10.10.1.10:4873/undici-types/-/undici-types-7.18.2.tgz", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], + } +} diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..902e106 --- /dev/null +++ b/index.ts @@ -0,0 +1,48 @@ +import { createServer } from "node:http"; +import { networkInterfaces } from "node:os"; + +const IONOS_TOKEN = process.env.IONOS_TOKEN; +const IPV4 = process.env.IPV4; +if (!IONOS_TOKEN || !IPV4) { + console.error("Missing IONOS_TOKEN or IPV4 environment variable."); + process.exit(1); +} +const IONOS_ENDPOINT = "https://ipv4.api.hosting.ionos.com/dns/v1/dyndns?q=" + IONOS_TOKEN + "&ipv4=" + IPV4; + +function getMyIPv6() { + const ifs = networkInterfaces(); + for (const name in ifs) { + for (const iface of ifs[name]!) { + if (iface.family === "IPv6" && !iface.internal) { + return iface.address; + } + } + } + return null; +} + +const server = createServer(async (req, res) => { + const ipv6 = getMyIPv6(); + if (ipv6) { + console.log(`My IPv6 address: ${ipv6}`); + } else { + console.error("No IPv6 address found."); + res.writeHead(503); + res.end(""); + return; + } + const ionos = await fetch(IONOS_ENDPOINT + "&ipv6=" + ipv6); + if (!ionos.ok) { + console.error("Failed to update IONOS DNS record:", ionos.statusText); + res.writeHead(502); + res.end(""); + return; + } + res.writeHead(200); + res.end(""); +}); + +const PORT = 10205; +server.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`); +}); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..f73b022 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "dynv6", + "module": "index.ts", + "type": "module", + "private": true, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bfa0fea --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}