Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: 重构前端代码 #988

Merged
merged 10 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
266 changes: 266 additions & 0 deletions static/constant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
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',
'ttl_help': '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',
'domains_help': `
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.',
'reg_help': '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',
'NotAllowWanAccess_help': 'Default enabled, can prohibit access to this page from the public network',
'Username': 'Username',
'account_help': 'Please enter to protect your information security',
'Password': 'Password',
'WebhookURL_help': `
<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}
`,
'WebhookRequestBody_help': 'If RequestBody is empty, it is a GET request, otherwise it is a POST request. Supported variables are the same as above',
'WebhookHeaders_help': '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小时',
'ttl_help': '如账号支持更小的 TTL, 可修改。IP 有变化时才会更新TTL',
'Enabled': '是否启用',
'Get IP method': '获取 IP 方式',
'By api': '通过接口获取',
'By network card': '通过网卡获取',
'By command': '通过命令获取',
'domains_help': `
一行一个域名, 可使用冒号分隔根域名(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.': '匹配正则表达式',
'reg_help': '可使用 @1 指定第一个IPv6地址, @2 指定第二个IPv6地址... 也可使用正则表达式匹配指定的IPv6地址, 留空则不启用',
'Others': '其他',
'Deny from WAN': '禁止公网访问',
'NotAllowWanAccess_help': '默认启用, 可禁止从公网访问本页面',
'Username': '用户名',
'account_help': '为保护你的信息安全,建议输入',
'Password': '密码',
'WebhookURL_help': `
<a target="blank" href="https://github.com/jeessy2/ddns-go#webhook">点击参考官方 Webhook 说明</a>
<br />
支持的变量 #{ipv4Addr}, #{ipv4Result}, #{ipv4Domains}, #{ipv6Addr}, #{ipv6Result}, #{ipv6Domains}
`,
'WebhookRequestBody_help': '如果 RequestBody 为空, 则为 GET 请求, 否则为 POST 请求。支持的变量同上',
'WebhookHeaders_help': '一行一个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>
`,
"Ipv4NetInterfaceEmptyHelp": "",
PairZhu marked this conversation as resolved.
Show resolved Hide resolved
"NetInterfaceEmptyHelp": '<span style="color: red">没有找到可用的网卡</span>',
}
};
60 changes: 60 additions & 0 deletions static/i18n.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const LANG = localStorage.getItem('lang') || (navigator.language || navigator.browserLanguage).replaceAll('_', '-').toLowerCase();

// 添加新的语言字典
const addI18n = (arg1, arg2) => {
if (typeof arg1 === 'string') {
I18N_MAP[arg1] = arg2;
} else {
for (let key in arg1) {
I18N_MAP[key] = arg1[key];
}
}
}
PairZhu marked this conversation as resolved.
Show resolved Hide resolved

// 支持两种调用方式:
// 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