Created
March 6, 2024 09:00
-
-
Save natthasath/2e496c1626974a003d122a3a35178457 to your computer and use it in GitHub Desktop.
ddns-cloudflare
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
################### Mikrotik - CF Worker DDNS ################# | |
# # | |
# MIKROTIK - CLOUDFLARE WORDERS DDNS # | |
# build 0.9.230428 public review # | |
# BY: PHUKAO PONGSUWAN # | |
# Worker Script: https://pastebin.com/zjtfsSRZ # | |
# Router Script: https://pastebin.com/kz9iCSeb # | |
# # | |
# 0.9.230222 | |
# fix compare ip method by reolve via DNS | |
# 0.9.230428 | |
# fix - date to thai format - dd/mm/yyyy | |
# add - dontnotify value in json - IF want to slient update to some subdomain | |
# "false" mean alway send notify, "true" mean slient update | |
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # | |
:local DDNSURL "https://<workerid>.<subdomain>.workers.dev/"; | |
:local interfaceName "pppoe-out1"; | |
:local domains { | |
{ | |
"domain"="xxx.yoursdomain.com"; | |
"records_id"="abcxxxxxxxxxxxxxxx"; | |
"zone_id"="xyzzzzzzzzzzzzzzzzzzzz"; | |
"proxied"="false"; | |
"dontnotify"="false"; | |
}; | |
} | |
############### NOT NEED TO CHANGE BELOW ############## | |
:global DDNSIP; | |
:global DDNSURL; | |
:local DNSSERVER "1.1.1.1" | |
:local headers "content-type:application/json"; | |
:local success "\"success\":true"; | |
:local getWanIPAddress [:pick [/ip/address/get value-name=address [find interface=$interfaceName]] 0 ([:len [/ip/address/get value-name=address [find interface=$interfaceName]]] - 3)]; | |
:put "WAN: $interfaceName = $getWanIPAddress"; | |
:set ($DDNSIP->$interfaceName) $getWanIPAddress; | |
:local domainIP [:resolve ($domains->0->"domain") server=$DNSSERVER] | |
:put "Resolve: $($domains->0->"domain") = $domainIP at $DNSSERVER\n"; | |
if (($DDNSIP->$interfaceName) != $domainIP) do={ | |
:foreach d in=$domains do={ | |
:local domain ($d->"domain"); | |
:local proxied ($d->"proxied"); | |
:local dontnotify false; | |
if ( ($d->"dontnotify") = true ) do={ | |
:set $dontnotify true | |
} | |
:local fURL ($DDNSURL."?zone=".$d->"zone_id"."&id=".$d->"records_id"); | |
:local fDATA "{\"type\":\"A\",\"name\":\"$domain\",\"content\":\"$getWanIPAddress\",\"proxied\":$proxied,\"dontnotify\":$dontnotify,\"ttl\": 60}"; | |
## Payload debug Line | |
:put $fDATA | |
if ( [:find ([/tool/fetch url=$fURL http-method=put http-header-field=$headers http-data=$fDATA as-value output=user]->"data") ($success)] ) do={ | |
:log warning ( "DDNS $interfaceName: $domain / $getWanIPAddress update success (Notify: ".(!$dontnotify)." )" ) | |
:put ( "DDNS $interfaceName: $domain / $getWanIPAddress update success (Notify: ".(!$dontnotify).")" ) | |
} else={ | |
:log error "DDNS $interfaceName: $domain - update FAIL!!"; | |
:put "DDNS $interfaceName: $domain - update FAIL!!\n"; | |
} | |
} | |
} else={ | |
#:log info "$interfaceName: IP not change"; | |
:put "$interfaceName: IP not change, no need to update\n"; | |
} | |
#################### updateDnsCloudFlare #################### |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** MIKROTIK - CLOUDFLARE WORDERS DDNS **/ | |
/** build 0.9.230428 public review **/ | |
/** BY: PHUKAO PONGSUWAN **/ | |
/** Worker Script: https://pastebin.com/zjtfsSRZ **/ | |
/** Router Script: https://pastebin.com/kz9iCSeb **/ | |
/** API TOKEN ** | |
* | |
* PUT Your TOKEN in this seciton or | |
* Create Environment Variables name "API_TOKEN" in setting workers page | |
* | |
* 0.9.230428 | |
* fix - date to thai format - dd/mm/yyyy | |
* add - dontnotify value in json - IF want to slient update to some subdomain | |
* | |
*/ | |
var CF_TOKEN = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" // <============== YOUR TOKEN HERE | |
/** | |
* ACCESS URL: https://<worker-service>.<worker-subdomain>.workers.dev/?zone=<zone-id>&id=<record-id> | |
* METHOD PUT or POST | |
* JSON BODY | |
{ | |
"type": "A", | |
"name": "<sub-domain>", | |
"content": "<new ip>", | |
"proxied": false, | |
"dontnotify" : false, | |
"ttl": 60 | |
} | |
* | |
* Environment Variables | |
* [Require] | |
* API_TOKEN - CloudFlare DNS ZONE TOKEN | |
* | |
* [Optional] | |
* NOTIFY_MESSAGE - MESSAGE PATTHEN include field [DOMAIN] [IP] and [TIME] | |
* LINE_TOKEN - LINE notify TOKEN issue form https://notify-bot.line.me/ | |
* TELEGRAM_TOKEN - TELEGRAM BOT TOKEN issue form https://core.telegram.org/bots/api | |
* TELEGRAM_CHANNEL - TELEGEAM CHANNEL to receive notify | |
* TIMEZONE - default Asia/Bangkok | |
*/ | |
/** | |
* | |
* DO NOT EDIT BELOW | |
* | |
*/ | |
/** CLOUDFLARE API URL**/ | |
const cfurl = 'https://api.cloudflare.com/client/v4/zones/'; | |
if (typeof API_TOKEN !== 'undefined') { | |
CF_TOKEN = API_TOKEN; // GET from Environment Variables | |
} | |
async function gatherResponse(response) { | |
const { headers } = response; | |
const contentType = headers.get('content-type') || ''; | |
if (contentType.includes('application/json')) { | |
const replyJSON = await response.json() | |
if ( replyJSON["success"] ) { | |
return JSON.stringify(replyJSON); | |
} else { | |
const response = new Response('{"success":false,"errors":"CF API error."}', { | |
status: 400 | |
}) | |
return response.text(); | |
} | |
} | |
return response.text(); | |
} | |
async function handleRequest(request) { | |
//ALLOW Only PUT and POST | |
if (request.method === 'PUT' || request.method === 'POST') { | |
// Get the JSON data from the request body | |
const url = new URL(request.url); | |
//check query string have ZONE and ID | |
if (url.searchParams.get('zone') && url.searchParams.get('id') ) { | |
// CALL CLOUDFLARE API | |
try { | |
//const body = await request.json(); | |
const body = await request.json(); | |
const putAPI = cfurl + url.searchParams.get('zone') + '/dns_records/' + url.searchParams.get('id'); | |
const putBODY = { | |
type: body["type"], | |
name: body["name"], | |
content: body["content"], | |
proxied: body["proxied"], | |
ttl: body["ttl"] | |
}; | |
const init = { | |
body: JSON.stringify(putBODY), | |
method: 'PUT', | |
headers: { | |
'Authorization': 'Bearer '+CF_TOKEN, | |
'content-type': 'application/json', | |
}, | |
}; | |
const response = await fetch(putAPI, init); | |
const results = await gatherResponse(response); | |
// NOTIFY SECTION ------ | |
if (!dontNotify) { //MAKE NOTIFY | |
if ((typeof LINE_TOKEN !== 'undefined') || (typeof TELEGRAM_TOKEN !== 'undefined')) { | |
const currentDate = new Date(); | |
var NotifyTIMEZONE = 'Asia/Bangkok'; | |
if (typeof TIMEZONE !== 'undefined') { | |
NotifyTIMEZONE = TIMEZONE; | |
} | |
const dateString = dateTimeString = currentDate.toLocaleString('th-TH', { | |
timeZone: NotifyTIMEZONE, | |
hour12: false | |
}); | |
const LogMessage = dateString+': '+body["name"]+' <'+body["content"]+'>'; | |
//Log result | |
console.log(LogMessage); | |
var NotifyMessage = body["name"]+' <'+body["content"]+'>\n'+dateString; | |
if (typeof NOTIFY_MESSAGE !== 'undefined') { | |
var NotifyMessage = NOTIFY_MESSAGE; | |
NotifyMessage = NotifyMessage.replace('[DOMAIN]',body["name"]); | |
NotifyMessage = NotifyMessage.replace('[IP]',body["content"]); | |
NotifyMessage = NotifyMessage.replace('[TIME]',dateString); | |
NotifyMessage = NotifyMessage.replace('\\n','\n');; | |
} | |
// LINE NOTIFY | |
if (typeof LINE_TOKEN !== 'undefined') { | |
const Notify_API_URL = 'https://notify-api.line.me/api/notify/'; | |
try { | |
const initLineNotify = { | |
body: 'message='+NotifyMessage, | |
method: 'POST', | |
headers: { | |
'content-type': 'application/x-www-form-urlencoded;charset=UTF-8', | |
'authorization': 'Bearer '+LINE_TOKEN | |
} | |
}; | |
const responseNotify = await fetch(Notify_API_URL, initLineNotify) | |
.then(response => { | |
if (response.status === 200) { | |
// Response is ok | |
console.log('LINE: '+NotifyMessage); | |
} else { | |
const response = new Response('Error: LINE API Response', { status: 400 }); | |
return response | |
} | |
}) | |
.catch(error => { | |
const response = new Response('Error: LINE API Calling', { status: 400 }); | |
return response | |
}); | |
} catch (error) { | |
const response = new Response('Error: LINE API', { status: 400 }); | |
return response | |
} | |
} else { console.log( "LINE: NULL" ) } | |
// end Line API defined | |
// END - LINE NOTIFY ----- | |
// TELEGRAM NOTIFY | |
if ((typeof TELEGRAM_TOKEN !== 'undefined') && (typeof TELEGRAM_CHANNEL !== 'undefined')) { | |
const Notify_TELEGRAM_URL = 'https://api.telegram.org/bot'+TELEGRAM_TOKEN+'/sendmessage?chat_id='+TELEGRAM_CHANNEL+''; | |
try { | |
const initTeleNotify = { | |
body: 'text='+NotifyMessage, | |
method: 'POST', | |
headers: { | |
'content-type': 'application/x-www-form-urlencoded;charset=UTF-8' | |
} | |
}; | |
const responseNotify = await fetch(Notify_TELEGRAM_URL, initTeleNotify) | |
.then(response => { | |
if (response.status === 200) { | |
// Response is ok | |
console.log('TELEGRAM: '+NotifyMessage); | |
} else { | |
const response = new Response('Error: TELEGRAM Response', { status: 400 }); | |
return response | |
} | |
}) | |
.catch(error => { | |
const response = new Response('Error: TELEGRAM API Calling', { status: 400 }); | |
return response | |
}); | |
} catch (error) { | |
const response = new Response('Error: TELEGRAM API', { status: 400 }); | |
return response | |
} | |
} else { console.log( "TELEGRAM: NULL" ) } | |
// end Telegram API defined | |
// END - TELEGRAM NOTIFY | |
} // END - NOTIFY SECTION ----- | |
} else { //END //MAKE NOTIFY | |
console.log("Domain "+body["name"]+"="+body["content"]) | |
} | |
// return MAIN | |
return new Response(results, init); | |
} catch (error) { | |
if (error instanceof SyntaxError) { | |
// The request body is not valid JSON | |
const response = new Response('Error: JSON SyntaxError',{ status: 400 }); | |
return response | |
} else { | |
// Reference or Declaration Error | |
if (error instanceof ReferenceError) { | |
const response = new Response('Error: ReferenceError',{ status: 400 }); | |
return response | |
} else { | |
// Some other error occurred | |
const response = new Response('Error: internal somting error',{ status: 400 }); | |
return response | |
} | |
} | |
} | |
} else { | |
// Zone Or ID not found | |
const response = new Response('Error: Query string not valid pattern',{ status: 400 }); | |
return response | |
} | |
} else { | |
const response = new Response('Error: Method not allow.', { status: 405 }); | |
return response | |
} | |
} | |
addEventListener('fetch', event => { | |
return event.respondWith(handleRequest(event.request)); | |
}); | |
/** --- END --- **/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment