rename apikey to dnsapikey, refactor ui and add dig verification in integration test

This commit is contained in:
Manoj Kumar 2026-04-17 06:57:12 +05:30
parent 2d14c266ad
commit 001a197689
No known key found for this signature in database
GPG Key ID: E952B7234D2C6F88
17 changed files with 164 additions and 103 deletions

View File

@ -1352,6 +1352,7 @@ public class ApiConstants {
// DNS provider related
public static final String NAME_SERVERS = "nameservers";
public static final String DNS_USER_NAME = "dnsusername";
public static final String DNS_API_KEY = "dnsapikey";
public static final String DNS_ZONE_ID = "dnszoneid";
public static final String DNS_ZONE = "dnszone";
public static final String DNS_RECORD = "dnsrecord";

View File

@ -59,11 +59,11 @@ public class AddDnsServerCmd extends BaseCmd {
private String provider;
@Parameter(name = ApiConstants.DNS_USER_NAME, type = CommandType.STRING,
description = "Username or email associated with the external DNS provider account (used for authentication)")
description = "Username or email associated with the DNS provider account (used for authentication)")
private String dnsUserName;
@Parameter(name = ApiConstants.API_KEY, required = true, type = CommandType.STRING, description = "API key or token for the external provider")
private String apiKey;
@Parameter(name = ApiConstants.DNS_API_KEY, required = true, type = CommandType.STRING, description = "API key or token for the DNS provider")
private String dnsApiKey;
@Parameter(name = ApiConstants.PORT, type = CommandType.INTEGER, description = "Port number of the external DNS server")
private Integer port;
@ -89,8 +89,8 @@ public class AddDnsServerCmd extends BaseCmd {
public String getUrl() { return url; }
public String getApiKey() {
return apiKey;
public String getDnsApiKey() {
return dnsApiKey;
}
public Integer getPort() {

View File

@ -58,8 +58,8 @@ public class UpdateDnsServerCmd extends BaseCmd {
@Parameter(name = ApiConstants.URL, type = CommandType.STRING, description = "API URL of the provider")
private String url;
@Parameter(name = ApiConstants.API_KEY, type = CommandType.STRING, required = false, description = "API Key or Credentials for the external provider")
private String apiKey;
@Parameter(name = ApiConstants.DNS_API_KEY, type = CommandType.STRING, required = false, description = "API Key or Credentials for the external provider")
private String dnsApiKey;
@Parameter(name = ApiConstants.PORT, type = CommandType.INTEGER, description = "Port number of the external DNS server")
private Integer port;
@ -83,8 +83,8 @@ public class UpdateDnsServerCmd extends BaseCmd {
public Long getId() { return id; }
public String getName() { return name; }
public String getUrl() { return url; }
public String getApiKey() {
return apiKey;
public String getDnsApiKey() {
return dnsApiKey;
}
public Integer getPort() {
return port;

View File

@ -37,7 +37,7 @@ public interface DnsServer extends InternalIdentity, Identity, ControlledEntity
List<String> getNameServers();
String getApiKey();
String getDnsApiKey();
long getAccountId();

View File

@ -40,7 +40,7 @@ public class AddDnsServerCmdTest extends BaseDnsCmdTest {
setField(cmd, "name", "test-dns");
setField(cmd, "url", "http://dns.example.com");
setField(cmd, "provider", "PowerDNS");
setField(cmd, "apiKey", "api-key-123");
setField(cmd, "dnsApiKey", "api-key-123");
setField(cmd, "port", 8081);
setField(cmd, "isPublic", true);
setField(cmd, "publicDomainSuffix", "public.example.com");
@ -56,7 +56,7 @@ public class AddDnsServerCmdTest extends BaseDnsCmdTest {
assertEquals("test-dns", cmd.getName());
assertEquals("http://dns.example.com", cmd.getUrl());
assertEquals("api-key-123", cmd.getApiKey());
assertEquals("api-key-123", cmd.getDnsApiKey());
assertEquals(Integer.valueOf(8081), cmd.getPort());
assertTrue(cmd.isPublic());
assertEquals("public.example.com", cmd.getPublicDomainSuffix());

View File

@ -39,7 +39,7 @@ public class UpdateDnsServerCmdTest extends BaseDnsCmdTest {
setField(cmd, "id", ENTITY_ID);
setField(cmd, "name", "updated-dns");
setField(cmd, "url", "http://updated.dns.com");
setField(cmd, "apiKey", "new-api-key");
setField(cmd, "dnsApiKey", "new-api-key");
setField(cmd, "port", 9090);
setField(cmd, "isPublic", true);
setField(cmd, "publicDomainSuffix", "updated.example.com");
@ -55,7 +55,7 @@ public class UpdateDnsServerCmdTest extends BaseDnsCmdTest {
assertEquals(Long.valueOf(ENTITY_ID), cmd.getId());
assertEquals("updated-dns", cmd.getName());
assertEquals("http://updated.dns.com", cmd.getUrl());
assertEquals("new-api-key", cmd.getApiKey());
assertEquals("new-api-key", cmd.getDnsApiKey());
assertEquals(Integer.valueOf(9090), cmd.getPort());
assertTrue(cmd.isPublic());
assertEquals("updated.example.com", cmd.getPublicDomainSuffix());

View File

@ -131,7 +131,7 @@ CREATE TABLE IF NOT EXISTS `cloud`.`dns_server` (
`provider_type` varchar(255) NOT NULL COMMENT 'Provider type such as PowerDns',
`url` varchar(1024) NOT NULL COMMENT 'dns server url',
`dns_username` varchar(255) COMMENT 'username or email for dns server credentials',
`api_key` varchar(255) NOT NULL COMMENT 'api key or token for the dns server ',
`dns_api_key` varchar(255) NOT NULL COMMENT 'api key or token for the dns server ',
`external_server_id` varchar(255) COMMENT 'dns server id e.g. localhost for powerdns',
`port` int(11) DEFAULT NULL COMMENT 'optional dns server port',
`name_servers` varchar(1024) DEFAULT NULL COMMENT 'Comma separated list of name servers',

View File

@ -44,13 +44,13 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider {
public void validate(DnsServer server) throws DnsProviderException {
validateRequiredServerFields(server);
client.validateServerId(server.getUrl(), server.getPort(), server.getApiKey(), server.getExternalServerId());
client.validateServerId(server.getUrl(), server.getPort(), server.getDnsApiKey(), server.getExternalServerId());
}
@Override
public String validateAndResolveServer(DnsServer server) throws Exception {
validateRequiredServerFields(server);
return client.resolveServerId(server.getUrl(), server.getPort(), server.getApiKey(), server.getExternalServerId());
return client.resolveServerId(server.getUrl(), server.getPort(), server.getDnsApiKey(), server.getExternalServerId());
}
@Override
@ -59,7 +59,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider {
return client.createZone(
server.getUrl(),
server.getPort(),
server.getApiKey(),
server.getDnsApiKey(),
server.getExternalServerId(),
zone.getName(),
ApiConstants.NATIVE_ZONE, false, server.getNameServers()
@ -69,7 +69,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider {
@Override
public void deleteZone(DnsServer server, DnsZone zone) throws DnsProviderException {
validateRequiredServerAndZoneFields(server, zone);
client.deleteZone(server.getUrl(), server.getPort(), server.getApiKey(), server.getExternalServerId(), zone.getName());
client.deleteZone(server.getUrl(), server.getPort(), server.getDnsApiKey(), server.getExternalServerId(), zone.getName());
}
@Override
@ -78,7 +78,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider {
client.updateZone(
server.getUrl(),
server.getPort(),
server.getApiKey(),
server.getDnsApiKey(),
server.getExternalServerId(),
zone.getName(), ApiConstants.NATIVE_ZONE, false, server.getNameServers());
}
@ -93,7 +93,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider {
return applyRecord(
server.getUrl(),
server.getPort(),
server.getApiKey(),
server.getDnsApiKey(),
server.getExternalServerId(),
zone.getName(), record, ChangeType.REPLACE);
}
@ -109,7 +109,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider {
validateRequiredServerAndZoneFields(server, zone);
return applyRecord(server.getUrl(),
server.getPort(),
server.getApiKey(),
server.getDnsApiKey(),
server.getExternalServerId(),
zone.getName(), record, ChangeType.DELETE);
}
@ -125,7 +125,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider {
public List<DnsRecord> listRecords(DnsServer server, DnsZone zone) throws DnsProviderException {
validateRequiredServerAndZoneFields(server, zone);
List<DnsRecord> records = new ArrayList<>();
Iterable<JsonNode> rrsetNodes = client.listRecords(server.getUrl(), server.getPort(), server.getApiKey(),
Iterable<JsonNode> rrsetNodes = client.listRecords(server.getUrl(), server.getPort(), server.getDnsApiKey(),
server.getExternalServerId(), zone.getName());
for (JsonNode rrset : rrsetNodes) {
@ -154,7 +154,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider {
}
public boolean dnsRecordExists(DnsServer server, DnsZone zone, String recordName, String recordType) throws DnsProviderException {
return client.dnsRecordExists(server.getUrl(), server.getPort(), server.getApiKey(),
return client.dnsRecordExists(server.getUrl(), server.getPort(), server.getDnsApiKey(),
server.getExternalServerId(), zone.getName(), recordName, recordType);
}
@ -169,7 +169,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider {
if (StringUtils.isBlank(server.getUrl())) {
throw new IllegalArgumentException("PowerDNS API URL cannot be empty");
}
if (StringUtils.isBlank(server.getApiKey())) {
if (StringUtils.isBlank(server.getDnsApiKey())) {
throw new IllegalArgumentException("PowerDNS API key cannot be empty");
}
}

View File

@ -73,7 +73,7 @@ public class PowerDnsProviderTest {
ReflectionTestUtils.setField(provider, "client", clientMock);
when(serverMock.getUrl()).thenReturn("http://pdns:8081");
when(serverMock.getApiKey()).thenReturn("secret");
when(serverMock.getDnsApiKey()).thenReturn("secret");
when(serverMock.getPort()).thenReturn(8081);
when(serverMock.getExternalServerId()).thenReturn("localhost");
when(serverMock.getNameServers()).thenReturn(Arrays.asList("ns1.example.com"));
@ -133,13 +133,13 @@ public class PowerDnsProviderTest {
@Test(expected = IllegalArgumentException.class)
public void testValidateServerFieldsNullApiKey() {
when(serverMock.getApiKey()).thenReturn(null);
when(serverMock.getDnsApiKey()).thenReturn(null);
provider.validateRequiredServerFields(serverMock);
}
@Test(expected = IllegalArgumentException.class)
public void testValidateServerFieldsBlankApiKey() {
when(serverMock.getApiKey()).thenReturn("");
when(serverMock.getDnsApiKey()).thenReturn("");
provider.validateRequiredServerFields(serverMock);
}

View File

@ -177,7 +177,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
DnsProviderType type = cmd.getProvider();
DnsServerVO server = new DnsServerVO(cmd.getName(), cmd.getUrl(), cmd.getPort(), cmd.getExternalServerId(), type,
cmd.getDnsUserName(), cmd.getApiKey(), isDnsPublic, publicDomainSuffix, cmd.getNameServers(),
cmd.getDnsUserName(), cmd.getDnsApiKey(), isDnsPublic, publicDomainSuffix, cmd.getNameServers(),
caller.getAccountId(), caller.getDomainId());
try {
DnsProvider provider = getProviderByType(type);
@ -233,7 +233,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
boolean validationRequired = false;
String originalUrl = dnsServer.getUrl();
String originalKey = dnsServer.getApiKey();
String originalKey = dnsServer.getDnsApiKey();
if (cmd.getName() != null) {
dnsServer.setName(cmd.getName());
@ -250,8 +250,8 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
}
}
if (cmd.getApiKey() != null && !cmd.getApiKey().equals(originalKey)) {
dnsServer.setApiKey(cmd.getApiKey());
if (cmd.getDnsApiKey() != null && !cmd.getDnsApiKey().equals(originalKey)) {
dnsServer.setDnsApiKey(cmd.getDnsApiKey());
validationRequired = true;
}

View File

@ -69,8 +69,8 @@ public class DnsServerVO implements DnsServer {
private String dnsUserName;
@Encrypt
@Column(name = "api_key")
private String apiKey;
@Column(name = "dns_api_key")
private String dnsApiKey;
@Column(name = "external_server_id")
private String externalServerId;
@ -107,7 +107,7 @@ public class DnsServerVO implements DnsServer {
this.created = new Date();
}
public DnsServerVO(String name, String url, Integer port, String externalServerId, DnsProviderType providerType, String dnsUserName, String apiKey,
public DnsServerVO(String name, String url, Integer port, String externalServerId, DnsProviderType providerType, String dnsUserName, String dnsApiKey,
boolean isPublic, String publicDomainSuffix, List<String> nameServers, Long accountId, Long domainId) {
this();
this.name = name;
@ -116,7 +116,7 @@ public class DnsServerVO implements DnsServer {
this.externalServerId = externalServerId;
this.providerType = providerType;
this.dnsUserName = dnsUserName;
this.apiKey = apiKey;
this.dnsApiKey = dnsApiKey;
this.accountId = accountId;
this.domainId = domainId;
this.publicDomainSuffix = publicDomainSuffix;
@ -151,8 +151,8 @@ public class DnsServerVO implements DnsServer {
}
@Override
public String getApiKey() {
return apiKey;
public String getDnsApiKey() {
return dnsApiKey;
}
@Override
@ -209,8 +209,8 @@ public class DnsServerVO implements DnsServer {
this.publicDomainSuffix = publicDomainSuffix;
}
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
public void setDnsApiKey(String dnsApiKey) {
this.dnsApiKey = dnsApiKey;
}
public void setPort(Integer port) {

View File

@ -849,10 +849,10 @@ public class DnsProviderManagerImplTest {
org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock(
org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class);
when(cmd.getId()).thenReturn(SERVER_ID);
when(cmd.getApiKey()).thenReturn("new-api-key");
when(cmd.getDnsApiKey()).thenReturn("new-api-key");
when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO);
Mockito.doReturn("old-api-key").when(serverVO).getApiKey();
Mockito.doReturn("old-api-key").when(serverVO).getDnsApiKey();
Mockito.doReturn("http://original:8081").when(serverVO).getUrl();
Mockito.doReturn(DnsProviderType.PowerDNS).when(serverVO).getProviderType();

View File

@ -51,7 +51,7 @@ public class DnsServerVOTest {
assertEquals(Integer.valueOf(8081), vo.getPort());
assertEquals("localhost", vo.getExternalServerId());
assertEquals(DnsProviderType.PowerDNS, vo.getProviderType());
assertEquals("api-key-123", vo.getApiKey());
assertEquals("api-key-123", vo.getDnsApiKey());
assertTrue(vo.getPublicServer());
assertEquals("public.example.com", vo.getPublicDomainSuffix());
assertEquals(nameServers, vo.getNameServers());
@ -82,8 +82,8 @@ public class DnsServerVOTest {
vo.setPort(9090);
assertEquals(Integer.valueOf(9090), vo.getPort());
vo.setApiKey("new-key");
assertEquals("new-key", vo.getApiKey());
vo.setDnsApiKey("new-key");
assertEquals("new-key", vo.getDnsApiKey());
vo.setPublicServer(true);
assertTrue(vo.getPublicServer());

View File

@ -160,6 +160,7 @@ class TestCloudStackDNSFramework(cloudstackTestCase):
)
self.assertIsNotNone(response, "Failed to create DNS record")
self.assertEqual(response.name, "www.example.com", "DNS record name mismatch")
self._assert_dns("www.example.com", "A", expected="10.1.1.10")
def test_07_create_aaaa_dns_records(self):
"""
@ -174,6 +175,7 @@ class TestCloudStackDNSFramework(cloudstackTestCase):
)
self.assertIsNotNone(response, "Failed to create AAAA DNS record")
self.assertTrue(response.name is not None, "DNS record name should not be None")
self._assert_dns("www.example.com", "AAAA", expected="2001:db8::10")
def test_08_create_mx_dns_record(self):
@ -189,6 +191,7 @@ class TestCloudStackDNSFramework(cloudstackTestCase):
)
self.assertIsNotNone(response, "Failed to create MX DNS record")
self.assertTrue(response.name is not None, "DNS record name should not be None")
self._assert_dns("example.com", "MX", contains=["10", "mail.example.com"])
def test_09_list_dns_records(self):
@ -299,7 +302,7 @@ class TestCloudStackDNSFramework(cloudstackTestCase):
cmd = addDnsServer.addDnsServerCmd()
cmd.name = "pdns-server"
cmd.url = self.pdns_url
cmd.credentials = "supersecretapikey"
cmd.dnsapikey = "supersecretapikey"
cmd.provider = "PowerDNS"
cmd.nameservers = ["ns1.example.com", "ns2.example.com"]
cmd.externalserverid = "localhost"
@ -309,6 +312,7 @@ class TestCloudStackDNSFramework(cloudstackTestCase):
return self.api_client.addDnsServer(cmd)
def _create_zone(self, server_id):
cmd = createDnsZone.createDnsZoneCmd()
cmd.dnsserverid = server_id
@ -316,3 +320,43 @@ class TestCloudStackDNSFramework(cloudstackTestCase):
cmd.description = "Test DNS Zone for PDNS"
return self.api_client.createDnsZone(cmd)
def _dig(self, name, rtype):
dns_ip = self.__class__.marvin_vm_ip
dns_port = 5353
cmd = [
"dig",
f"@{dns_ip}",
"-p",
str(dns_port),
name,
rtype,
"+short"
]
self.logger.info(f"Running: {' '.join(cmd)}")
result = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
self.assertEqual(result.returncode, 0, f"dig failed: {result.stderr}")
output = result.stdout.strip().replace("\n", " ")
self.logger.info(f"dig output: {output}")
return output
def _assert_dns(self, name, rtype, expected=None, contains=None):
output = self._dig(name, rtype)
if expected is not None:
self.assertIn(expected, output)
if contains:
for item in contains:
self.assertIn(item, output)
return output

View File

@ -953,11 +953,11 @@
"label.dns1": "DNS 1",
"label.dns2": "DNS 2",
"label.dns.add.server": "Add DNS Server",
"label.dns.apikey": "DNS API key",
"label.dns.create.record": "Create DNS Record",
"label.dns.create.zone": "Create DNS Zone",
"label.dns.delete.server": "Delete DNS Server",
"label.dns.delete.zone": "Delete DNS Zone",
"label.dns.dnsapikey": "DNS API key",
"label.dns.externalserverid": "DNS server ID",
"label.dns.publicdomainsuffix": "Public domain suffix",
"label.dns.records": "DNS Records",

View File

@ -60,9 +60,9 @@
showSearch>
<a-select-option
v-for="provider in providers"
:key="provider.name || provider"
:value="provider.name || provider">
{{ provider.description || provider.name || provider }}
:key="provider.name"
:value="provider.name">
{{ provider.description || provider.name }}
</a-select-option>
</a-select>
</a-form-item>
@ -80,15 +80,15 @@
style="width: 100%" />
</a-form-item>
<a-form-item name="apikey" ref="apikey">
<a-form-item name="dnsapikey" ref="dnsapikey">
<template #label>
<tooltip-label
:title="$t('label.dns.apikey')"
:tooltip="apiParams.apikey?.description" />
:title="$t('label.dns.dnsapikey')"
:tooltip="apiParams.dnsapikey?.description" />
</template>
<a-input-password
v-model:value="form.apikey"
:placeholder="apiParams.apikey?.description || 'Enter API Key'" />
v-model:value="form.dnsapikey"
:placeholder="apiParams.dnsapikey?.description || 'Enter API Key'" />
</a-form-item>
<a-form-item name="externalserverid" ref="externalserverid">
@ -125,7 +125,8 @@
mode="tags"
style="width: 100%"
:token-separators="[',', ' ']"
:placeholder="apiParams.nameservers?.description" />
:placeholder="apiParams.nameservers?.description"
@change="onNameserversChange" />
</a-form-item>
<a-form-item v-if="isAdminOrDomainAdmin()" name="ispublic" ref="ispublic">
@ -155,6 +156,8 @@
import { getAPI, postAPI } from '@/api'
import TooltipLabel from '@/components/widgets/TooltipLabel'
const FQDN_REGEX = /^(?=.{1,253}$)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z]{2,})+$/
export default {
name: 'AddDnsServer',
components: {
@ -192,13 +195,15 @@ export default {
{ validator: this.validatePort }
],
provider: [{ required: true, message: this.$t('message.error.required.input') }],
apikey: [{ required: true, message: this.$t('message.error.required.input') }],
dnsapikey: [{ required: true, message: this.$t('message.error.required.input') }],
externalserverid: [{ required: true, message: this.$t('message.error.required.input') }],
nameservers: [
{ required: true, type: 'array', min: 1, message: this.$t('message.error.required.input') },
{ validator: this.validateNameservers }
],
publicdomainsuffix: [{ validator: this.validatePublicDomainSuffix }]
]
}
if (this.isAdminOrDomainAdmin()) {
this.rules.publicdomainsuffix = [{ validator: this.validatePublicDomainSuffix }]
}
this.fetchProviders()
},
@ -217,16 +222,16 @@ export default {
try {
const params = {
name: this.form.name.trim(),
url: this.form.url?.trim().replace(/\/$/, ''),
url: this.form.url?.trim().replace(/\/+$/, ''),
port: this.form.port,
provider: this.form.provider,
apikey: this.form.apikey.trim(),
externalserverid: this.form.externalserverid.trim(),
nameservers: this.form.nameservers?.map(ns => ns.toLowerCase().trim()).filter(Boolean),
dnsapikey: this.form.dnsapikey?.trim(),
externalserverid: this.form.externalserverid?.trim(),
nameservers: this.form.nameservers || [],
ispublic: this.form.ispublic
}
if (this.form.ispublic) {
params.publicdomainsuffix = this.form.publicdomainsuffix?.trim().toLowerCase()
params.publicdomainsuffix = this.form.publicdomainsuffix?.toLowerCase().trim()
}
await postAPI('addDnsServer', params)
this.$notification.success({
@ -253,13 +258,14 @@ export default {
try {
const response = await getAPI('listDnsProviders')
const listResponse = response?.listdnsprovidersresponse || {}
this.providers = listResponse.dnsprovider || listResponse.provider || []
if (this.providers.length > 0) {
const defaultProvider = this.providers[0]
this.form.provider = defaultProvider.name || defaultProvider
this.providers = Array.isArray(listResponse.dnsprovider) ? listResponse.dnsprovider : []
if (!this.form.provider && this.providers.length > 0) {
this.form.provider = this.providers[0].name || ''
}
} catch (error) {
console.error('Failed to fetch DNS providers', error)
this.providers = []
this.form.provider = ''
this.$message.warning('Could not load DNS providers.')
} finally {
this.fetchingProviders = false
@ -296,10 +302,8 @@ export default {
return Promise.resolve()
}
const fqdnRegex = /^(?=.{1,253}$)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z]{2,})+$/
for (const ns of value) {
if (!fqdnRegex.test(ns)) {
if (!FQDN_REGEX.test(ns)) {
return Promise.reject(new Error('Invalid nameserver'))
}
}
@ -314,14 +318,11 @@ export default {
if (!this.form.ispublic) {
return Promise.resolve()
}
if (!value) {
const normalized = value?.toLowerCase().trim()
if (!normalized) {
return Promise.reject(new Error(this.$t('message.error.required.publicdomainsuffix')))
}
const fqdnRegex = /^(?=.{1,253}$)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z]{2,})+$/
if (!fqdnRegex.test(value)) {
if (!FQDN_REGEX.test(normalized)) {
return Promise.reject(new Error('Invalid domain suffix'))
}
@ -329,6 +330,11 @@ export default {
},
isAdminOrDomainAdmin () {
return ['Admin', 'DomainAdmin'].includes(this.$store.getters.userInfo.roletype)
},
onNameserversChange (value) {
this.form.nameservers = (value || [])
.map(ns => ns?.toLowerCase().trim())
.filter(Boolean)
}
}
}

View File

@ -22,7 +22,8 @@
ref="formRef"
:model="form"
:rules="rules"
layout="vertical">
layout="vertical"
:disabled="loading">
<a-form-item name="name" ref="name">
<template #label>
@ -33,7 +34,7 @@
<a-input
v-model:value="form.name"
:placeholder="apiParams.name?.description"
v-focus="true" />
v-focus />
</a-form-item>
<a-form-item name="url" ref="url">
@ -60,11 +61,11 @@
style="width: 100%" />
</a-form-item>
<a-form-item name="apikey">
<a-form-item name="dnsapikey">
<template #label>
<tooltip-label :title="$t('label.dns.apikey')" :tooltip="apiParams.apikey?.description" />
<tooltip-label :title="$t('label.dns.dnsapikey')" :tooltip="apiParams.dnsapikey?.description" />
</template>
<a-input-password v-model:value="form.apikey" :placeholder="apiParams.apikey?.description" />
<a-input-password v-model:value="form.dnsapikey" :placeholder="apiParams.dnsapikey?.description" />
</a-form-item>
<a-form-item v-if="isAdminOrDomainAdmin()" name="publicdomainsuffix">
@ -86,7 +87,8 @@
mode="tags"
style="width: 100%"
:token-separators="[',', ' ']"
:placeholder="apiParams.nameservers?.description" />
:placeholder="apiParams.nameservers?.description"
@change="onNameserversChange"/>
</a-form-item>
<a-form-item v-if="isAdminOrDomainAdmin()" name="ispublic">
@ -116,6 +118,8 @@
import { postAPI } from '@/api'
import TooltipLabel from '@/components/widgets/TooltipLabel'
const FQDN_REGEX = /^(?=.{1,253}$)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z]{2,})+$/
export default {
name: 'UpdateDnsServer',
components: {
@ -134,7 +138,8 @@ export default {
form: {
name: '',
url: '',
port: 53
port: 53,
dnsapikey: ''
},
rules: {}
}
@ -151,15 +156,22 @@ export default {
{ required: true, message: this.$t('message.error.required.input') },
{ validator: this.validatePort }
],
publicdomainsuffix: [{ validator: this.validatePublicDomainSuffix }],
state: [{ required: true, message: this.$t('message.error.required.input') }]
nameservers: [
{ required: true, type: 'array', min: 1, message: this.$t('message.error.required.input') },
{ validator: this.validateNameservers }
]
}
if (this.isAdminOrDomainAdmin()) {
this.rules.publicdomainsuffix = [{ validator: this.validatePublicDomainSuffix }]
}
this.form.name = this.resource.name
this.form.url = this.resource.url
this.form.port = this.resource.port
this.form.publicdomainsuffix = this.resource.publicdomainsuffix
this.form.nameservers = this.resource.nameservers || []
this.form.ispublic = this.resource.ispublic
this.form.ispublic = this.resource.ispublic ?? false
if (this.isAdminOrDomainAdmin()) {
this.form.publicdomainsuffix = this.resource.publicdomainsuffix
}
},
methods: {
async handleSubmit () {
@ -180,14 +192,14 @@ export default {
const params = {
id: this.resource.id,
name: this.form.name.trim(),
url: this.form.url?.trim().replace(/\/$/, ''),
url: this.form.url?.trim().replace(/\/+$/, ''),
port: this.form.port,
nameservers: this.form.nameservers?.map(ns => ns.toLowerCase().trim()).filter(Boolean),
ispublic: this.form.ispublic,
state: this.form.state
nameservers: this.form.nameservers || [],
ispublic: this.form.ispublic
}
if (this.form.apikey) {
params.apikey = this.form.apikey.trim()
const dnsapikey = this.form.dnsapikey?.trim()
if (dnsapikey) {
params.dnsapikey = dnsapikey
}
if (this.form.ispublic) {
params.publicdomainsuffix = this.form.publicdomainsuffix?.trim().toLowerCase()
@ -244,10 +256,8 @@ export default {
return Promise.resolve()
}
const fqdnRegex = /^(?=.{1,253}$)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z]{2,})+$/
for (const ns of value) {
if (!fqdnRegex.test(ns)) {
if (!FQDN_REGEX.test(ns)) {
return Promise.reject(new Error('Invalid nameserver'))
}
}
@ -262,14 +272,11 @@ export default {
if (!this.form.ispublic) {
return Promise.resolve()
}
if (!value) {
const normalized = value?.toLowerCase().trim()
if (!normalized) {
return Promise.reject(new Error(this.$t('message.error.required.publicdomainsuffix')))
}
const fqdnRegex = /^(?=.{1,253}$)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z]{2,})+$/
if (!fqdnRegex.test(value)) {
if (!FQDN_REGEX.test(normalized)) {
return Promise.reject(new Error('Invalid domain suffix'))
}
@ -277,6 +284,9 @@ export default {
},
isAdminOrDomainAdmin () {
return ['Admin', 'DomainAdmin'].includes(this.$store.getters.userInfo.roletype)
},
onNameserversChange (value) {
this.form.nameservers = Array.isArray(value) ? value.map(ns => ns.toLowerCase().trim()).filter(Boolean) : []
}
}
}