From cac3e9487b94a50fd0dd5dcaeb4ebb0ef8cdace3 Mon Sep 17 00:00:00 2001 From: Raymond Yang Date: Sat, 12 Aug 2023 13:30:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=A6=E4=BD=9C=E6=9B=B4=E6=96=B0DDNS?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 6 + config.json | 4 + src/main/kotlin/Main.kt | 139 +++++++++++++++++++- src/main/kotlin/api/CloudflareApiService.kt | 2 +- src/main/kotlin/model/Configure.kt | 9 ++ 5 files changed, 155 insertions(+), 5 deletions(-) create mode 100644 config.json create mode 100644 src/main/kotlin/model/Configure.kt diff --git a/build.gradle.kts b/build.gradle.kts index be42ac7..74f7ef3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,6 +13,12 @@ repositories { dependencies { testImplementation(kotlin("test")) implementation("com.squareup.retrofit2:retrofit:2.9.0") + implementation("com.squareup.retrofit2:converter-gson:2.9.0") + + implementation("com.google.code.gson:gson:2.10.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") + + implementation("org.jsoup:jsoup:1.14.3") } tasks.test { diff --git a/config.json b/config.json new file mode 100644 index 0000000..b91be9c --- /dev/null +++ b/config.json @@ -0,0 +1,4 @@ +{ + "token": "n_9L9-W70oYWtMV16GgYTs0yWZ72Vjs17QD4dUZl", + "domain": "ray650128.com" +} \ No newline at end of file diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 8f69baa..4f36916 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -1,7 +1,138 @@ -fun main(args: Array) { +import api.CloudflareApiService +import com.google.gson.Gson +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import model.Configure +import model.zones.UpdateDnsBody +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.io.File +import java.io.IOException + + +private lateinit var config: Configure + +private lateinit var token: String +private lateinit var domain: String + +fun main() { println("Cloudflare DDNS 更新程式已啟動") - // Try adding program arguments via Run/Debug configuration. - // Learn more about running applications: https://www.jetbrains.com/help/idea/running-applications.html. - println("Program arguments: ${args.joinToString()}") + val confFile = File("config.json") + if (!confFile.exists()) { + confFile.createNewFile() + + println("請先設定 config.json 檔案") + return + } + + val configRawText = confFile.readText(Charsets.UTF_8) + if (configRawText.isEmpty()) { + println("請先設定 config.json 檔案") + return + } + + config = loadConfigFile(configRawText) + + val retrofit = Retrofit.Builder() + .baseUrl("https://api.cloudflare.com") + .addConverterFactory(GsonConverterFactory.create()) + .build() + val apiService = retrofit.create(CloudflareApiService::class.java) + + runBlocking { + println("-------- [ 開始更新 DNS ] --------") + val client = OkHttpClient() + val request: Request = Request.Builder() + .url("https://ifconfig.me") + .addHeader("User-Agent", "") + .method("GET", null) + .build() + val ipAddress = try { + val response = withContext(Dispatchers.IO) { + client.newCall(request).execute() + } + + if (response.isSuccessful) { + response.body()?.string() ?: run { + println("無法取得 WAN IP 位址") + return@runBlocking + } + } else { + null + } + } catch (e: IOException) { + null + } finally { + client.dispatcher().executorService().shutdown() + } + + if (ipAddress != null) { + println("您目前的 WAN IP 位址為: $ipAddress") + try { + println("驗證 CF 權杖是否有效...") + val verifyTokenResult = apiService.getVerifyToken("Bearer $token") + if (verifyTokenResult.success) { + println("CF 權杖有效,載入 Zone 資料中...") + val zonesResult = apiService.getZones("Bearer $token") + if (zonesResult.success) { + println("Zone 資料載入成功,取得域名資料中...") + val zoneData = zonesResult.result.firstOrNull { it.name == domain } ?: run { + println("無法找到指定的域名 [ $domain ]...") + return@runBlocking + } + val zoneId = zoneData.id + val zoneDnsRecords = apiService.getZoneDnsRecords("Bearer $token", zoneId) + if (zoneDnsRecords.success) { + println("域名資料載入成功,處理中...") + val record = zoneDnsRecords.result.firstOrNull { it.name == domain } ?: run { + println("無法找到指定的域名 [ $domain ]...") + return@runBlocking + } + val recordId = record.id + val updateDnsBody = UpdateDnsBody( + type = "A", + name = record.name, + content = ipAddress, + ttl = 120, + proxied = false + ) + val updateDnsResult = apiService.updateZoneDnsRecords("Bearer $token", zoneId, recordId, updateDnsBody) + if (updateDnsResult.success) { + println("更新 DDNS 成功") + } else { + println("更新 DDNS 失敗") + } + } else { + println("無法取得域名資料資料") + } + } else { + println("無法取得 Zone 資料") + } + } else { + println("CF 權杖無效") + } + } catch (e: Exception) { + e.printStackTrace() + println("Error: ${e.message}") + } + } else { + println("Error retrieving IP address") + } + } +} + +private fun loadConfigFile(configRawText: String): Configure { + val config = Gson().fromJson(configRawText, Configure::class.java) + token = config.token + domain = config.domain + + println("-------- [ 設定檔已載入 ] --------") + println("CF 權杖: $token") + println("欲更新的域名: $domain") + return config } \ No newline at end of file diff --git a/src/main/kotlin/api/CloudflareApiService.kt b/src/main/kotlin/api/CloudflareApiService.kt index 783dcea..954c7d5 100644 --- a/src/main/kotlin/api/CloudflareApiService.kt +++ b/src/main/kotlin/api/CloudflareApiService.kt @@ -17,7 +17,7 @@ interface CloudflareApiService { @GET("/client/v4/zones") suspend fun getZones( @Header("Authorization") token: String - ): CloudflareResult + ): CloudflareResult> @GET("/client/v4/zones/{ZONE_ID}/dns_records") suspend fun getZoneDnsRecords( diff --git a/src/main/kotlin/model/Configure.kt b/src/main/kotlin/model/Configure.kt new file mode 100644 index 0000000..fb46c28 --- /dev/null +++ b/src/main/kotlin/model/Configure.kt @@ -0,0 +1,9 @@ +package model + + +import com.google.gson.annotations.SerializedName + +data class Configure( + val token: String, + val domain: String +) \ No newline at end of file