Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
325bc905f0 | ||
|
|
4995a08260 | ||
|
|
6695db9d31 | ||
|
|
64de314ea5 | ||
|
|
869d0d4e97 | ||
|
|
027d499875 |
@@ -5,6 +5,6 @@ services:
|
||||
environment:
|
||||
- API_KEY=123
|
||||
- ZONE_ID=023e105f4ecef8ad9ca31a8372d0c353
|
||||
- DOMAIN_NAME=dyndns.example.com
|
||||
- DOMAIN_NAMES=dyndns.example.com,example.com
|
||||
- INTERVAL=5
|
||||
- LOG_LEVEL=INFO
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "cloudflare-dynamic-dns",
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.2",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "rimraf ./dist && tsc",
|
||||
"start": "pnpm build && node ."
|
||||
"start": "node .",
|
||||
"dev": "tsc -w",
|
||||
"build": "rimraf ./dist && tsc"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
||||
@@ -17,7 +17,7 @@ const getDnsRecord = async (
|
||||
name: string,
|
||||
type: string,
|
||||
apiKey: string,
|
||||
): Promise<{ content: string; id: string }> => {
|
||||
): Promise<{ content: string; name: string; id: string }> => {
|
||||
const url =
|
||||
`https://api.cloudflare.com/client/v4/zones/${zoneIdentifier}/dns_records?name=${name}&type=${type}`;
|
||||
const headers = {
|
||||
@@ -28,7 +28,7 @@ const getDnsRecord = async (
|
||||
const json: CFResponse = await response.json();
|
||||
|
||||
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(
|
||||
@@ -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 (
|
||||
zoneIdentifier: 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 };
|
||||
|
||||
35
src/index.ts
35
src/index.ts
@@ -1,49 +1,56 @@
|
||||
import { getDnsRecord, patchDnsRecord } from "./cloudflare";
|
||||
import { getDnsRecords, patchDnsRecords } from "./cloudflare";
|
||||
import getPublicIp from "./getPublicIp";
|
||||
import * as log from "./log";
|
||||
|
||||
const { ZONE_ID, DOMAIN_NAME, API_KEY, INTERVAL } = process.env;
|
||||
const { ZONE_ID, DOMAIN_NAMES, API_KEY } = process.env;
|
||||
const INTERVAL = process.env.INTERVAL ?? "5";
|
||||
|
||||
if (ZONE_ID === undefined) {
|
||||
log.error("could not access environment variable 'ZONE_ID'");
|
||||
}
|
||||
if (DOMAIN_NAME === undefined) {
|
||||
log.error("could not access environment variable 'DOMAIN_NAME'");
|
||||
if (DOMAIN_NAMES === undefined) {
|
||||
log.error("could not access environment variable 'DOMAIN_NAMES'");
|
||||
}
|
||||
if (API_KEY === undefined) {
|
||||
log.error("could not access environment variable 'API_KEY'");
|
||||
}
|
||||
if (
|
||||
ZONE_ID === undefined || DOMAIN_NAME === undefined || API_KEY === undefined
|
||||
ZONE_ID === undefined || DOMAIN_NAMES === undefined || API_KEY === undefined
|
||||
) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const dynamicDns = async () => {
|
||||
const domainNames = DOMAIN_NAMES.split(",");
|
||||
try {
|
||||
const [publicIp, dnsRecord] = await Promise.all([
|
||||
const [publicIp, dnsRecords] = await Promise.all([
|
||||
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(`checking again in ${INTERVAL} minutes\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
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,
|
||||
dnsRecord.id,
|
||||
API_KEY,
|
||||
publicIp,
|
||||
DOMAIN_NAME,
|
||||
"A",
|
||||
);
|
||||
log.info("patched dns entry");
|
||||
log.info("patched dns entries");
|
||||
log.info(`checking again in ${INTERVAL} minutes\n`);
|
||||
} catch (error) {
|
||||
log.error((error as Error).message);
|
||||
@@ -54,5 +61,5 @@ const dynamicDns = async () => {
|
||||
dynamicDns();
|
||||
setInterval(
|
||||
dynamicDns,
|
||||
Number.parseInt(INTERVAL === undefined ? "5" : INTERVAL) * 60 * 1000,
|
||||
Number.parseInt(INTERVAL) * 60 * 1000,
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ const STYLES = {
|
||||
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)) {
|
||||
console.warn(
|
||||
|
||||
Reference in New Issue
Block a user