Skip to content

Commit

Permalink
refactor: 重构前端代码 (#988)
Browse files Browse the repository at this point in the history
* refactor: 重构前端代码

几乎完全重构前端js代码
小幅度重构后端代码(主要为了适配前端)

* fix: 修复主题色无法自适应的bug

还原了主题色修改函数的write参数,去除该参数会导致自适应修改主题时也会写入localStorage

* feat: 自动修改标签的for属性

自动修改”获取 IP 方式“标签的for属性,保证其始终指向当前可编辑的元素

* refactor: 去除JQuery

* refactor: 按业务划分代码

* fix: 对空值兼容

* fix: 统一html命名

* feat: 默认选择最后一个配置

* fix: 删除冗余代码

* fix: Update writing.html
  • Loading branch information
PairZhu authored Jan 22, 2024
1 parent fe9f437 commit 7befe6e
Show file tree
Hide file tree
Showing 13 changed files with 1,088 additions and 950 deletions.
14 changes: 7 additions & 7 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type DnsConfig struct {
URL string
NetInterface string
Cmd string
IPv6Reg string // ipv6匹配正则表达式
Ipv6Reg string // ipv6匹配正则表达式
Domains []string
}
DNS DNS
Expand Down Expand Up @@ -291,10 +291,10 @@ func (conf *DnsConfig) getIpv6AddrFromInterface() string {

for _, netInterface := range ipv6 {
if netInterface.Name == conf.Ipv6.NetInterface && len(netInterface.Address) > 0 {
if conf.Ipv6.IPv6Reg != "" {
if conf.Ipv6.Ipv6Reg != "" {
// 匹配第几个IPv6
if match, err := regexp.MatchString("@\\d", conf.Ipv6.IPv6Reg); err == nil && match {
num, err := strconv.Atoi(conf.Ipv6.IPv6Reg[1:])
if match, err := regexp.MatchString("@\\d", conf.Ipv6.Ipv6Reg); err == nil && match {
num, err := strconv.Atoi(conf.Ipv6.Ipv6Reg[1:])
if err == nil {
if num > 0 {
if num <= len(netInterface.Address) {
Expand All @@ -303,14 +303,14 @@ func (conf *DnsConfig) getIpv6AddrFromInterface() string {
util.Log("未找到第 %d 个IPv6地址! 将使用第一个IPv6地址", num)
return netInterface.Address[0]
}
util.Log("IPv6匹配表达式 %s 不正确! 最小从1开始", conf.Ipv6.IPv6Reg)
util.Log("IPv6匹配表达式 %s 不正确! 最小从1开始", conf.Ipv6.Ipv6Reg)
return ""
}
}
// 正则表达式匹配
util.Log("IPv6将使用正则表达式 %s 进行匹配", conf.Ipv6.IPv6Reg)
util.Log("IPv6将使用正则表达式 %s 进行匹配", conf.Ipv6.Ipv6Reg)
for i := 0; i < len(netInterface.Address); i++ {
matched, err := regexp.MatchString(conf.Ipv6.IPv6Reg, netInterface.Address[i])
matched, err := regexp.MatchString(conf.Ipv6.Ipv6Reg, netInterface.Address[i])
if matched && err == nil {
util.Log("匹配成功! 匹配到地址: ", netInterface.Address[i])
return netInterface.Address[i]
Expand Down
2 changes: 0 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,6 @@ func runWebServer() error {
http.HandleFunc("/save", web.BasicAuth(web.Save))
http.HandleFunc("/logs", web.BasicAuth(web.Logs))
http.HandleFunc("/clearLog", web.BasicAuth(web.ClearLog))
http.HandleFunc("/ipv4NetInterface", web.BasicAuth(web.Ipv4NetInterfaces))
http.HandleFunc("/ipv6NetInterface", web.BasicAuth(web.Ipv6NetInterfaces))
http.HandleFunc("/webhookTest", web.BasicAuth(web.WebhookTest))

util.Log("监听 %s", *listen)
Expand Down
4 changes: 2 additions & 2 deletions static/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,11 @@ main {
margin-right: 25px;
}

[data-theme='dark'] .theme-button:hover {
.theme-button:hover {
box-shadow: 0px 0px 15px #0d0d0dab;
}

[data-theme='dark'] .theme-button:active {
.theme-button:active {
transform: scale(0.98);
}

Expand Down
265 changes: 265 additions & 0 deletions static/constant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
const DNS_PROVIDERS = {
alidns: {
name: {
"en": "Aliyun",
"zh-cn": "阿里云",
},
idLabel: "AccessKey ID",
secretLabel: "AccessKey Secret",
helpHtml: {
"en": "<a target='_blank' href='https://ram.console.aliyun.com/manage/ak?spm=5176.12818093.nav-right.dak.488716d0mHaMgg'>Create AccessKey</a>",
"zh-cn": "<a target='_blank' href='https://ram.console.aliyun.com/manage/ak?spm=5176.12818093.nav-right.dak.488716d0mHaMgg'>创建 AccessKey</a>",
}
},
tencentcloud: {
name: {
"en": "Tencent",
"zh-cn": "腾讯云",
},
idLabel: "SecretId",
secretLabel: "SecretKey",
helpHtml: {
"en": "<a target='_blank' href='https://console.dnspod.cn/account/token/apikey'>Create AccessKey</a>",
"zh-cn": "<a target='_blank' href='https://console.dnspod.cn/account/token/apikey'>创建腾讯云 API 密钥</a>",
}
},
dnspod: {
name: {
"en": "DnsPod",
},
idLabel: "ID",
secretLabel: "Token",
helpHtml: {
"en": "<a target='_blank' href='https://console.dnspod.cn/account/token/token'>Create Token</a>",
"zh-cn": "<a target='_blank' href='https://console.dnspod.cn/account/token/token'>创建 DNSPod Token</a>",
}
},
cloudflare: {
name: {
"en": "Cloudflare",
},
idLabel: "",
secretLabel: "Token",
helpHtml: {
"en": "<a target='_blank' href='https://dash.cloudflare.com/profile/api-tokens'>Create Token -> Edit Zone DNS (Use template)</a>",
"zh-cn": "<a target='_blank' href='https://dash.cloudflare.com/profile/api-tokens'>创建令牌 -> 编辑区域 DNS (使用模板)</a>",
}
},
huaweicloud: {
name: {
"en": "Huawei",
"zh-cn": "华为云",
},
idLabel: "Access Key Id",
secretLabel: "Secret Access Key",
helpHtml: {
"en": "<a target='_blank' href='https://console.huaweicloud.com/iam/?locale=zh-cn#/mine/accessKey'>Create</a>",
"zh-cn": "<a target='_blank' href='https://console.huaweicloud.com/iam/?locale=zh-cn#/mine/accessKey'>新增访问密钥</a>",
}
},
callback: {
name: {
"en": "Callback",
},
idLabel: "URL",
secretLabel: "RequestBody",
helpHtml: {
"en": "<a target='_blank' href='https://github.com/jeessy2/ddns-go/blob/master/README_EN.md#callback'>Callback</a> Support variables #{ip}, #{domain}, #{recordType}, #{ttl}",
"zh-cn": "<a target='_blank' href='https://github.com/jeessy2/ddns-go#callback'>自定义回调</a> 支持的变量 #{ip}, #{domain}, #{recordType}, #{ttl}",
}
},
baiducloud: {
name: {
"en": "Baidu",
"zh-cn": "百度云",
},
idLabel: "AccessKey ID",
secretLabel: "AccessKey Secret",
helpHtml: {
"en": "<a target='_blank' href='https://console.bce.baidu.com/iam/?_=1651763238057#/iam/accesslist'>Create AccessKey</a><br /><a target='_blank' href='https://ticket.bce.baidu.com/#/ticket/create~productId=60&questionId=393&channel=2'>Apply for a ticket</a> DDNS needs to call the API, and the related APIs of Baidu Cloud are only open to users who have applied. Please submit a ticket before using it.",
"zh-cn": "<a target='_blank' href='https://console.bce.baidu.com/iam/?_=1651763238057#/iam/accesslist'>创建 AccessKey</a><br /><a target='_blank' href='https://ticket.bce.baidu.com/#/ticket/create~productId=60&questionId=393&channel=2'>申请工单</a> DDNS 需调用 API ,而百度云相关 API 仅对申请用户开放,使用前请先提交工单申请。",
}
},
porkbun: {
name: {
"en": "Porkbun",
},
idLabel: "API Key",
secretLabel: "Secret Key",
helpHtml: {
"en": "<a target='_blank' href='https://porkbun.com/account/api'>Create Access</a>",
"zh-cn": "<a target='_blank' href='https://porkbun.com/account/api'>创建 Access</a>",
}
},
godaddy: {
name: {
"en": "GoDaddy",
},
idLabel: "Key",
secretLabel: "Secret",
helpHtml: {
"en": "<a target='_blank' href='https://porkbun.com/account/api'>Create API KEY</a>",
"zh-cn": "<a target='_blank' href='https://developer.godaddy.com/keys'>创建 API KEY</a>",
}
},
googledomain: {
name: {
"en": "Google Domain",
},
idLabel: "Username",
secretLabel: "Password",
helpHtml: {
"en": "<a target='_blank' href='https://support.google.com/domains/answer/6147083?hl=en'>How to get started</a>",
"zh-cn": "<a target='_blank' href='https://support.google.com/domains/answer/6147083?hl=zh-Hans'>新建动态域名解析记录</a>",
}
},
namecheap: {
name: {
"en": "Namecheap",
},
idLabel: "",
secretLabel: "Password",
helpHtml: {
"en": "<a target='_blank' href='https://www.namecheap.com/support/knowledgebase/article.aspx/36/11/how-do-i-start-using-dynamic-dns/'>How to get started</a> <span style='color: red'>Namecheap DDNS does not support updating IPv6</span>",
"zh-cn": "<a target='_blank' href='https://www.namecheap.com/support/knowledgebase/article.aspx/36/11/how-do-i-start-using-dynamic-dns/'>开启namecheap动态域名解析</a> <span style='color: red'>Namecheap DDNS 不支持更新 IPv6</span>",
}
},
namesilo: {
name: {
"en": "NameSilo",
},
idLabel: "",
secretLabel: "Password",
helpHtml: {
"en": "<a target='_blank' href='https://www.namesilo.com/account/api-manager'>How to get started</a> <b>Please note that the TTL of namesilo is at least 1 hour</b>",
"zh-cn": "<a target='_blank' href='https://www.namesilo.com/account/api-manager'>开启namesilo动态域名解析</a> <b>请注意namesilo的TTL最低1小时</b>",
}
},
};

const SVG_CODE = {
success: `<svg viewBox="64 64 896 896" focusable="false" data-icon="check-circle" width="1em" height="1em" fill="#52c41a" aria-hidden="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"></path></svg>`,
info: `<svg viewBox="64 64 896 896" focusable="false" data-icon="info-circle" width="1em" height="1em" fill="#1677ff" aria-hidden="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm32 664c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V456c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272zm-32-344a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"></path></svg>`,
warning: '<svg viewBox="64 64 896 896" focusable="false" data-icon="exclamation-circle" width="1em" height="1em" fill="#faad14" aria-hidden="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"></path></svg>',
error: '<svg viewBox="64 64 896 896" focusable="false" data-icon="close-circle" width="1em" height="1em" fill="#ff4d4f" aria-hidden="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg>'
}


const I18N_MAP = {
'en': {
'Logs': 'Logs',
'Save': 'Save',
'Config:': 'Config:',
'Add': 'Add',
'Delete': 'Delete',
'DNS Provider': 'DNS Provider',
'Create AccessKey': 'Create AccessKey',
'Auto': 'Auto',
'1s': '1s',
'5s': '5s',
'10s': '10s',
'1m': '1m',
'2m': '2m',
'10m': '10m',
'30m': '30m',
'1h': '1h',
'ttlHelp': 'You can modify it if the account supports a smaller TTL. The TTL will only be updated when the IP changes',
'Enabled': 'Enabled',
'Get IP method': 'Get IP method',
'By api': 'By api',
'By network card': 'By network card',
'By command': 'By command',
'domainsHelp': `
One domain per line, you can use colon to separate the root domain
(example.cn.eu.org) and the subdomain (www), fill in as: <b>www:example.cn.eu.org</b>
`,
'Regular exp.': 'Regular exp.',
'regHelp': 'You can use @1 to specify the first IPv6 address, @2 to specify the second IPv6 address... You can also use regular expressions to match the specified IPv6 address, leave it blank to disable it',
'Others': 'Others',
'Deny from WAN': 'Deny from WAN',
'NotAllowWanAccessHelp': 'Default enabled, can prohibit access to this page from the public network',
'Username': 'Username',
'accountHelp': 'Please enter to protect your information security',
'Password': 'Password',
'WebhookURLHelp': `
<a
target="blank"
href="https://github.com/jeessy2/ddns-go/blob/master/README_EN.md#webhook"
>Click to get more info</a
><br />
Support variables #{ipv4Addr}, #{ipv4Result},
#{ipv4Domains}, #{ipv6Addr}, #{ipv6Result}, #{ipv6Domains}
`,
'WebhookRequestBodyHelp': 'If RequestBody is empty, it is a GET request, otherwise it is a POST request. Supported variables are the same as above',
'WebhookHeadersHelp': 'One header per line, such as: Authorization: Bearer API_KEY',
'Try it': 'Try it',
'Clear': 'Clear',
'OK': 'OK',
"Ipv4UrlHelp": "https://myip4.ipip.net, https://ddns.oray.com/checkip, https://ip.3322.net",
"Ipv6UrlHelp": "https://speed.neu6.edu.cn/getIP.php, https://v6.ident.me, https://6.ipw.cn",
"Ipv4NetInterfaceHelp": "Get IPv4 address through network card",
"Ipv6NetInterfaceHelp": "If you do not specify a matching regular expression, the first IPv6 address will be used by default",
"Ipv4CmdHelp": "Get IPv4 through command, only use the first matching IPv4 address of standard output(stdout). Such as: ip -4 addr show eth1",
"Ipv6CmdHelp": "Get IPv6 through command, only use the first matching IPv6 address of standard output(stdout). Such as: ip -6 addr show eth1",
"NetInterfaceEmptyHelp": '<span style="color: red">No available network card found</span>',
},
'zh-cn': {
'Logs': '日志',
'Save': '保存',
'Config:': '配置切换:',
'Add': '添加',
'Delete': '删除',
'DNS Provider': 'DNS服务商',
'Create AccessKey': '创建 AccessKey',
'Auto': '自动',
'1s': '1秒',
'5s': '5秒',
'10s': '10秒',
'1m': '1分钟',
'2m': '2分钟',
'10m': '10分钟',
'30m': '30分钟',
'1h': '1小时',
'ttlHelp': '如账号支持更小的 TTL, 可修改。IP 有变化时才会更新TTL',
'Enabled': '是否启用',
'Get IP method': '获取 IP 方式',
'By api': '通过接口获取',
'By network card': '通过网卡获取',
'By command': '通过命令获取',
'domainsHelp': `
一行一个域名, 可使用冒号分隔根域名(example.cn.eu.org)与子域名(www), 填写为:<b>www:example.cn.eu.org</b>
<a target="blank" href="https://github.com/jeessy2/ddns-go/wiki/传递自定义参数">支持传递自定义参数</a>
`,
'Regular exp.': '匹配正则表达式',
'regHelp': '可使用 @1 指定第一个IPv6地址, @2 指定第二个IPv6地址... 也可使用正则表达式匹配指定的IPv6地址, 留空则不启用',
'Others': '其他',
'Deny from WAN': '禁止公网访问',
'NotAllowWanAccessHelp': '默认启用, 可禁止从公网访问本页面',
'Username': '用户名',
'accountHelp': '为保护你的信息安全,建议输入',
'Password': '密码',
'WebhookURLHelp': `
<a target="blank" href="https://github.com/jeessy2/ddns-go#webhook">点击参考官方 Webhook 说明</a>
<br />
支持的变量 #{ipv4Addr}, #{ipv4Result}, #{ipv4Domains}, #{ipv6Addr}, #{ipv6Result}, #{ipv6Domains}
`,
'WebhookRequestBodyHelp': '如果 RequestBody 为空, 则为 GET 请求, 否则为 POST 请求。支持的变量同上',
'WebhookHeadersHelp': '一行一个Header, 如: Authorization: Bearer API_KEY',
'Try it': '模拟测试Webhook',
'Clear': '清空',
'OK': '确定',
"Ipv4UrlHelp": "https://myip4.ipip.net, https://ddns.oray.com/checkip, https://ip.3322.net",
"Ipv6UrlHelp": "https://speed.neu6.edu.cn/getIP.php, https://v6.ident.me, https://6.ipw.cn",
"Ipv4NetInterfaceHelp": "通过网卡获取IPv4",
"Ipv6NetInterfaceHelp": "如不指定匹配正则表达式,将默认使用第一个 IPv6 地址",
"Ipv4CmdHelp": `
通过命令获取IPv4, 仅使用标准输出(stdout)的第一个匹配的 IPv4 地址。如: ip -4 addr show eth1
<a target="blank" href="https://github.com/jeessy2/ddns-go/wiki/通过命令获取IP参考">点击参考更多</a>
`,
"Ipv6CmdHelp": `
通过命令获取IPv6, 仅使用标准输出(stdout)的第一个匹配的 IPv6 地址。如: ip -6 addr show eth1
<a target="blank" href="https://github.com/jeessy2/ddns-go/wiki/通过命令获取IP参考">点击参考更多</a>
`,
"NetInterfaceEmptyHelp": '<span style="color: red">没有找到可用的网卡</span>',
}
};
49 changes: 49 additions & 0 deletions static/i18n.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const LANG = localStorage.getItem('lang') || (navigator.language || navigator.browserLanguage).replaceAll('_', '-').toLowerCase();

// 支持两种调用方式:
// 1. 文本的key + (可选:语言映射字典),{en: {hello: "hello", world: "world"}, zh: {hello: "你好", world: "世界"}}
// 2. 语言字符串字典,{en: "hello", zh: "你好"}
const i18n = (key, langMap = I18N_MAP) => {
if (typeof key !== 'string') {
langMap = key;
key = null;
}
// 优先取地区语言,否则取表示语言,再否则取表示语言相同的地区语言,最后取英文
let lang = 'en';
if (LANG in langMap) {
lang = LANG;
} else if (LANG.split('-')[0] in langMap) {
lang = LANG.split('-')[0];
} else {
for (const l in langMap) {
if (l.split('-')[0] === LANG.split('-')[0]) {
lang = l;
break;
}
}
}
let text = '';
if (key) {
text = langMap[lang][key];
} else {
text = langMap[lang];
}
if (text === undefined) {
console.warn(`i18n: No translation for ${key}`);
return key;
}
return text;
}

const convertDom = (dom = document, ...args) => {
dom.querySelectorAll('[data-i18n]').forEach(el => {
const key = el.dataset.i18n;
el.textContent = i18n(key, ...args);
});
dom.querySelectorAll('[data-i18n_html]').forEach(el => {
const key = el.dataset.i18n_html;
el.innerHTML = i18n(key, ...args);
});
}

document.addEventListener('DOMContentLoaded', () => {convertDom();});
Loading

0 comments on commit 7befe6e

Please sign in to comment.