Last active
March 15, 2020 15:28
Star
You must be signed in to star a gist
通过 QCloud/DnsPod API 动态更新域名解析
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
# frozen_string_literal: true | |
require 'json' | |
require 'net/http' | |
class DnspodAPI | |
API_HOST = 'https://dnsapi.cn' | |
# API requires UserAgent contains app name/version and email | |
def initialize(token, app_name:, app_version:, email:) | |
@token = token | |
@app_name = app_name | |
@app_version = app_version | |
@email = email | |
end | |
def domain_records(domain, length: 100, **params) | |
method(__method__).parameters.each do |(type, name)| | |
params[name] = binding.local_variable_get(name) if type != :keyrest | |
end | |
request_api('/Record.List', params)['records'] | |
end | |
def update_record(domain, sub_domain, value:, **params) | |
method(__method__).parameters.each do |(type, name)| | |
params[name] = binding.local_variable_get(name) if type != :keyrest | |
end | |
params[:record_type] ||= 'A' | |
params[:record_line] ||= '默认' | |
unless params[:record_id] | |
record = domain_records(domain, params.slice(:sub_domain, :record_type)).first | |
raise 'record not found' if record.nil? | |
return record if record['value'] == value | |
params[:record_id] = record['id'] | |
end | |
request_api('/Record.Modify', params) | |
end | |
def request_api(path, params) | |
params = params.merge(login_token: @token, format: :json) | |
resp = Net::HTTP.post( | |
URI(API_HOST).merge(path), | |
URI.encode_www_form(params), | |
'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8', | |
'User-Agent' => user_agent | |
) | |
raise resp unless resp.is_a?(Net::HTTPOK) | |
JSON.parse(resp.body) | |
end | |
def user_agent | |
"#{@app_name}/#{@app_version} (#{@email})" | |
end | |
end | |
if $PROGRAM_NAME == __FILE__ | |
require 'socket' | |
def current_ip | |
s = TCPSocket.new 'ns1.dnspod.net', 6666 | |
ip = s.gets | |
s.close | |
ip | |
end | |
token = ARGV[0] | |
client = DnspodAPI.new( | |
token, | |
app_name: 'Ruby Client', | |
app_version: '0.1.0', | |
email: 'xiaohui@tanmer.com' | |
) | |
pp client.domain_records('tanmer.com', sub_domain: 'debug') | |
pp client.update_record('tanmer.com', 'debug', value: ARGV[2] || current_ip) | |
end |
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
#! /usr/bin/env ruby | |
require 'active_support/core_ext/string' | |
require 'active_support/core_ext/hash' | |
require 'active_support/core_ext/object/to_query' | |
require 'openssl' | |
require 'base64' | |
require 'open-uri' | |
require 'json' | |
require 'logger' | |
class QCloudAPI | |
attr_reader :secret_id, :secret | |
def initialize(secret_id, secret, logger: Logger.new(STDOUT)) | |
@secret_id = secret_id | |
@secret = secret | |
@logger = logger | |
end | |
def log(method=:puts, message) | |
@logger.send(method, message) | |
end | |
def common_args( | |
action: , | |
timestamp: , | |
nonce: , | |
secret_id: , | |
signature: nil, | |
region: nil, | |
signature_method: 'HmacSHA256', | |
token: nil | |
) | |
%w[action timestamp nonce secret_id signature region signature_method token].reduce({}) { |h, k| | |
v = eval(k) | |
h[k.classify] = v if v | |
h | |
} | |
end | |
# https://cloud.tencent.com/document/product/302/7310 | |
def build_signature(method, host, path, args, secret) | |
args = args.stringify_keys | |
args_str = args.keys.sort.map{ |k| "#{k}=#{args[k]}" }.join('&') | |
str = "#{method.to_s.upcase}#{host}#{path}?#{args_str}" | |
mac = OpenSSL::HMAC.digest("SHA256", secret, str) | |
Base64.encode64(mac).strip | |
end | |
def request_api(host, path, action, args) | |
final_args = common_args(action: action, timestamp: Time.now.to_i, nonce: SecureRandom.hex(4), secret_id: secret_id).merge(args) | |
signature = build_signature('GET', host, path, final_args, secret) | |
params = final_args.merge('Signature' => signature).to_query | |
response = open("https://#{host}#{path}?#{params}") and nil | |
body = JSON.parse(response.read) | |
raise "#{body['code']} - #{body['codeDesc']} - #{body['message']}" unless body['code'] == 0 | |
body | |
end | |
def domain_lists | |
response = request_api('cns.api.qcloud.com', '/v2/index.php', 'DomainList', length: 200) | |
response['data']['domains'].map { |domain| domain.slice('id', 'name') } | |
end | |
def domain_records(domain) | |
response = request_api('cns.api.qcloud.com', '/v2/index.php', 'RecordList', length: 200, domain: domain) | |
response['data']['records'].map { |record| record.slice('id', 'name', 'status', 'line', 'type', 'value') } | |
end | |
# https://cloud.tencent.com/document/product/302/8511 | |
def update_domain_record( | |
domain: , | |
sub_domain: , | |
value: , | |
record_id: nil, | |
record_type: nil, | |
record_line: nil) | |
old_value = nil | |
if record_id.nil? || record_type.nil? | |
record = domain_records(domain).find { |domain| domain['name'] == sub_domain } | |
raise "Domain #{sub_domain}.#{domain} not found" if record.nil? | |
old_value = record['value'] | |
return false if value == old_value | |
record_id = record['id'] | |
record_type = record['type'] | |
record_line = record['line'] | |
end | |
log :print, "Updating DNS #{sub_domain}.#{domain} from #{old_value} to #{value} ... " | |
response = request_api( | |
'cns.api.qcloud.com', | |
'/v2/index.php', | |
'RecordModify', | |
domain: domain, | |
recordId: record_id, | |
subDomain: sub_domain, | |
recordType: record_type, | |
recordLine: record_line, | |
value: value | |
) | |
log :puts, 'Done.' | |
return true | |
end | |
end | |
require 'socket' | |
def current_ip | |
s = TCPSocket.new 'ns1.dnspod.net', 6666 | |
ip = s.gets | |
s.close | |
ip | |
end | |
# ========================= update below ====================== | |
api = QCloudAPI.new('secret key', 'secret') | |
api.update_domain_record( | |
domain: 'c456.com', | |
sub_domain: 'tanmerdev', | |
value: current_ip | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment