4 Commits

Author SHA1 Message Date
mxmlndml
6695db9d31 1.0.1 2023-08-28 19:39:24 +02:00
mxmlndml
64de314ea5 fix node script 2023-08-28 19:37:47 +02:00
mxmlndml
869d0d4e97 bump version 2023-08-28 19:12:52 +02:00
mxmlndml
027d499875 support multiple domains 2023-08-28 19:11:35 +02:00
5 changed files with 57 additions and 22 deletions

View File

@@ -5,6 +5,6 @@ services:
environment: environment:
- API_KEY=123 - API_KEY=123
- ZONE_ID=023e105f4ecef8ad9ca31a8372d0c353 - ZONE_ID=023e105f4ecef8ad9ca31a8372d0c353
- DOMAIN_NAME=dyndns.example.com - DOMAIN_NAMES=dyndns.example.com,example.com
- INTERVAL=5 - INTERVAL=5
- LOG_LEVEL=INFO - LOG_LEVEL=INFO

View File

@@ -1,11 +1,12 @@
{ {
"name": "cloudflare-dynamic-dns", "name": "cloudflare-dynamic-dns",
"version": "0.1.0", "version": "1.0.1",
"description": "", "description": "",
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {
"build": "rimraf ./dist && tsc", "start": "node .",
"start": "pnpm build && node ." "dev": "tsc -w",
"build": "rimraf ./dist && tsc"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",

View File

@@ -17,7 +17,7 @@ const getDnsRecord = async (
name: string, name: string,
type: string, type: string,
apiKey: string, apiKey: string,
): Promise<{ content: string; id: string }> => { ): Promise<{ content: string; name: string; id: string }> => {
const url = const url =
`https://api.cloudflare.com/client/v4/zones/${zoneIdentifier}/dns_records?name=${name}&type=${type}`; `https://api.cloudflare.com/client/v4/zones/${zoneIdentifier}/dns_records?name=${name}&type=${type}`;
const headers = { const headers = {
@@ -28,7 +28,7 @@ const getDnsRecord = async (
const json: CFResponse = await response.json(); const json: CFResponse = await response.json();
if (json.success) { if (json.success) {
return (({ content, id }) => ({ content, id }))(json.result[0]); return (({ content, name, id }) => ({ content, name, id }))(json.result[0]);
} }
const error = json.errors.reduce( const error = json.errors.reduce(
@@ -40,6 +40,16 @@ const getDnsRecord = async (
); );
}; };
const getDnsRecords = async (
zoneIdentifier: string,
names: string[],
type: string,
apiKey: string,
): Promise<{ content: string; name: string; id: string }[]> =>
await Promise.all(
names.map(async (name) => getDnsRecord(zoneIdentifier, name, type, apiKey)),
);
const patchDnsRecord = async ( const patchDnsRecord = async (
zoneIdentifier: string, zoneIdentifier: string,
identifier: string, identifier: string,
@@ -77,4 +87,22 @@ const patchDnsRecord = async (
); );
}; };
export { getDnsRecord, patchDnsRecord }; const patchDnsRecords = async (
dnsRecords: { content: string; name: string; id: string }[],
zoneIdentifier: string,
apiKey: string,
content: string,
type: string,
) =>
dnsRecords.forEach(async (dnsRecord) =>
await patchDnsRecord(
zoneIdentifier,
dnsRecord.id,
apiKey,
content,
dnsRecord.name,
type,
)
);
export { getDnsRecord, getDnsRecords, patchDnsRecord, patchDnsRecords };

View File

@@ -1,49 +1,55 @@
import { getDnsRecord, patchDnsRecord } from "./cloudflare"; import { getDnsRecords, patchDnsRecords } from "./cloudflare";
import getPublicIp from "./getPublicIp"; import getPublicIp from "./getPublicIp";
import * as log from "./log"; import * as log from "./log";
const { ZONE_ID, DOMAIN_NAME, API_KEY, INTERVAL } = process.env; const { ZONE_ID, DOMAIN_NAMES, API_KEY, INTERVAL } = process.env;
if (ZONE_ID === undefined) { if (ZONE_ID === undefined) {
log.error("could not access environment variable 'ZONE_ID'"); log.error("could not access environment variable 'ZONE_ID'");
} }
if (DOMAIN_NAME === undefined) { if (DOMAIN_NAMES === undefined) {
log.error("could not access environment variable 'DOMAIN_NAME'"); log.error("could not access environment variable 'DOMAIN_NAMES'");
} }
if (API_KEY === undefined) { if (API_KEY === undefined) {
log.error("could not access environment variable 'API_KEY'"); log.error("could not access environment variable 'API_KEY'");
} }
if ( if (
ZONE_ID === undefined || DOMAIN_NAME === undefined || API_KEY === undefined ZONE_ID === undefined || DOMAIN_NAMES === undefined || API_KEY === undefined
) { ) {
process.exit(1); process.exit(1);
} }
const dynamicDns = async () => { const dynamicDns = async () => {
const domainNames = DOMAIN_NAMES.split(",");
try { try {
const [publicIp, dnsRecord] = await Promise.all([ const [publicIp, dnsRecords] = await Promise.all([
getPublicIp(), getPublicIp(),
getDnsRecord(ZONE_ID, DOMAIN_NAME, "A", API_KEY), getDnsRecords(ZONE_ID, domainNames, "A", API_KEY),
]); ]);
if (publicIp === dnsRecord.content) { const dnsRecordsToPatch = dnsRecords.filter((dnsRecord) => {
dnsRecord.content !== publicIp;
});
if (dnsRecordsToPatch.length === 0) {
log.info(`public ip address remained at '${publicIp}', no patch needed`); log.info(`public ip address remained at '${publicIp}', no patch needed`);
log.info(`checking again in ${INTERVAL} minutes\n`); log.info(`checking again in ${INTERVAL} minutes\n`);
return; return;
} }
log.info( log.info(
`public ip address changed from '${dnsRecord.content}' to '${publicIp}'`, `public ip address changed from '${
dnsRecordsToPatch[0].content
}' to '${publicIp}'`,
); );
await patchDnsRecord( await patchDnsRecords(
dnsRecordsToPatch,
ZONE_ID, ZONE_ID,
dnsRecord.id,
API_KEY, API_KEY,
publicIp, publicIp,
DOMAIN_NAME,
"A", "A",
); );
log.info("patched dns entry"); log.info("patched dns entries");
log.info(`checking again in ${INTERVAL} minutes\n`); log.info(`checking again in ${INTERVAL} minutes\n`);
} catch (error) { } catch (error) {
log.error((error as Error).message); log.error((error as Error).message);

View File

@@ -6,7 +6,7 @@ const STYLES = {
ERROR: "\x1b[31m", ERROR: "\x1b[31m",
}; };
let LOG_LEVEL = process.env.LOG_LEVEL || "INFO"; let LOG_LEVEL = process.env.LOG_LEVEL ?? "INFO";
if (!["DEBUG", "INFO", "WARN", "ERROR"].includes(LOG_LEVEL)) { if (!["DEBUG", "INFO", "WARN", "ERROR"].includes(LOG_LEVEL)) {
console.warn( console.warn(