Merge pull request #1639 from master3395/v2.5.5-dev

- Fixed CloudFlare proxy toggle button to display as oblong with a round dot
- Enable CloudFlare proxy by default for all domains/subdomains except mail domains
- Automatically add AAAA (IPv6) DNS records when creating domains/subdomains
- Added GetServerIPv6() function to retrieve server IPv6 address
- Updated DNS template styling and Angular.js binding for toggle buttons
This commit is contained in:
Master3395 2026-01-04 04:17:02 +01:00 committed by GitHub
commit 11a78d81ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 147 additions and 29 deletions

View File

@ -472,41 +472,60 @@
}
}
.proxy-toggle {
appearance: none;
width: 48px;
height: 24px;
background: #e8e9ff;
border-radius: 24px;
position: relative;
cursor: pointer;
transition: all 0.3s ease;
input.proxy-toggle[type="checkbox"] {
-webkit-appearance: none !important;
-moz-appearance: none !important;
appearance: none !important;
width: 50px !important;
height: 26px !important;
min-width: 50px !important;
min-height: 26px !important;
max-width: 50px !important;
max-height: 26px !important;
background: #ccc !important;
border-radius: 13px !important;
position: relative !important;
cursor: pointer !important;
transition: background 0.3s ease !important;
border: none !important;
outline: none !important;
display: inline-block !important;
vertical-align: middle !important;
margin: 0 !important;
padding: 0 !important;
box-sizing: border-box !important;
}
.proxy-toggle:checked {
background: #5b5fcf;
input.proxy-toggle[type="checkbox"]:checked {
background: #5b5fcf !important;
}
.proxy-toggle::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 20px;
height: 20px;
background: var(--bg-primary, white);
border-radius: 50%;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
input.proxy-toggle[type="checkbox"]::before {
content: '' !important;
position: absolute !important;
width: 22px !important;
height: 22px !important;
border-radius: 50% !important;
background: white !important;
top: 2px !important;
left: 2px !important;
transition: left 0.3s ease, transform 0.3s ease !important;
box-shadow: 0 2px 4px rgba(0,0,0,0.2) !important;
display: block !important;
}
.proxy-toggle:checked::after {
left: 26px;
input.proxy-toggle[type="checkbox"]:checked::before {
left: 26px !important;
}
.proxy-toggle:disabled {
opacity: 0.5;
cursor: not-allowed;
input.proxy-toggle[type="checkbox"]:disabled {
opacity: 0.5 !important;
cursor: not-allowed !important;
}
input.proxy-toggle[type="checkbox"]:focus {
outline: 2px solid rgba(91, 95, 207, 0.3) !important;
outline-offset: 2px !important;
}
</style>
@ -876,7 +895,7 @@
<input class="proxy-toggle"
ng-click="enableProxy(record.name, record.proxy)"
ng-disabled="!record.proxiable"
ng-checked="record.proxy"
ng-checked="record.proxy"
type="checkbox">
</td>
<td style="text-align: center;">

View File

@ -1048,6 +1048,38 @@ class ACLManager:
ipData = f.read()
return ipData.split('\n', 1)[0]
@staticmethod
def GetServerIPv6():
"""
Get the server's primary IPv6 address (non-link-local, non-loopback)
Returns None if no IPv6 address is found
"""
try:
import subprocess
# Get IPv6 addresses, exclude link-local (fe80::) and loopback (::1)
result = subprocess.run(
['ip', '-6', 'addr', 'show'],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
lines = result.stdout.split('\n')
for line in lines:
if 'inet6' in line and '::1' not in line and 'fe80::' not in line:
# Extract IPv6 address (format: inet6 2a02:c207:2139:8929::1/64)
parts = line.strip().split()
if len(parts) >= 2:
ipv6 = parts[1].split('/')[0]
# Validate it's a real IPv6 (not link-local)
if not ipv6.startswith('fe80::'):
return ipv6
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error getting IPv6 address: {str(e)}')
return None
@staticmethod
def CheckForPremFeature(feature):
try:

View File

@ -220,6 +220,15 @@ class DNS:
DNS.createDNSRecord(zone, topLevelDomain, "A", ipAddress, 0, 3600)
# AAAA Record (IPv6) - Required for mail delivery to Google, Outlook, etc.
try:
from plogical.acl import ACLManager
ipv6Address = ACLManager.GetServerIPv6()
if ipv6Address:
DNS.createDNSRecord(zone, topLevelDomain, "AAAA", ipv6Address, 0, 3600)
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error creating AAAA record for {topLevelDomain}: {str(e)}')
# CNAME Records.
cNameValue = "www." + topLevelDomain
@ -282,6 +291,15 @@ class DNS:
DNS.createDNSRecord(zone, mxValue, "A", ipAddress, 0, 3600)
# AAAA Record for mail (IPv6) - Required for mail delivery
try:
from plogical.acl import ACLManager
ipv6Address = ACLManager.GetServerIPv6()
if ipv6Address:
DNS.createDNSRecord(zone, mxValue, "AAAA", ipv6Address, 0, 3600)
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error creating AAAA record for mail {mxValue}: {str(e)}')
## TXT Records for mail
# record = Records(domainOwner=zone,
@ -365,6 +383,15 @@ class DNS:
DNS.createDNSRecord(zone, topLevelDomain, "A", ipAddress, 0, 3600)
# AAAA Record (IPv6) - Required for mail delivery to Google, Outlook, etc.
try:
from plogical.acl import ACLManager
ipv6Address = ACLManager.GetServerIPv6()
if ipv6Address:
DNS.createDNSRecord(zone, topLevelDomain, "AAAA", ipv6Address, 0, 3600)
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error creating AAAA record for {topLevelDomain}: {str(e)}')
# CNAME Records.
cNameValue = "www." + topLevelDomain
@ -427,6 +454,15 @@ class DNS:
DNS.createDNSRecord(zone, mxValue, "A", ipAddress, 0, 3600)
# AAAA Record for mail (IPv6) - Required for mail delivery
try:
from plogical.acl import ACLManager
ipv6Address = ACLManager.GetServerIPv6()
if ipv6Address:
DNS.createDNSRecord(zone, mxValue, "AAAA", ipv6Address, 0, 3600)
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error creating AAAA record for mail {mxValue}: {str(e)}')
## TXT Records for mail
# record = Records(domainOwner=zone,
@ -478,10 +514,27 @@ class DNS:
DNS.createDNSRecord(zone, actualSubDomain, "A", ipAddress, 0, 3600)
# AAAA Record for subdomain (IPv6)
try:
from plogical.acl import ACLManager
ipv6Address = ACLManager.GetServerIPv6()
if ipv6Address:
DNS.createDNSRecord(zone, actualSubDomain, "AAAA", ipv6Address, 0, 3600)
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error creating AAAA record for subdomain {actualSubDomain}: {str(e)}')
## Mail Record
if ('mail.%s' % (actualSubDomain)).find('mail.mail') == -1:
DNS.createDNSRecord(zone, 'mail.' + actualSubDomain, "A", ipAddress, 0, 3600)
# AAAA Record for mail subdomain (IPv6) - Required for mail delivery
try:
from plogical.acl import ACLManager
ipv6Address = ACLManager.GetServerIPv6()
if ipv6Address:
DNS.createDNSRecord(zone, 'mail.' + actualSubDomain, "AAAA", ipv6Address, 0, 3600)
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error creating AAAA record for mail subdomain {actualSubDomain}: {str(e)}')
# CNAME Records.
@ -656,17 +709,31 @@ class DNS:
return 0
@staticmethod
def createDNSRecordCloudFlare(cf, zone, name, type, value, priority, ttl):
def createDNSRecordCloudFlare(cf, zone, name, type, value, priority, ttl, proxied=None):
try:
if value.find('DKIM') > -1:
value = value.replace('\n\t', '')
value = value.replace('"', '')
# Only A and CNAME records can be proxied in CloudFlare
# Determine if proxy should be enabled (default: True for A/CNAME, except for mail domains)
if proxied is None and type in ['A', 'CNAME']:
# Check if this is a mail domain (starts with 'mail.' or contains 'mail.')
is_mail_domain = name.lower().startswith('mail.') or '.mail.' in name.lower()
proxied = not is_mail_domain
elif type not in ['A', 'CNAME']:
# AAAA, MX, TXT, etc. cannot be proxied
proxied = False
if ttl > 0:
dns_record = {'name': name, 'type': type, 'content': value, 'ttl': ttl, 'priority': priority}
else:
dns_record = {'name': name, 'type': type, 'content': value, 'priority': priority}
# Only add proxied parameter for A and CNAME records
if type in ['A', 'CNAME']:
dns_record['proxied'] = proxied
cf.zones.dns_records.post(zone, data=dns_record)
except BaseException as msg: