Files
2026-06-02 00:32:05 +08:00

190 lines
5.2 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace app\admin\model;
use think\Model;
/**
* 域名管理模型
*/
class Domain extends Model
{
/** @var string 中国域名后缀黑名单 */
public const CHINA_SUFFIX_BLACKLIST = [
'cn', 'com.cn', 'net.cn', 'org.cn', 'gov.cn', 'edu.cn', 'ac.cn', 'mil.cn',
];
protected $name = 'domain';
protected $autoWriteTimestamp = 'integer';
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
protected $deleteTime = false;
protected $append = [
'zone_status_text',
'ns_status_text',
'dns_status_text',
];
public function getZoneStatusList(): array
{
return [
'pending' => __('Zone pending'),
'active' => __('Zone active'),
'failed' => __('Zone failed'),
];
}
public function getNsStatusList(): array
{
return [
'pending' => __('NS pending'),
'verified' => __('NS verified'),
'failed' => __('NS failed'),
];
}
public function getDnsStatusList(): array
{
return [
'pending' => __('DNS pending'),
'created' => __('DNS created'),
'failed' => __('DNS failed'),
];
}
public function getZoneStatusTextAttr($value, $data): string
{
$value = $value ?: ($data['zone_status'] ?? '');
$list = $this->getZoneStatusList();
return $list[$value] ?? '';
}
public function getNsStatusTextAttr($value, $data): string
{
$value = $value ?: ($data['ns_status'] ?? '');
$list = $this->getNsStatusList();
return $list[$value] ?? '';
}
public function getDnsStatusTextAttr($value, $data): string
{
$value = $value ?: ($data['dns_status'] ?? '');
$list = $this->getDnsStatusList();
return $list[$value] ?? '';
}
public function setNameserversAttr($value): string
{
if (is_array($value)) {
return json_encode(array_values($value), JSON_UNESCAPED_UNICODE);
}
return (string)$value;
}
/**
* @return array<int, string>
*/
public function getNameserversArray(): array
{
$value = $this->getAttr('nameservers');
if (is_array($value)) {
return $value;
}
if (is_string($value) && $value !== '') {
$decoded = json_decode($value, true);
return is_array($decoded) ? $decoded : [];
}
return [];
}
public static function normalizeDomain(string $domain): string
{
return strtolower(trim($domain));
}
public static function isValidRootDomain(string $domain): bool
{
$domain = self::normalizeDomain($domain);
if ($domain === '' || strlen($domain) > 253) {
return false;
}
if (strpos($domain, 'www.') === 0) {
return false;
}
if (!preg_match('/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/i', $domain)) {
return false;
}
if (self::isChinaSuffix($domain)) {
return false;
}
$label = self::getDomainLabelWithoutSuffix($domain);
if ($label === '' || strpos($label, '.') !== false) {
return false;
}
return true;
}
public static function isChinaSuffix(string $domain): bool
{
$domain = self::normalizeDomain($domain);
foreach (self::CHINA_SUFFIX_BLACKLIST as $suffix) {
if ($domain === $suffix || self::endsWith($domain, '.' . $suffix)) {
return true;
}
}
return false;
}
public static function getMatchedSuffix(string $domain): string
{
$domain = self::normalizeDomain($domain);
$suffixes = array_merge(self::CHINA_SUFFIX_BLACKLIST, ['co.uk', 'com', 'net', 'org', 'io', 'co', 'me', 'info', 'biz']);
usort($suffixes, function ($a, $b) {
return strlen($b) - strlen($a);
});
foreach ($suffixes as $suffix) {
if ($domain === $suffix || self::endsWith($domain, '.' . $suffix)) {
return $suffix;
}
}
return '';
}
public static function getDomainLabelWithoutSuffix(string $domain): string
{
$domain = self::normalizeDomain($domain);
$suffix = self::getMatchedSuffix($domain);
if ($suffix === '') {
$pos = strrpos($domain, '.');
return $pos === false ? $domain : substr($domain, 0, $pos);
}
$suffixWithDot = '.' . $suffix;
if (self::endsWith($domain, $suffixWithDot)) {
return substr($domain, 0, -strlen($suffixWithDot));
}
return $domain;
}
public static function mapCloudflareZoneStatus(string $status): string
{
if ($status === 'active') {
return 'active';
}
if (in_array($status, ['pending', 'initializing'], true)) {
return 'pending';
}
return 'failed';
}
private static function endsWith(string $haystack, string $needle): bool
{
if ($needle === '') {
return true;
}
return substr($haystack, -strlen($needle)) === $needle;
}
}