Kaynağa Gözat

fix: allow dns credentials across zones

AI-Co-Authored-By: Codex
chendeben 1 ay önce
ebeveyn
işleme
c9d5a43f71

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
public/assets/index-kRpKLbfI.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
public/assets/login-B0xej5ug.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
public/assets/styles-JlAcutjU.js


+ 2 - 2
public/index.html

@@ -4,8 +4,8 @@
     <meta charset="UTF-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>MailHub</title>
-    <script type="module" crossorigin src="/assets/index-DVZJ2w6n.js"></script>
-    <link rel="modulepreload" crossorigin href="/assets/styles-5CnVXnu6.js">
+    <script type="module" crossorigin src="/assets/index-kRpKLbfI.js"></script>
+    <link rel="modulepreload" crossorigin href="/assets/styles-JlAcutjU.js">
     <link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-Dezn_h7o.js">
     <link rel="stylesheet" crossorigin href="/assets/styles-yTEP6Wre.css">
     <link rel="stylesheet" crossorigin href="/assets/index-Tu04tXLf.css">

+ 2 - 2
public/login.html

@@ -4,8 +4,8 @@
     <meta charset="UTF-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>MailHub Auth</title>
-    <script type="module" crossorigin src="/assets/login-DhHJ-nET.js"></script>
-    <link rel="modulepreload" crossorigin href="/assets/styles-5CnVXnu6.js">
+    <script type="module" crossorigin src="/assets/login-B0xej5ug.js"></script>
+    <link rel="modulepreload" crossorigin href="/assets/styles-JlAcutjU.js">
     <link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-Dezn_h7o.js">
     <link rel="stylesheet" crossorigin href="/assets/styles-yTEP6Wre.css">
   </head>

+ 1 - 0
src/dns-providers.js

@@ -431,6 +431,7 @@ function isHostInZone(host, zoneName) {
 }
 
 function effectiveZoneName(credential, domain, record) {
+  if (domain?.domain) return domain.domain;
   if (credential.provider !== 'cloudflare') return credential.zoneName || '';
   const configuredZone = credential.zoneName || '';
   if (configuredZone && isHostInZone(record.host, configuredZone)) return configuredZone;

+ 2 - 0
src/frontend/i18n/index.js

@@ -287,6 +287,7 @@ const messages = {
     'dnsApi.editTitle': '编辑',
     'dnsApi.zone': 'Zone / 根域名',
     'dnsApi.zoneRequired': '请输入 Zone 或根域名',
+    'dnsApi.zoneExtra': '填写该账号下任一可管理 Zone 用于连接测试;一键配置时会按当前发信域名自动匹配 Zone。',
     'dnsApi.cloudflareZoneExtra': 'Cloudflare 可填写任一可访问 Zone 用于测试;一键配置时会按当前发信域名自动匹配 Zone。',
     'dnsApi.tokenExtra': '需要 Zone DNS Edit 权限。',
     'dnsApi.keepSecret': '留空表示保留原密钥。',
@@ -745,6 +746,7 @@ const messages = {
     'dnsApi.editTitle': 'Edit',
     'dnsApi.zone': 'Zone / root domain',
     'dnsApi.zoneRequired': 'Enter the Zone or root domain',
+    'dnsApi.zoneExtra': 'Use any manageable Zone under this account for connection testing. One-click DNS matches the current sending domain automatically.',
     'dnsApi.cloudflareZoneExtra': 'For Cloudflare, use any accessible Zone for testing. One-click DNS matches the current sending domain automatically.',
     'dnsApi.tokenExtra': 'Requires Zone DNS Edit permission.',
     'dnsApi.keepSecret': 'Leave empty to keep the existing secret.',

+ 1 - 1
src/pages/DnsApi.tsx

@@ -98,7 +98,7 @@ export default function DnsApi({ credentials, loading, onSave, onTest, onDelete
               name="zoneName"
               label={t('dnsApi.zone')}
               rules={[{ required: true, message: t('dnsApi.zoneRequired') }]}
-              extra={provider === 'cloudflare' ? t('dnsApi.cloudflareZoneExtra') : undefined}
+              extra={t('dnsApi.zoneExtra')}
             >
               <Input placeholder="example.com" />
             </Form.Item>

+ 77 - 0
test/dns-providers.test.js

@@ -198,6 +198,46 @@ test('aliyun one-click dns falls back to parent zone for subdomain sending domai
   )));
 });
 
+test('aliyun one-click dns can use another managed root zone with the same credentials', async () => {
+  const calls = [];
+  globalThis.fetch = async (url) => {
+    const params = new URL(String(url)).searchParams;
+    calls.push({
+      action: params.get('Action'),
+      domainName: params.get('DomainName'),
+      rr: params.get('RR')
+    });
+    if (params.get('Action') === 'DescribeDomainRecords') {
+      return json({ DomainRecords: { Record: [] } });
+    }
+    return json({});
+  };
+
+  const result = await applyDnsSetup(
+    { domain: 'phplife.net', senderHost: 'mail.phplife.net', sendingIp: '127.0.0.1' },
+    { ...aliyunCredential(), zoneName: 'ss5.xyz' },
+    {
+      records: [
+        {
+          key: 'verification',
+          host: '_mailhub.phplife.net',
+          type: 'TXT',
+          value: 'mailhub-verification=token',
+          status: 'missing'
+        }
+      ]
+    }
+  );
+
+  assert.equal(result.ok, true);
+  assert.ok(calls.some((call) => call.action === 'DescribeDomainRecords' && call.domainName === 'phplife.net'));
+  assert.ok(calls.some((call) => (
+    call.action === 'AddDomainRecord'
+    && call.domainName === 'phplife.net'
+    && call.rr === '_mailhub'
+  )));
+});
+
 test('dnspod provider signs and sends create record actions', async () => {
   const actions = [];
   globalThis.fetch = async (url, options = {}) => {
@@ -294,6 +334,43 @@ test('dnspod one-click dns falls back to parent zone for subdomain sending domai
   )));
 });
 
+test('dnspod one-click dns can use another managed root zone with the same credentials', async () => {
+  const calls = [];
+  globalThis.fetch = async (url, options = {}) => {
+    assert.equal(String(url), 'https://dnspod.tencentcloudapi.com');
+    const payload = JSON.parse(options.body);
+    calls.push({ action: options.headers['X-TC-Action'], payload });
+    if (options.headers['X-TC-Action'] === 'DescribeRecordList') {
+      return json({ Response: { RecordList: [] } });
+    }
+    return json({ Response: { RecordId: 123 } });
+  };
+
+  const result = await applyDnsSetup(
+    { domain: 'phplife.net', senderHost: 'mail.phplife.net', sendingIp: '127.0.0.1' },
+    { ...dnspodCredential(), zoneName: 'ss5.xyz' },
+    {
+      records: [
+        {
+          key: 'verification',
+          host: '_mailhub.phplife.net',
+          type: 'TXT',
+          value: 'mailhub-verification=token',
+          status: 'missing'
+        }
+      ]
+    }
+  );
+
+  assert.equal(result.ok, true);
+  assert.ok(calls.some((call) => call.action === 'DescribeRecordList' && call.payload.Domain === 'phplife.net'));
+  assert.ok(calls.some((call) => (
+    call.action === 'CreateRecord'
+    && call.payload.Domain === 'phplife.net'
+    && call.payload.SubDomain === '_mailhub'
+  )));
+});
+
 test('one-click dns setup only applies records under the user domain zone', async () => {
   const calls = [];
   globalThis.fetch = async (url, options = {}) => {

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor