上传文件至「/」
这个提交包含在:
@@ -3,9 +3,9 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
Parlz Domain 是一个开源、简洁的二级域名分发系统,使用的技术是 PHP 和 MySQL (MariaDB)。目前仅支持 Cloudflare API,后续会加入更多 API 支持。
|
Parlz Domain 是一个开源、简洁的二级域名分发系统,使用的技术是 PHP 和 MySQL (MariaDB)。目前支持 Cloudflare API 和 Aliyun API,后续会加入更多 API 支持。
|
||||||
|
|
||||||
域名会自动写入名称和记录值,目前会自动配置灰云及 TTL 5min。
|
域名会自动写入名称和记录值。
|
||||||
|
|
||||||
### `建立初衷`
|
### `建立初衷`
|
||||||
和 PicHost 一样,网上大多数程序都是 Vite、Go...搭建的。配置很麻烦,不仅要装一堆依赖而且还要整端口和反代。于是,我就开发这个源码了。可能会持续更新。
|
和 PicHost 一样,网上大多数程序都是 Vite、Go...搭建的。配置很麻烦,不仅要装一堆依赖而且还要整端口和反代。于是,我就开发这个源码了。可能会持续更新。
|
||||||
@@ -21,25 +21,16 @@ Parlz Domain 是一个开源、简洁的二级域名分发系统,使用的技
|
|||||||
找到 Release 并直接下载源码压缩包即可。
|
找到 Release 并直接下载源码压缩包即可。
|
||||||
|
|
||||||
### `配置` (重要)
|
### `配置` (重要)
|
||||||
打开 **`config.php`** 文件,根据注释来配置这些行:
|
1. 打开 **`config.php`** 文件,根据注释来配置这些行:
|
||||||
```php
|
```php
|
||||||
define('DB_HOST', ''); // 数据库地址
|
define('DB_HOST', ''); // 数据库地址
|
||||||
define('DB_NAME', ''); // 数据库名称
|
define('DB_NAME', ''); // 数据库名称
|
||||||
define('DB_USER', ''); // 数据库用户
|
define('DB_USER', ''); // 数据库用户
|
||||||
define('DB_PASS', ''); // 数据库密码
|
define('DB_PASS', ''); // 数据库密码
|
||||||
$DOMAINS_CONFIG = [
|
$DOMAINS_CONFIG = [
|
||||||
'Domain1' => [ // 你要托管的另一个子域名
|
'Domain1' => [], // Domain1: 你要托管的域名
|
||||||
'zone_id' => '', // 区域 ID,在 CF 的域名三个点就有
|
'Domain2' => [], // 以此类推
|
||||||
'api_token' => '' // API,在 CF 的账户栏有一个 API 配置,需要给 DNS 读写权限。
|
'Domain3' => []
|
||||||
],
|
|
||||||
'Domain2' => [ // 以此类推...
|
|
||||||
'zone_id' => '',
|
|
||||||
'api_token' => ''
|
|
||||||
],
|
|
||||||
'Domain3' => [
|
|
||||||
'zone_id' => '',
|
|
||||||
'api_token' => ''
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
define('SMTP_HOST', ''); // SMTP 地址
|
define('SMTP_HOST', ''); // SMTP 地址
|
||||||
define('SMTP_PORT', 587); // SMTP 端口
|
define('SMTP_PORT', 587); // SMTP 端口
|
||||||
@@ -51,8 +42,9 @@ define('SMTP_FROM_NAME', ''); // SMTP 发件人名称,可以使用你自己的
|
|||||||
define('SITE_URL', ''); // 分发站点 URL
|
define('SITE_URL', ''); // 分发站点 URL
|
||||||
define('SITE_NAME', ''); // 分发站点名称
|
define('SITE_NAME', ''); // 分发站点名称
|
||||||
```
|
```
|
||||||
|
2. 访问 `https://你的网站/install.php` 来导入数据库。
|
||||||
配置好后,在终端中输入以下命令安装 PHPMailer:
|
3. 访问 `https://你的网站/aliapi/setup_token.php` 来生成 Aliyun Token。
|
||||||
|
4. 在终端中输入以下命令安装 PHPMailer:
|
||||||
```sh
|
```sh
|
||||||
composer install
|
composer install
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -43,6 +43,27 @@ $currentTheme = getTheme();
|
|||||||
<title><?php echo $action === 'login' ? '登录' : '注册'; ?> - <?php echo SITE_NAME; ?></title>
|
<title><?php echo $action === 'login' ? '登录' : '注册'; ?> - <?php echo SITE_NAME; ?></title>
|
||||||
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<meta name="description" content="登录,Parlz Domains 账号,管理你的二级域名。">
|
||||||
|
<meta name="keywords" content="登录,用户登录,域名管理">
|
||||||
|
<link rel="icon" href="favicon.ico">
|
||||||
|
<meta name="robots" content="index, follow">
|
||||||
|
<link rel="alternate" hreflang="zh-CN" href="https://<?php echo $_SERVER['HTTP_HOST']; ?>/auth.php" />
|
||||||
|
<link rel="me" href="https://git.parlz.com/Xiaoxiaobai5724/parlz-domain">
|
||||||
|
<meta property="og:title" content="登录 - Parlz Domains">
|
||||||
|
<meta property="og:description" content="登录 Parlz Domains 账号,管理你的二级域名。">
|
||||||
|
<meta property="og:image" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/favicon.ico">
|
||||||
|
<meta property="og:image:width" content="256">
|
||||||
|
<meta property="og:image:height" content="256">
|
||||||
|
<meta property="og:url" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/auth.php">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:site_name" content="<?php echo SITE_NAME; ?>">
|
||||||
|
<meta property="og:locale" content="zh_CN">
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content="登录 - <?php echo SITE_NAME; ?>">
|
||||||
|
<meta name="twitter:description" content="登录 <?php echo SITE_NAME; ?> 账号。">
|
||||||
|
<meta name="twitter:image" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/favicon.ico">
|
||||||
|
<meta name="twitter:site" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/auth.php">
|
||||||
|
<link rel="canonical" href="https://<?php echo $_SERVER['HTTP_HOST']; ?>/auth.php">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container auth-container">
|
<div class="container auth-container">
|
||||||
@@ -75,6 +96,8 @@ $currentTheme = getTheme();
|
|||||||
</form>
|
</form>
|
||||||
<div class="auth-switch">
|
<div class="auth-switch">
|
||||||
还没有账号?<a href="?action=register">立即注册</a>
|
还没有账号?<a href="?action=register">立即注册</a>
|
||||||
|
<a href="forgot_password.php">忘记密码?</a>
|
||||||
|
注册用户默认已同意 <a href="/doc/?file=md/使用协议.md">使用协议</a> 和 <a href="/doc/?file=md/隐私政策.md">隐私政策</a>。
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
@@ -107,9 +130,21 @@ $currentTheme = getTheme();
|
|||||||
</form>
|
</form>
|
||||||
<div class="auth-switch">
|
<div class="auth-switch">
|
||||||
已有账号?<a href="?action=login">立即登录</a>
|
已有账号?<a href="?action=login">立即登录</a>
|
||||||
|
<a href="forgot_password.php">忘记密码?</a>
|
||||||
|
注册用户默认已同意 <a href="/doc/?file=md/使用协议.md">使用协议</a> 和 <a href="/doc/?file=md/隐私政策.md">隐私政策</a>。
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "WebPage",
|
||||||
|
"name": "登录 - <?php echo SITE_NAME; ?>",
|
||||||
|
"description": "登录 <?php echo SITE_NAME; ?> 账号,管理你的二级域名。",
|
||||||
|
"url": "https://<?php echo $_SERVER['HTTP_HOST']; ?>/login",
|
||||||
|
"inLanguage": "zh-CN"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+26
-17
@@ -1,16 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
define('DB_HOST', '');
|
define('DB_HOST', ''); // 数据库地址
|
||||||
define('DB_NAME', '');
|
define('DB_NAME', ''); // 数据库名称
|
||||||
define('DB_USER', '');
|
define('DB_USER', ''); // 数据库用户
|
||||||
define('DB_PASS', '');
|
define('DB_PASS', ''); // 数据库密码
|
||||||
define('DB_CHARSET', 'utf8mb4');
|
define('DB_CHARSET', 'utf8mb4');
|
||||||
|
|
||||||
|
/**
|
||||||
$DOMAINS_CONFIG = [
|
$DOMAINS_CONFIG = [
|
||||||
'Domain1' => [
|
'Domain1' => [ // 你要托管的另一个子域名
|
||||||
'zone_id' => '',
|
'zone_id' => '', // 区域 ID,在 CF 的域名三个点就有
|
||||||
'api_token' => ''
|
'api_token' => '' // API,在 CF 的账户栏有一个 API 配置,需要给 DNS 读写权限。
|
||||||
],
|
],
|
||||||
'Domain2' => [
|
'Domain2' => [ // 以此类推...
|
||||||
'zone_id' => '',
|
'zone_id' => '',
|
||||||
'api_token' => ''
|
'api_token' => ''
|
||||||
],
|
],
|
||||||
@@ -20,15 +21,23 @@ $DOMAINS_CONFIG = [
|
|||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
define('SMTP_HOST', '');
|
*/
|
||||||
define('SMTP_PORT', 587);
|
|
||||||
define('SMTP_SECURE', 'tls');
|
$DOMAINS_CONFIG = [
|
||||||
define('SMTP_USERNAME', '');
|
'Domain1' => [],
|
||||||
define('SMTP_PASSWORD', '');
|
'Domain2' => [],
|
||||||
define('SMTP_FROM_EMAIL', '');
|
'Domain3' => []
|
||||||
define('SMTP_FROM_NAME', '');
|
];
|
||||||
define('SITE_URL', '');
|
|
||||||
define('SITE_NAME', '');
|
define('SMTP_HOST', ''); // SMTP 地址
|
||||||
|
define('SMTP_PORT', 587); // SMTP 端口
|
||||||
|
define('SMTP_SECURE', 'tls'); // SMTP 协议,ssl 加密,tls 不加密
|
||||||
|
define('SMTP_USERNAME', ''); // SMTP 用户名
|
||||||
|
define('SMTP_PASSWORD', ''); // SMTP 授权码
|
||||||
|
define('SMTP_FROM_EMAIL', ''); // SMTP 发件邮箱,和用户名保持一致即可
|
||||||
|
define('SMTP_FROM_NAME', ''); // SMTP 发件人名称,可以使用你自己的站点名字
|
||||||
|
define('SITE_URL', ''); // 分发站点 URL
|
||||||
|
define('SITE_NAME', ''); // 分发站点名称
|
||||||
define('CREDIT_PER_DOMAIN', 5);
|
define('CREDIT_PER_DOMAIN', 5);
|
||||||
define('MAX_DOMAINS_PER_USER', 10);
|
define('MAX_DOMAINS_PER_USER', 10);
|
||||||
define('SIGN_IN_CREDIT', 2);
|
define('SIGN_IN_CREDIT', 2);
|
||||||
|
|||||||
+71
-2
@@ -82,6 +82,26 @@ $currentTheme = getTheme();
|
|||||||
<title>用户面板 - <?php echo SITE_NAME; ?></title>
|
<title>用户面板 - <?php echo SITE_NAME; ?></title>
|
||||||
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<meta name="description" content="<?php echo SITE_NAME; ?> 管理你的域名,查看积分,签到获取奖励。">
|
||||||
|
<meta name="keywords" content="parlz,parlzdomains,域名管理,我的域名,积分查询">
|
||||||
|
<link rel="icon" href="favicon.ico">
|
||||||
|
<link rel="alternate" hreflang="zh-CN" href="https://<?php echo $_SERVER['HTTP_HOST']; ?>/dashboard" />
|
||||||
|
<meta property="og:title" content="用户面板 - <?php echo SITE_NAME; ?>">
|
||||||
|
<meta property="og:description" content="自研域名分发网站,免费获取二级域名,支持 A、AAAA、CNAME、MX、NS、TXT 等记录。积分制管理,每日签到得积分,邀请好友额外奖励。基于阿里云DNS,稳定可靠。">
|
||||||
|
<meta property="og:image" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/favicon.ico">
|
||||||
|
<meta property="og:image:width" content="256">
|
||||||
|
<meta property="og:image:height" content="256">
|
||||||
|
<meta property="og:url" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:site_name" content="<?php echo SITE_NAME; ?>">
|
||||||
|
<meta property="og:locale" content="zh_CN">
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content="用户面板 - <?php echo SITE_NAME; ?>">
|
||||||
|
<meta name="twitter:description" content="自研域名分发网站,免费获取二级域名,支持 A、AAAA、CNAME、MX、NS、TXT 等记录。积分制管理,每日签到得积分,邀请好友额外奖励。基于阿里云DNS,稳定可靠。">
|
||||||
|
<meta name="twitter:image" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/favicon.ico">
|
||||||
|
<meta name="twitter:site" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/">
|
||||||
|
<link rel="canonical" href="https://<?php echo $_SERVER['HTTP_HOST']; ?>/dashboard">
|
||||||
|
<link rel="me" href="https://git.parlz.com/Xiaoxiaobai5724/parlz-domain">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -156,7 +176,40 @@ $currentTheme = getTheme();
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 欢迎区域 -->
|
||||||
|
<div class="welcome-header">
|
||||||
|
<div>
|
||||||
|
<h1>👋 欢迎回来,<?php echo htmlspecialchars($userInfo['username']); ?>!</h1>
|
||||||
|
<p style="color:var(--text-secondary);font-size:14px;">
|
||||||
|
您在 <?php echo date('Y-m-d', strtotime($userInfo['created_at'])); ?> 注册
|
||||||
|
<?php if ($userInfo['is_admin']): ?>
|
||||||
|
<span style="background:var(--accent-blue);color:#fff;padding:2px 12px;border-radius:4px;font-size:12px;margin-left:10px;">
|
||||||
|
<i class="fas fa-crown"></i> 管理员
|
||||||
|
</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="user-actions">
|
||||||
|
<form method="POST" style="display:inline;">
|
||||||
|
<input type="hidden" name="action" value="sign_in">
|
||||||
|
<button type="submit" class="btn btn-primary" <?php echo $signInToday ? 'disabled' : ''; ?>>
|
||||||
|
<i class="fas fa-calendar-check"></i>
|
||||||
|
<?php echo $signInToday ? '已签到' : '签到 (+' . SIGN_IN_CREDIT . '积分)'; ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-warning" style="margin-bottom:25px;border-left:4px solid var(--accent-red);background:rgba(248,81,73,0.08);">
|
||||||
|
<div style="display:flex;align-items:flex-start;gap:14px;">
|
||||||
|
<div style="font-size:28px;flex-shrink:0;">⚠️</div>
|
||||||
|
<div>
|
||||||
|
<div style="font-weight:700;font-size:16px;color:var(--accent-red);">📢 重要提醒:</div>
|
||||||
|
<div style="font-size:14px;color:var(--text-secondary);margin-top:4px;line-height:1.8;">
|
||||||
|
严禁使用本平台域名搭建色情、淫秽、低俗、赌博、博彩、诈骗、毒品、违禁品交易、传播病毒、木马、钓鱼、侵犯他人知识产权或隐私权的网站。一旦发现,立即封号并移交公安机关处理!如果您发现了,请发送邮件至 odoorbell@163.com。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- 统计卡片 -->
|
<!-- 统计卡片 -->
|
||||||
<div class="stats-grid">
|
<div class="stats-grid">
|
||||||
<div class="stat-card credits">
|
<div class="stat-card credits">
|
||||||
@@ -372,7 +425,7 @@ $currentTheme = getTheme();
|
|||||||
// 切换事件
|
// 切换事件
|
||||||
typeSelect.addEventListener('change', function() {
|
typeSelect.addEventListener('change', function() {
|
||||||
const type = this.value;
|
const type = this.value;
|
||||||
hintSpan.innerHTML = hints[type] || '请输入记录值'。;
|
hintSpan.innerHTML = hints[type] || '请输入记录值。';
|
||||||
valueInput.placeholder = placeholders[type] || '请输入记录值。';
|
valueInput.placeholder = placeholders[type] || '请输入记录值。';
|
||||||
|
|
||||||
// 添加小字提示
|
// 添加小字提示
|
||||||
@@ -409,5 +462,21 @@ $currentTheme = getTheme();
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "ProfilePage",
|
||||||
|
"name": "<?php echo htmlspecialchars($userInfo['username']); ?> 的域名管理面板",
|
||||||
|
"description": "管理你的二级域名,当前拥有 <?php echo $domainCount; ?> 个域名,<?php echo $credits; ?> 积分。",
|
||||||
|
"url": "https://<?php echo $_SERVER['HTTP_HOST']; ?>/dashboard",
|
||||||
|
"about": {
|
||||||
|
"@type": "Person",
|
||||||
|
"name": "<?php echo htmlspecialchars($userInfo['username']); ?>",
|
||||||
|
"description": "<?php echo SITE_NAME; ?> 用户"
|
||||||
|
},
|
||||||
|
"dateCreated": "<?php echo date('Y-m-d', strtotime($userInfo['created_at'])); ?>",
|
||||||
|
"inLanguage": "zh-CN"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'functions.php';
|
||||||
|
|
||||||
|
$currentTheme = getTheme();
|
||||||
|
$message = null;
|
||||||
|
$success = false;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$email = trim($_POST['email'] ?? '');
|
||||||
|
|
||||||
|
if (empty($email)) {
|
||||||
|
$message = '请输入邮箱地址';
|
||||||
|
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$message = '请输入有效的邮箱地址';
|
||||||
|
} else {
|
||||||
|
$result = requestPasswordReset($email);
|
||||||
|
if ($result['success']) {
|
||||||
|
$success = true;
|
||||||
|
$message = $result['message'];
|
||||||
|
} else {
|
||||||
|
$message = $result['message'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pageTitle = '忘记密码 - ' . SITE_NAME;
|
||||||
|
$pageDescription = '重置您的 ' . SITE_NAME . ' 账号密码,通过邮箱验证找回账号。';
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN" data-theme="<?php echo $currentTheme; ?>">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><?php echo htmlspecialchars($pageTitle); ?></title>
|
||||||
|
<meta name="description" content="<?php echo htmlspecialchars($pageDescription); ?>">
|
||||||
|
<link rel="canonical" href="https://<?php echo $_SERVER['HTTP_HOST']; ?>/forgot_password">
|
||||||
|
<meta name="robots" content="index, follow">
|
||||||
|
<link rel="alternate" hreflang="zh-CN" href="https://<?php echo $_SERVER['HTTP_HOST']; ?>/forgot_password.php" />
|
||||||
|
<link rel="icon" href="favicon.ico">
|
||||||
|
<meta property="og:title" content="<?php echo htmlspecialchars($pageTitle); ?>">
|
||||||
|
<meta property="og:description" content="<?php echo htmlspecialchars($pageDescription); ?>">
|
||||||
|
<meta property="og:image" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/favicon.ico">
|
||||||
|
<meta property="og:image:width" content="256">
|
||||||
|
<meta property="og:image:height" content="256">
|
||||||
|
<meta property="og:url" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/forgot_password.php">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:site_name" content="<?php echo SITE_NAME; ?>">
|
||||||
|
<meta property="og:locale" content="zh_CN">
|
||||||
|
|
||||||
|
<!-- ===== Twitter Card ===== -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content="<?php echo htmlspecialchars($pageTitle); ?>">
|
||||||
|
<meta name="twitter:description" content="<?php echo htmlspecialchars($pageDescription); ?>">
|
||||||
|
<meta name="twitter:image" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/favicon.ico">
|
||||||
|
<meta name="twitter:site" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>">
|
||||||
|
<!-- ===== Schema.org ===== -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "WebPage",
|
||||||
|
"name": "<?php echo htmlspecialchars($pageTitle); ?>",
|
||||||
|
"description": "<?php echo htmlspecialchars($pageDescription); ?>",
|
||||||
|
"url": "https://<?php echo $_SERVER['HTTP_HOST']; ?>/forgot-password",
|
||||||
|
"inLanguage": "zh-CN"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="auth-container">
|
||||||
|
<div class="auth-card">
|
||||||
|
<div class="icon-top">
|
||||||
|
<i class="fas fa-key"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>忘记密码</h2>
|
||||||
|
<p class="subtitle">输入您的邮箱,我们将发送重置链接</p>
|
||||||
|
|
||||||
|
<?php if ($message): ?>
|
||||||
|
<div class="alert alert-<?php echo $success ? 'success' : 'error'; ?>">
|
||||||
|
<i class="fas <?php echo $success ? 'fa-check-circle' : 'fa-exclamation-circle'; ?>"></i>
|
||||||
|
<?php echo htmlspecialchars($message); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($success): ?>
|
||||||
|
<div class="success-box">
|
||||||
|
<i class="fas fa-envelope"></i>
|
||||||
|
<h3>📧 邮件已发送</h3>
|
||||||
|
<p>请检查您的邮箱,点击邮件中的链接重置密码。<br>如果没收到邮件,请检查垃圾箱。</p>
|
||||||
|
<a href="/login" class="btn btn-primary" style="display:inline-flex;width:auto;padding:12px 32px;">
|
||||||
|
<i class="fas fa-sign-in-alt"></i> 返回登录
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<form method="POST">
|
||||||
|
<div class="form-group">
|
||||||
|
<label><i class="fas fa-envelope"></i> 邮箱地址</label>
|
||||||
|
<input type="email" name="email" placeholder="请输入注册邮箱" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="fas fa-paper-plane"></i> 发送重置链接
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="auth-links">
|
||||||
|
想起密码了?<a href="/login">返回登录</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+427
-272
@@ -5,6 +5,100 @@ require_once 'vendor/autoload.php'; // PHPMailer
|
|||||||
use PHPMailer\PHPMailer\PHPMailer;
|
use PHPMailer\PHPMailer\PHPMailer;
|
||||||
use PHPMailer\PHPMailer\Exception;
|
use PHPMailer\PHPMailer\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出 Canonical 标签
|
||||||
|
*/
|
||||||
|
function renderCanonical($customUrl = null) {
|
||||||
|
if ($customUrl) {
|
||||||
|
$url = $customUrl;
|
||||||
|
} else {
|
||||||
|
$scheme = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
|
||||||
|
$host = $_SERVER['HTTP_HOST'];
|
||||||
|
$uri = $_SERVER['REQUEST_URI'];
|
||||||
|
|
||||||
|
// 移除查询参数中的无关参数(如 session、utm 等)
|
||||||
|
$parsed = parse_url($uri);
|
||||||
|
if (isset($parsed['query'])) {
|
||||||
|
parse_str($parsed['query'], $query);
|
||||||
|
// 移除无关参数
|
||||||
|
unset($query['_ajax'], $query['t'], $query['sid']);
|
||||||
|
$newQuery = http_build_query($query);
|
||||||
|
$url = $scheme . '://' . $host . $parsed['path'] . ($newQuery ? '?' . $newQuery : '');
|
||||||
|
} else {
|
||||||
|
$url = $scheme . '://' . $host . $uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<link rel="canonical" href="' . htmlspecialchars($url) . '">' . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出 Schema.org 结构化数据(网站和组织)
|
||||||
|
*/
|
||||||
|
function renderSchemaOrg($pageType = 'WebPage', $extraData = []) {
|
||||||
|
$schema = [
|
||||||
|
'@context' => 'https://schema.org',
|
||||||
|
'@type' => $pageType,
|
||||||
|
'name' => SITE_NAME,
|
||||||
|
'description' => '免费二级域名分发平台,支持A、AAAA、CNAME、MX、NS、TXT、SRV记录',
|
||||||
|
'url' => 'https://' . $_SERVER['HTTP_HOST'],
|
||||||
|
'inLanguage' => 'zh-CN',
|
||||||
|
'about' => [
|
||||||
|
'@type' => 'Thing',
|
||||||
|
'name' => '域名分发'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// 根据页面类型添加额外数据
|
||||||
|
if ($pageType === 'WebPage') {
|
||||||
|
$schema['dateModified'] = date('Y-m-d\TH:i:sP');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pageType === 'Product') {
|
||||||
|
$schema['offers'] = [
|
||||||
|
'@type' => 'Offer',
|
||||||
|
'price' => '0',
|
||||||
|
'priceCurrency' => 'CNY',
|
||||||
|
'availability' => 'https://schema.org/InStock'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并额外数据
|
||||||
|
if (!empty($extraData)) {
|
||||||
|
$schema = array_merge($schema, $extraData);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<script type="application/ld+json">' . "\n";
|
||||||
|
echo json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||||
|
echo "\n" . '</script>' . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出面包屑结构化数据
|
||||||
|
*/
|
||||||
|
function renderBreadcrumbSchema($items) {
|
||||||
|
$schema = [
|
||||||
|
'@context' => 'https://schema.org',
|
||||||
|
'@type' => 'BreadcrumbList',
|
||||||
|
'itemListElement' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
$position = 1;
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$schema['itemListElement'][] = [
|
||||||
|
'@type' => 'ListItem',
|
||||||
|
'position' => $position,
|
||||||
|
'name' => $item['name'],
|
||||||
|
'item' => $item['url']
|
||||||
|
];
|
||||||
|
$position++;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<script type="application/ld+json">' . "\n";
|
||||||
|
echo json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||||
|
echo "\n" . '</script>' . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
// ===== 用户相关函数 =====
|
// ===== 用户相关函数 =====
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -221,6 +315,148 @@ function verifyEmail($token) {
|
|||||||
return ['success' => true, 'message' => '邮箱验证成功!'];
|
return ['success' => true, 'message' => '邮箱验证成功!'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求密码重置
|
||||||
|
*/
|
||||||
|
function requestPasswordReset($email) {
|
||||||
|
$pdo = getDB();
|
||||||
|
|
||||||
|
// 检查用户是否存在
|
||||||
|
$stmt = $pdo->prepare("SELECT id, username FROM users WHERE email = ? AND is_verified = 1");
|
||||||
|
$stmt->execute([$email]);
|
||||||
|
$user = $stmt->fetch();
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
// 为了安全,不暴露用户是否存在
|
||||||
|
return ['success' => true, 'message' => '如果该邮箱已注册,我们将发送重置链接'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成重置令牌
|
||||||
|
$token = bin2hex(random_bytes(32));
|
||||||
|
$expires = date('Y-m-d H:i:s', strtotime('+1 hour'));
|
||||||
|
|
||||||
|
// 保存令牌
|
||||||
|
$stmt = $pdo->prepare("UPDATE users SET reset_token = ?, reset_expires = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$token, $expires, $user['id']]);
|
||||||
|
|
||||||
|
// 发送重置邮件
|
||||||
|
sendResetEmail($email, $user['username'], $token);
|
||||||
|
|
||||||
|
return ['success' => true, 'message' => '如果该邮箱已注册,我们将发送重置链接'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置密码
|
||||||
|
*/
|
||||||
|
function resetPassword($token, $newPassword) {
|
||||||
|
$pdo = getDB();
|
||||||
|
|
||||||
|
// 验证令牌
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM users WHERE reset_token = ? AND reset_expires > NOW()");
|
||||||
|
$stmt->execute([$token]);
|
||||||
|
$user = $stmt->fetch();
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
return ['success' => false, 'message' => '重置链接无效或已过期'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新密码
|
||||||
|
$hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT);
|
||||||
|
$stmt = $pdo->prepare("UPDATE users SET password = ?, reset_token = NULL, reset_expires = NULL WHERE id = ?");
|
||||||
|
$stmt->execute([$hashedPassword, $user['id']]);
|
||||||
|
|
||||||
|
return ['success' => true, 'message' => '密码已重置,请使用新密码登录'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送重置密码邮件
|
||||||
|
*/
|
||||||
|
function sendResetEmail($email, $username, $token) {
|
||||||
|
$mail = new PHPMailer(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$mail->isSMTP();
|
||||||
|
$mail->Host = SMTP_HOST;
|
||||||
|
$mail->SMTPAuth = true;
|
||||||
|
$mail->Username = SMTP_USERNAME;
|
||||||
|
$mail->Password = SMTP_PASSWORD;
|
||||||
|
$mail->SMTPSecure = SMTP_SECURE;
|
||||||
|
$mail->Port = SMTP_PORT;
|
||||||
|
|
||||||
|
$mail->CharSet = 'UTF-8';
|
||||||
|
$mail->Encoding = 'base64';
|
||||||
|
|
||||||
|
$mail->setFrom(SMTP_FROM_EMAIL, SMTP_FROM_NAME);
|
||||||
|
$mail->addAddress($email, $username);
|
||||||
|
$mail->isHTML(true);
|
||||||
|
|
||||||
|
$resetLink = SITE_URL . "/reset-password?token=" . $token;
|
||||||
|
$mail->Subject = '重置密码 - ' . SITE_NAME;
|
||||||
|
|
||||||
|
$mail->Body = '
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>重置密码</title>
|
||||||
|
<style>
|
||||||
|
@import url("https://cdn.jsdmirror.com/gh/willow-god/Sharding-fonts/Yozai-Medium/result.min.css");
|
||||||
|
body { font-family: "Yozai Medium"; background: #f6f8fa; padding: 40px 20px; }
|
||||||
|
.container { max-width: 560px; margin: 0 auto; background: #ffffff; border-radius: 12px; padding: 40px; box-shadow: 0 8px 32px rgba(0,0,0,0.08); }
|
||||||
|
.header { text-align: center; margin-bottom: 30px; }
|
||||||
|
.header h1 { color: #24292f; font-size: 24px; margin: 0; }
|
||||||
|
.content { color: #24292f; line-height: 1.8; }
|
||||||
|
.btn { display: inline-block; padding: 12px 32px; background: #0969da; color: #ffffff !important; text-decoration: none; border-radius: 6px; font-weight: 600; margin: 20px 0; }
|
||||||
|
.btn:hover { background: #0550ae; }
|
||||||
|
.footer { margin-top: 30px; padding-top: 20px; border-top: 1px solid #d0d7de; font-size: 12px; color: #57606a; text-align: center; }
|
||||||
|
.link-box { background: #f6f8fa; padding: 12px 16px; border-radius: 6px; word-break: break-all; font-size: 13px; color: #57606a; border: 1px solid #d0d7de; }
|
||||||
|
.highlight { color: #0969da; font-weight: 600; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>🔑 重置密码</h1>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>您好,<strong>' . htmlspecialchars($username) . '</strong>:</p>
|
||||||
|
<p>我们收到了您重置密码的请求。请点击下方按钮设置新密码:</p>
|
||||||
|
<p style="text-align: center;">
|
||||||
|
<a href="' . $resetLink . '" class="btn">🔐 重置密码</a>
|
||||||
|
</p>
|
||||||
|
<p style="color: #57606a; font-size: 14px;">如果按钮无法点击,请复制以下链接到浏览器打开:</p>
|
||||||
|
<div class="link-box">' . $resetLink . '</div>
|
||||||
|
<p style="margin-top: 20px; font-size: 14px; color: #57606a;">
|
||||||
|
⏰ 此链接 <span class="highlight">1 小时</span> 内有效。<br>
|
||||||
|
🔒 如果您没有请求重置密码,请忽略此邮件。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<p>' . SITE_NAME . ' · 安全可靠</p>
|
||||||
|
<p style="margin-top: 4px;">© ' . date('Y') . ' ' . SITE_NAME . '</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
';
|
||||||
|
|
||||||
|
$mail->AltBody = "重置密码 - " . SITE_NAME . "\n\n" .
|
||||||
|
"您好," . $username . ":\n\n" .
|
||||||
|
"请访问以下链接重置您的密码:\n" .
|
||||||
|
$resetLink . "\n\n" .
|
||||||
|
"此链接 1 小时内有效。\n\n" .
|
||||||
|
"如果您没有请求重置密码,请忽略此邮件。";
|
||||||
|
|
||||||
|
$mail->send();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log("重置邮件发送失败: " . $e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ===== 积分相关函数 =====
|
// ===== 积分相关函数 =====
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -517,8 +753,152 @@ function getRecordTypeHint($type) {
|
|||||||
return $hints[$type] ?? '';
|
return $hints[$type] ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== 阿里云 API 函数(通过你的 aliapi 接口) =====
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加域名(扣除积分)
|
* 调用阿里云API的通用函数
|
||||||
|
*/
|
||||||
|
function callAliyunAPI($endpoint, $params) {
|
||||||
|
$tokenFile = __DIR__ . '/aliapi/.token';
|
||||||
|
$token = trim(file_get_contents($tokenFile));
|
||||||
|
$params['token'] = $token;
|
||||||
|
$url = "https://" . $_SERVER['HTTP_HOST'] . "/aliapi/api/" . $endpoint . ".php";
|
||||||
|
$fullUrl = $url . '?' . http_build_query($params);
|
||||||
|
|
||||||
|
error_log("阿里云API请求: " . $fullUrl);
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $fullUrl);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$curlError = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($curlError) {
|
||||||
|
error_log("阿里云 API CURL 错误: " . $curlError);
|
||||||
|
return ['success' => false, 'message' => 'CURL 错误: ' . $curlError];
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = json_decode($response, true);
|
||||||
|
if (!$result) {
|
||||||
|
error_log("阿里云 API 响应解析失败: " . $response);
|
||||||
|
return ['success' => false, 'message' => '响应解析失败。'];
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("阿里云 API 响应: " . $response);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加DNS记录(调用阿里云API)
|
||||||
|
*/
|
||||||
|
function addAliyunRecord($mainDomain, $subdomain, $type, $value) {
|
||||||
|
$params = [
|
||||||
|
'domain_name' => $mainDomain,
|
||||||
|
'rr' => $subdomain,
|
||||||
|
'type' => strtoupper($type),
|
||||||
|
'value' => $value,
|
||||||
|
'ttl' => 600
|
||||||
|
];
|
||||||
|
|
||||||
|
// MX和SRV记录需要priority参数
|
||||||
|
$typeUpper = strtoupper($type);
|
||||||
|
if ($typeUpper === 'MX' || $typeUpper === 'SRV') {
|
||||||
|
$parts = preg_split('/\s+/', trim($value));
|
||||||
|
if (count($parts) >= 1 && is_numeric($parts[0])) {
|
||||||
|
$params['priority'] = intval($parts[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = callAliyunAPI('add_record', $params);
|
||||||
|
|
||||||
|
if ($result && isset($result['code']) && $result['code'] === 200) {
|
||||||
|
return ['success' => true, 'message' => '添加成功', 'data' => $result['data'] ?? []];
|
||||||
|
} else {
|
||||||
|
$errorMsg = $result['message'] ?? '未知错误';
|
||||||
|
return ['success' => false, 'message' => '添加失败: ' . $errorMsg];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除DNS记录(调用阿里云API)
|
||||||
|
*/
|
||||||
|
function deleteAliyunRecord($mainDomain, $subdomain) {
|
||||||
|
// 1. 先查询记录ID
|
||||||
|
$listParams = [
|
||||||
|
'domain_name' => $mainDomain,
|
||||||
|
'rr_keyword' => $subdomain
|
||||||
|
];
|
||||||
|
|
||||||
|
$listResult = callAliyunAPI('list_records', $listParams);
|
||||||
|
|
||||||
|
if (!$listResult || $listResult['code'] !== 200) {
|
||||||
|
error_log("查询记录失败: " . ($listResult['message'] ?? '未知错误'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找匹配的记录ID
|
||||||
|
$recordId = null;
|
||||||
|
if (isset($listResult['data']['records'])) {
|
||||||
|
foreach ($listResult['data']['records'] as $record) {
|
||||||
|
if ($record['rr'] === $subdomain) {
|
||||||
|
$recordId = $record['record_id'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$recordId) {
|
||||||
|
error_log("未找到记录: {$subdomain}.{$mainDomain}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 删除记录
|
||||||
|
$deleteParams = ['record_id' => $recordId];
|
||||||
|
$deleteResult = callAliyunAPI('delete_record', $deleteParams);
|
||||||
|
|
||||||
|
if ($deleteResult && isset($deleteResult['code']) && $deleteResult['code'] === 200) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("删除记录失败: " . ($deleteResult['message'] ?? '未知错误'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询DNS记录列表(调用阿里云API)
|
||||||
|
*/
|
||||||
|
function listAliyunRecords($mainDomain, $rrKeyword = null, $type = null) {
|
||||||
|
$params = [
|
||||||
|
'domain_name' => $mainDomain,
|
||||||
|
'page' => 1,
|
||||||
|
'page_size' => 100
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($rrKeyword !== null) {
|
||||||
|
$params['rr_keyword'] = $rrKeyword;
|
||||||
|
}
|
||||||
|
if ($type !== null) {
|
||||||
|
$params['type'] = strtoupper($type);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = callAliyunAPI('list_records', $params);
|
||||||
|
|
||||||
|
if ($result && isset($result['code']) && $result['code'] === 200) {
|
||||||
|
return ['success' => true, 'data' => $result['data'] ?? []];
|
||||||
|
} else {
|
||||||
|
return ['success' => false, 'message' => $result['message'] ?? '查询失败'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加域名(扣除积分)- 使用阿里云API
|
||||||
*/
|
*/
|
||||||
function addDomain($userId, $mainDomain, $subdomain, $type, $value) {
|
function addDomain($userId, $mainDomain, $subdomain, $type, $value) {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
@@ -557,10 +937,10 @@ function addDomain($userId, $mainDomain, $subdomain, $type, $value) {
|
|||||||
$pdo->beginTransaction();
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 先尝试添加到 Cloudflare
|
// 先尝试添加到阿里云
|
||||||
$cfResult = addCloudflareRecord($mainDomain, $subdomain, $type, $value);
|
$aliResult = addAliyunRecord($mainDomain, $subdomain, $type, $value);
|
||||||
if (!$cfResult['success']) {
|
if (!$aliResult['success']) {
|
||||||
return ['success' => false, 'message' => '添加失败: ' . $cfResult['message']];
|
return ['success' => false, 'message' => '添加失败: ' . $aliResult['message']];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 扣减积分
|
// 扣减积分
|
||||||
@@ -579,8 +959,8 @@ function addDomain($userId, $mainDomain, $subdomain, $type, $value) {
|
|||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$pdo->rollBack();
|
$pdo->rollBack();
|
||||||
// 如果数据库失败,尝试删除 Cloudflare 记录
|
// 如果数据库失败,尝试删除阿里云记录
|
||||||
deleteCloudflareRecord($mainDomain, $subdomain);
|
deleteAliyunRecord($mainDomain, $subdomain);
|
||||||
return ['success' => false, 'message' => '添加失败: ' . $e->getMessage()];
|
return ['success' => false, 'message' => '添加失败: ' . $e->getMessage()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -596,7 +976,7 @@ function getUserDomains($userId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除域名
|
* 删除域名 - 使用阿里云API
|
||||||
*/
|
*/
|
||||||
function deleteDomain($userId, $domainId) {
|
function deleteDomain($userId, $domainId) {
|
||||||
$pdo = getDB();
|
$pdo = getDB();
|
||||||
@@ -610,8 +990,11 @@ function deleteDomain($userId, $domainId) {
|
|||||||
return ['success' => false, 'message' => '该域名不存在。'];
|
return ['success' => false, 'message' => '该域名不存在。'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从 Cloudflare 删除
|
// 从阿里云删除
|
||||||
deleteCloudflareRecord($domain['main_domain'], $domain['subdomain']);
|
$deleted = deleteAliyunRecord($domain['main_domain'], $domain['subdomain']);
|
||||||
|
if (!$deleted) {
|
||||||
|
error_log("阿里云删除失败: " . $domain['subdomain'] . '.' . $domain['main_domain'] . ',但数据库将继续删除');
|
||||||
|
}
|
||||||
|
|
||||||
// 软删除
|
// 软删除
|
||||||
$stmt = $pdo->prepare("UPDATE domains SET status = 0 WHERE id = ?");
|
$stmt = $pdo->prepare("UPDATE domains SET status = 0 WHERE id = ?");
|
||||||
@@ -620,268 +1003,6 @@ function deleteDomain($userId, $domainId) {
|
|||||||
return ['success' => true, 'message' => '域名删除成功。'];
|
return ['success' => true, 'message' => '域名删除成功。'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Cloudflare API函数 =====
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取域名配置
|
|
||||||
*/
|
|
||||||
function getDomainConfig($mainDomain) {
|
|
||||||
global $DOMAINS_CONFIG;
|
|
||||||
return $DOMAINS_CONFIG[$mainDomain] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加Cloudflare记录(最终修复SRV - 使用正确的字段名)
|
|
||||||
*/
|
|
||||||
function addCloudflareRecord($mainDomain, $subdomain, $type, $value) {
|
|
||||||
$config = getDomainConfig($mainDomain);
|
|
||||||
if (!$config) {
|
|
||||||
error_log("Cloudflare 错误: 未找到域名配置 - {$mainDomain}");
|
|
||||||
return ['success' => false, 'message' => '未找到域名配置'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$fullDomain = strtolower($subdomain) . '.' . strtolower($mainDomain);
|
|
||||||
$type = strtoupper($type);
|
|
||||||
|
|
||||||
// 检查记录是否已存在
|
|
||||||
$ch = curl_init('https://api.cloudflare.com/client/v4/zones/' . $config['zone_id'] . '/dns_records?name=' . urlencode($fullDomain) . '&type=' . $type);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
||||||
'Content-Type: application/json',
|
|
||||||
'Authorization: Bearer ' . $config['api_token']
|
|
||||||
]);
|
|
||||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
if ($httpCode === 200) {
|
|
||||||
$result = json_decode($response, true);
|
|
||||||
if ($result && $result['success'] && !empty($result['result'])) {
|
|
||||||
error_log("Cloudflare 记录已存在 - {$fullDomain}");
|
|
||||||
return ['success' => false, 'message' => '该记录已存在。'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建请求数据 - 基础字段
|
|
||||||
$data = [
|
|
||||||
'type' => $type,
|
|
||||||
'name' => $fullDomain,
|
|
||||||
'ttl' => 300,
|
|
||||||
'proxied' => false // 必须明确设置为 false
|
|
||||||
];
|
|
||||||
|
|
||||||
// 根据记录类型处理值
|
|
||||||
switch ($type) {
|
|
||||||
case 'A':
|
|
||||||
if (!filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
|
||||||
return ['success' => false, 'message' => '无效的 IPv4 地址。'];
|
|
||||||
}
|
|
||||||
$data['content'] = $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'AAAA':
|
|
||||||
if (!filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
|
||||||
return ['success' => false, 'message' => '无效的 IPv6 地址。'];
|
|
||||||
}
|
|
||||||
$data['content'] = $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'CNAME':
|
|
||||||
$value = strtolower(trim($value));
|
|
||||||
if (!preg_match('/^([a-z0-9\-]+\.)+[a-z]{2,}$/', $value)) {
|
|
||||||
return ['success' => false, 'message' => '无效的域名格式。'];
|
|
||||||
}
|
|
||||||
$data['content'] = $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'MX':
|
|
||||||
$parts = preg_split('/\s+/', trim($value));
|
|
||||||
if (count($parts) != 2) {
|
|
||||||
return ['success' => false, 'message' => 'MX 记录格式: 优先级 域名 (例如: 10 mail.example.com)。'];
|
|
||||||
}
|
|
||||||
if (!is_numeric($parts[0]) || intval($parts[0]) < 0 || intval($parts[0]) > 65535) {
|
|
||||||
return ['success' => false, 'message' => '优先级必须是 0-65535 之间的数字。'];
|
|
||||||
}
|
|
||||||
$domain = strtolower(trim($parts[1]));
|
|
||||||
if (!preg_match('/^([a-z0-9\-]+\.)+[a-z]{2,}$/', $domain)) {
|
|
||||||
return ['success' => false, 'message' => '无效的邮件服务器域名。'];
|
|
||||||
}
|
|
||||||
$data['priority'] = intval($parts[0]);
|
|
||||||
$data['content'] = $domain;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'NS':
|
|
||||||
$value = strtolower(trim($value));
|
|
||||||
if (!preg_match('/^([a-z0-9\-]+\.)+[a-z]{2,}$/', $value)) {
|
|
||||||
return ['success' => false, 'message' => '无效的域名服务器地址。'];
|
|
||||||
}
|
|
||||||
$data['content'] = $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'TXT':
|
|
||||||
if (strlen($value) > 255) {
|
|
||||||
return ['success' => false, 'message' => 'TXT 记录值不能超过 255 字符。'];
|
|
||||||
}
|
|
||||||
$data['content'] = $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'SRV':
|
|
||||||
// ===== SRV记录特殊处理 =====
|
|
||||||
$parts = preg_split('/\s+/', trim($value));
|
|
||||||
if (count($parts) != 4) {
|
|
||||||
return ['success' => false, 'message' => 'SRV 记录格式: 优先级 权重 端口 目标域名 (例如: 10 5 5060 sip.example.com)'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$priority = intval($parts[0]);
|
|
||||||
$weight = intval($parts[1]);
|
|
||||||
$port = intval($parts[2]);
|
|
||||||
$target = strtolower(trim($parts[3]));
|
|
||||||
|
|
||||||
// 验证数值范围
|
|
||||||
if ($priority < 0 || $priority > 65535) {
|
|
||||||
return ['success' => false, 'message' => '优先级必须是 0-65535 之间的数字。'];
|
|
||||||
}
|
|
||||||
if ($weight < 0 || $weight > 65535) {
|
|
||||||
return ['success' => false, 'message' => '权重必须是 0-65535 之间的数字。'];
|
|
||||||
}
|
|
||||||
if ($port < 0 || $port > 65535) {
|
|
||||||
return ['success' => false, 'message' => '端口必须是 0-65535 之间的数字。'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证目标域名
|
|
||||||
if ($target !== '.' && !preg_match('/^([a-z0-9\-]+\.)+[a-z]{2,}$/', $target)) {
|
|
||||||
return ['success' => false, 'message' => '无效的目标域名,请输入有效的域名或用 . 表示空。'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== 重要:SRV 记录使用不同的字段名 =====
|
|
||||||
// Cloudflare SRV API 要求使用这些字段名:
|
|
||||||
// - priority, weight, port, target (不是 content!)
|
|
||||||
$data['priority'] = (int)$priority;
|
|
||||||
$data['weight'] = (int)$weight;
|
|
||||||
$data['port'] = (int)$port;
|
|
||||||
$data['target'] = (string)$target; // 关键:使用 target 而不是 content
|
|
||||||
$data['proxied'] = false;
|
|
||||||
|
|
||||||
error_log("SRV 数据: priority={$priority}, weight={$weight}, port={$port}, target={$target}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return ['success' => false, 'message' => '不支持的记录类型'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录请求数据用于调试
|
|
||||||
error_log("Cloudflare 请求数据: " . json_encode($data));
|
|
||||||
|
|
||||||
$ch = curl_init('https://api.cloudflare.com/client/v4/zones/' . $config['zone_id'] . '/dns_records');
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_POST, true);
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
||||||
'Content-Type: application/json',
|
|
||||||
'Authorization: Bearer ' . $config['api_token']
|
|
||||||
]);
|
|
||||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
$curlError = curl_error($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
// 记录响应
|
|
||||||
error_log("Cloudflare 响应: HTTP {$httpCode}");
|
|
||||||
error_log("Cloudflare 响应内容: " . $response);
|
|
||||||
|
|
||||||
if ($curlError) {
|
|
||||||
error_log("Cloudflare CURL 错误: " . $curlError);
|
|
||||||
return ['success' => false, 'message' => 'CURL 错误: ' . $curlError];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($httpCode === 200) {
|
|
||||||
$result = json_decode($response, true);
|
|
||||||
if ($result && $result['success']) {
|
|
||||||
error_log("Cloudflare 添加成功: {$fullDomain}");
|
|
||||||
return ['success' => true, 'message' => '添加成功'];
|
|
||||||
} else {
|
|
||||||
$errorMsg = '未知错误';
|
|
||||||
if (isset($result['errors']) && !empty($result['errors'])) {
|
|
||||||
$errorMsg = $result['errors'][0]['message'] ?? 'API错误';
|
|
||||||
if (isset($result['errors'][0]['error_chain'])) {
|
|
||||||
foreach ($result['errors'][0]['error_chain'] as $chain) {
|
|
||||||
if (isset($chain['message'])) {
|
|
||||||
$errorMsg .= ' - ' . $chain['message'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
error_log("Cloudflare API 错误: " . $errorMsg);
|
|
||||||
return ['success' => false, 'message' => 'Cloudflare 错误: ' . $errorMsg];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析错误响应
|
|
||||||
$result = json_decode($response, true);
|
|
||||||
if ($result && isset($result['errors'])) {
|
|
||||||
$errorMsg = '';
|
|
||||||
foreach ($result['errors'] as $error) {
|
|
||||||
$errorMsg .= $error['message'] . ' ';
|
|
||||||
if (isset($error['error_chain'])) {
|
|
||||||
foreach ($error['error_chain'] as $chain) {
|
|
||||||
if (isset($chain['message'])) {
|
|
||||||
$errorMsg .= '- ' . $chain['message'] . ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ['success' => false, 'message' => '请求错误: ' . trim($errorMsg)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return ['success' => false, 'message' => "HTTP错误: {$httpCode} - " . substr($response, 0, 200)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除Cloudflare记录(支持SRV)
|
|
||||||
*/
|
|
||||||
function deleteCloudflareRecord($mainDomain, $subdomain) {
|
|
||||||
$config = getDomainConfig($mainDomain);
|
|
||||||
if (!$config) return false;
|
|
||||||
|
|
||||||
$fullDomain = $subdomain . '.' . $mainDomain;
|
|
||||||
$zoneId = $config['zone_id'];
|
|
||||||
$token = $config['api_token'];
|
|
||||||
|
|
||||||
// 查询记录ID - 不限定类型,因为SRV记录可能用target字段
|
|
||||||
$ch = curl_init('https://api.cloudflare.com/client/v4/zones/' . $zoneId . '/dns_records?name=' . urlencode($fullDomain));
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
||||||
'Content-Type: application/json',
|
|
||||||
'Authorization: Bearer ' . $token
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
$result = json_decode($response, true);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
if (isset($result['result']) && !empty($result['result'])) {
|
|
||||||
$recordId = $result['result'][0]['id'];
|
|
||||||
|
|
||||||
$ch = curl_init('https://api.cloudflare.com/client/v4/zones/' . $zoneId . '/dns_records/' . $recordId);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
||||||
'Content-Type: application/json',
|
|
||||||
'Authorization: Bearer ' . $token
|
|
||||||
]);
|
|
||||||
|
|
||||||
curl_exec($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== 其他辅助函数 =====
|
// ===== 其他辅助函数 =====
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -959,4 +1080,38 @@ function getMainDomains() {
|
|||||||
global $DOMAINS_CONFIG;
|
global $DOMAINS_CONFIG;
|
||||||
return array_keys($DOMAINS_CONFIG);
|
return array_keys($DOMAINS_CONFIG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 兼容旧代码 - 获取所有域名(管理员使用)
|
||||||
|
*/
|
||||||
|
function getDomains($mainDomain = null) {
|
||||||
|
$pdo = getDB();
|
||||||
|
|
||||||
|
$sql = "SELECT d.*, u.username FROM domains d
|
||||||
|
LEFT JOIN users u ON d.user_id = u.id
|
||||||
|
WHERE d.status = 1";
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
if ($mainDomain) {
|
||||||
|
$sql .= " AND d.main_domain = ?";
|
||||||
|
$params[] = $mainDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql .= " ORDER BY d.created_at DESC";
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
return $stmt->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 兼容旧代码 - 获取所有域名(包含已删除的)
|
||||||
|
*/
|
||||||
|
function getAllDomains() {
|
||||||
|
$pdo = getDB();
|
||||||
|
$stmt = $pdo->query("SELECT d.*, u.username FROM domains d
|
||||||
|
LEFT JOIN users u ON d.user_id = u.id
|
||||||
|
ORDER BY d.created_at DESC");
|
||||||
|
return $stmt->fetchAll();
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
@@ -19,9 +19,30 @@ $mainDomains = getMainDomains();
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta name="description" content="自研域名分发网站,免费获取二级域名,支持 A、AAAA、CNAME、MX、NS、TXT 等记录。积分制管理,每日签到得积分,邀请好友额外奖励。基于阿里云DNS,稳定可靠。" />
|
||||||
|
<meta name="keywords" content="域名,二级域名,免费,域名分发,分发,积分,记录" />
|
||||||
|
<meta name="robots" content="index, follow">
|
||||||
<title><?php echo SITE_NAME; ?> - 用心制作的免费二级域名分发网站</title>
|
<title><?php echo SITE_NAME; ?> - 用心制作的免费二级域名分发网站</title>
|
||||||
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<link rel="icon" href="favicon.ico">
|
||||||
|
<link rel="alternate" hreflang="zh-CN" href="https://dns.parlz.com/" />
|
||||||
|
<meta property="og:title" content="Parlz Domains - 用心制作的免费二级域名分发网站">
|
||||||
|
<meta property="og:description" content="自研域名分发网站,免费获取二级域名,支持 A、AAAA、CNAME、MX、NS、TXT 等记录。积分制管理,每日签到得积分,邀请好友额外奖励。基于阿里云DNS,稳定可靠。">
|
||||||
|
<meta property="og:image" content="https://dns.parlz.com/favicon.ico">
|
||||||
|
<meta property="og:image:width" content="256">
|
||||||
|
<meta property="og:image:height" content="256">
|
||||||
|
<meta property="og:url" content="https://dns.parlz.com/">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:site_name" content="Parlz Domains">
|
||||||
|
<meta property="og:locale" content="zh_CN">
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content="Parlz Domains - 用心制作的免费二级域名分发网站">
|
||||||
|
<meta name="twitter:description" content="自研域名分发网站,免费获取二级域名,支持 A、AAAA、CNAME、MX、NS、TXT 等记录。积分制管理,每日签到得积分,邀请好友额外奖励。基于阿里云DNS,稳定可靠。">
|
||||||
|
<meta name="twitter:image" content="https://dns.parlz.com/favicon.ico">
|
||||||
|
<meta name="twitter:site" content="https://dns.parlz.com/">
|
||||||
|
<link rel="canonical" href="https://dns.parlz.com/">
|
||||||
|
<link rel="me" href="https://git.parlz.com/Xiaoxiaobai5724/parlz-domain">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -35,10 +56,10 @@ $mainDomains = getMainDomains();
|
|||||||
<i class="fas <?php echo $currentTheme === 'dark' ? 'fa-sun' : 'fa-moon'; ?>"></i>
|
<i class="fas <?php echo $currentTheme === 'dark' ? 'fa-sun' : 'fa-moon'; ?>"></i>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<a href="auth.php?action=login" class="btn btn-primary btn-sm">
|
<a href="login" class="btn btn-primary btn-sm">
|
||||||
<i class="fas fa-sign-in-alt"></i>
|
<i class="fas fa-sign-in-alt"></i>
|
||||||
</a>
|
</a>
|
||||||
<a href="auth.php?action=register" class="btn btn-sm" style="background:var(--bg-primary);border:1px solid var(--border-color);color:var(--text-primary);">
|
<a href="register" class="btn btn-sm" style="background:var(--bg-primary);border:1px solid var(--border-color);color:var(--text-primary);">
|
||||||
<i class="fas fa-user-plus"></i>
|
<i class="fas fa-user-plus"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,10 +72,10 @@ $mainDomains = getMainDomains();
|
|||||||
<br>积分制管理,每日签到免费领取积分
|
<br>积分制管理,每日签到免费领取积分
|
||||||
</p>
|
</p>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<a href="auth.php?action=register" class="btn btn-primary btn-large">
|
<a href="register" class="btn btn-primary btn-large">
|
||||||
<i class="fas fa-user-plus"></i> 立即注册
|
<i class="fas fa-user-plus"></i> 立即注册
|
||||||
</a>
|
</a>
|
||||||
<a href="auth.php?action=login" class="btn btn-outline btn-large">
|
<a href="login" class="btn btn-outline btn-large">
|
||||||
<i class="fas fa-sign-in-alt"></i> 已有账号
|
<i class="fas fa-sign-in-alt"></i> 已有账号
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,8 +111,8 @@ $mainDomains = getMainDomains();
|
|||||||
</div>
|
</div>
|
||||||
<div class="feature-card">
|
<div class="feature-card">
|
||||||
<i class="fas fa-cloud-upload-alt"></i>
|
<i class="fas fa-cloud-upload-alt"></i>
|
||||||
<h3>Cloudflare 驱动</h3>
|
<h3>域名数量</h3>
|
||||||
<p>域名自动托管到 Cloudflare,全球节点加速,稳定可靠</p>
|
<p>目前有 3 个域名:euil.cn、feld.top、lrgdmc.cn,全球节点加速,稳定可靠</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="feature-card">
|
<div class="feature-card">
|
||||||
<i class="fas fa-users"></i>
|
<i class="fas fa-users"></i>
|
||||||
@@ -108,7 +129,48 @@ $mainDomains = getMainDomains();
|
|||||||
<p style="margin-top:6px;font-size:12px;">
|
<p style="margin-top:6px;font-size:12px;">
|
||||||
<i class="fas fa-shield-alt"></i> 所有数据安全加密
|
<i class="fas fa-shield-alt"></i> 所有数据安全加密
|
||||||
</p>
|
</p>
|
||||||
|
<div class="social-links" style="display: none;">
|
||||||
|
<a href="https://facebook.com/" target="_blank" rel="noopener">
|
||||||
|
<i class="fab fa-facebook"></i>
|
||||||
|
</a>
|
||||||
|
<a href="https://twitter.com/" target="_blank" rel="noopener">
|
||||||
|
<i class="fab fa-twitter"></i>
|
||||||
|
</a>
|
||||||
|
<a href="https://linkedin.com/" target="_blank" rel="noopener">
|
||||||
|
<i class="fab fa-linkedin"></i>
|
||||||
|
</a>
|
||||||
|
<a href="https://instagram.com/" target="_blank" rel="noopener">
|
||||||
|
<i class="fab fa-instagram"></i>
|
||||||
|
</a>
|
||||||
|
<a href="https://youtube.com/" target="_blank" rel="noopener">
|
||||||
|
<i class="fab fa-youtube"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "WebApplication",
|
||||||
|
"name": "Parlz Domains",
|
||||||
|
"description": "免费二级域名分发平台,支持 A、AAAA、CNAME、MX、NS、TXT、SRV 记录,积分制管理。",
|
||||||
|
"url": "https://dns.parlz.com/",
|
||||||
|
"applicationCategory": "NetworkApplication",
|
||||||
|
"operatingSystem": "All",
|
||||||
|
"browserRequirements": "现代浏览器",
|
||||||
|
"offers": {
|
||||||
|
"@type": "Offer",
|
||||||
|
"price": "0",
|
||||||
|
"priceCurrency": "CNY",
|
||||||
|
"availability": "https://schema.org/InStock"
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "Parlz Domains",
|
||||||
|
"url": "https://dns.parlz.com/"
|
||||||
|
},
|
||||||
|
"inLanguage": "zh-CN"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+6
-3
@@ -22,10 +22,13 @@ try {
|
|||||||
is_admin TINYINT DEFAULT 0,
|
is_admin TINYINT DEFAULT 0,
|
||||||
is_verified TINYINT DEFAULT 0,
|
is_verified TINYINT DEFAULT 0,
|
||||||
verification_token VARCHAR(64),
|
verification_token VARCHAR(64),
|
||||||
|
reset_token VARCHAR(64) NULL,
|
||||||
|
reset_expires DATETIME NULL,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
last_sign_in DATE,
|
last_sign_in DATE,
|
||||||
INDEX idx_invite_code (invite_code),
|
INDEX idx_invite_code (invite_code),
|
||||||
INDEX idx_email (email),
|
INDEX idx_email (email),
|
||||||
|
INDEX idx_reset_token (reset_token),
|
||||||
FOREIGN KEY (invited_by) REFERENCES users(id)
|
FOREIGN KEY (invited_by) REFERENCES users(id)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
||||||
|
|
||||||
@@ -83,12 +86,12 @@ try {
|
|||||||
('max_domains_per_user', '10'),
|
('max_domains_per_user', '10'),
|
||||||
('sign_in_credit', '2'),
|
('sign_in_credit', '2'),
|
||||||
('invite_credit', '10'),
|
('invite_credit', '10'),
|
||||||
('site_name', '域名分发系统')
|
('site_name', 'Parlz Domains')
|
||||||
");
|
");
|
||||||
|
|
||||||
// 创建管理员账户(提示用户输入)
|
// 创建管理员账户(提示用户输入)
|
||||||
echo "<h2>数据库安装成功!</h2>";
|
echo "<h2>✅ 数据库安装成功!</h2>";
|
||||||
echo "<p><b>您已安装 Parlz Domains Beta 0.1。</b></p>"
|
echo "<p><b>您已安装 " . SITE_NAME . " Beta 0.2。</b></p>";
|
||||||
echo "<p>请访问 <a href='auth.php?action=register'>注册页面</a> 来创建管理员账户。</p>";
|
echo "<p>请访问 <a href='auth.php?action=register'>注册页面</a> 来创建管理员账户。</p>";
|
||||||
echo "<p><a href='/'>进入首页</a></p>";
|
echo "<p><a href='/'>进入首页</a></p>";
|
||||||
|
|
||||||
|
|||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'functions.php';
|
||||||
|
|
||||||
|
$currentTheme = getTheme();
|
||||||
|
$message = null;
|
||||||
|
$success = false;
|
||||||
|
$token = $_GET['token'] ?? '';
|
||||||
|
|
||||||
|
// 验证token
|
||||||
|
if (empty($token)) {
|
||||||
|
$message = '缺少重置令牌';
|
||||||
|
$error = true;
|
||||||
|
} else {
|
||||||
|
// 验证token是否有效
|
||||||
|
$pdo = getDB();
|
||||||
|
$stmt = $pdo->prepare("SELECT id, email FROM users WHERE reset_token = ? AND reset_expires > NOW()");
|
||||||
|
$stmt->execute([$token]);
|
||||||
|
$user = $stmt->fetch();
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
$message = '重置链接无效或已过期,请重新申请';
|
||||||
|
$error = true;
|
||||||
|
} else {
|
||||||
|
$error = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理密码重置
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !$error) {
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
$confirm = $_POST['confirm_password'] ?? '';
|
||||||
|
|
||||||
|
if (strlen($password) < 6) {
|
||||||
|
$message = '密码长度至少6位';
|
||||||
|
} elseif ($password !== $confirm) {
|
||||||
|
$message = '两次密码输入不一致';
|
||||||
|
} else {
|
||||||
|
$result = resetPassword($token, $password);
|
||||||
|
if ($result['success']) {
|
||||||
|
$success = true;
|
||||||
|
$message = $result['message'];
|
||||||
|
} else {
|
||||||
|
$message = $result['message'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pageTitle = '重置密码 - ' . SITE_NAME;
|
||||||
|
$pageDescription = '重置您的 ' . SITE_NAME . ' 账号密码。';
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN" data-theme="<?php echo $currentTheme; ?>">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><?php echo htmlspecialchars($pageTitle); ?></title>
|
||||||
|
<meta name="description" content="<?php echo htmlspecialchars($pageDescription); ?>">
|
||||||
|
<link rel="canonical" href="https://<?php echo $_SERVER['HTTP_HOST']; ?>/reset-password.php">
|
||||||
|
<meta name="robots" content="index, follow">
|
||||||
|
<link rel="alternate" hreflang="zh-CN" href="https://<?php echo $_SERVER['HTTP_HOST']; ?>/reset_password.php" />
|
||||||
|
<meta name="twitter:site" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/reset_password.php">
|
||||||
|
<meta property="og:title" content="<?php echo htmlspecialchars($pageTitle); ?>">
|
||||||
|
<meta property="og:description" content="<?php echo htmlspecialchars($pageDescription); ?>">
|
||||||
|
<meta property="og:image" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/favicon.ico">
|
||||||
|
<meta property="og:image:width" content="256">
|
||||||
|
<meta property="og:image:height" content="256">
|
||||||
|
<meta property="og:url" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/reset_password">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:site_name" content="<?php echo SITE_NAME; ?>">
|
||||||
|
<meta property="og:locale" content="zh_CN">
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content="<?php echo htmlspecialchars($pageTitle); ?>">
|
||||||
|
<meta name="twitter:description" content="<?php echo htmlspecialchars($pageDescription); ?>">
|
||||||
|
<meta name="twitter:image" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/favicon.ico">
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "WebPage",
|
||||||
|
"name": "<?php echo htmlspecialchars($pageTitle); ?>",
|
||||||
|
"description": "<?php echo htmlspecialchars($pageDescription); ?>",
|
||||||
|
"url": "https://<?php echo $_SERVER['HTTP_HOST']; ?>/reset_password.php",
|
||||||
|
"inLanguage": "zh-CN"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="auth-container">
|
||||||
|
<div class="auth-card">
|
||||||
|
<div class="icon-top">
|
||||||
|
<i class="fas fa-lock"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>重置密码</h2>
|
||||||
|
<p class="subtitle">设置新密码</p>
|
||||||
|
|
||||||
|
<?php if ($message && !$success): ?>
|
||||||
|
<div class="alert alert-error">
|
||||||
|
<i class="fas fa-exclamation-circle"></i>
|
||||||
|
<?php echo htmlspecialchars($message); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($success): ?>
|
||||||
|
<div class="success-box">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<h3>✅ 密码重置成功</h3>
|
||||||
|
<p><?php echo htmlspecialchars($message); ?></p>
|
||||||
|
<a href="/login" class="btn btn-primary" style="display:inline-flex;width:auto;padding:12px 32px;">
|
||||||
|
<i class="fas fa-sign-in-alt"></i> 立即登录
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php elseif ($error): ?>
|
||||||
|
<div class="alert alert-error" style="margin-top:10px;">
|
||||||
|
<i class="fas fa-exclamation-circle"></i>
|
||||||
|
<?php echo htmlspecialchars($message); ?>
|
||||||
|
</div>
|
||||||
|
<div class="auth-links" style="margin-top:20px;">
|
||||||
|
<a href="/forgot-password">重新申请重置链接</a>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<form method="POST">
|
||||||
|
<div class="form-group">
|
||||||
|
<label><i class="fas fa-lock"></i> 新密码</label>
|
||||||
|
<input type="password" name="password" placeholder="至少6位" required minlength="6">
|
||||||
|
<div class="password-hint">密码至少6个字符</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label><i class="fas fa-lock"></i> 确认密码</label>
|
||||||
|
<input type="password" name="confirm_password" placeholder="再次输入新密码" required minlength="6">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="fas fa-check"></i> 确认重置
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="auth-links">
|
||||||
|
<a href="/login">返回登录</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow: /vendor/
|
||||||
|
Disallow: /aliapi/
|
||||||
|
Sitemap: https://dns.parlz.com/sitemap.xml
|
||||||
+356
@@ -0,0 +1,356 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'functions.php';
|
||||||
|
$currentTheme = getTheme();
|
||||||
|
if (isset($_GET['ajax']) && $_GET['ajax'] == 1) {
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
$domain = isset($_GET['domain']) ? trim($_GET['domain']) : '';
|
||||||
|
if (empty($domain)) {
|
||||||
|
echo json_encode(['success' => false, 'message' => '请提供域名参数']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if (!preg_match('/^([a-z0-9\-]+\.)+[a-z]{2,}$/i', $domain)) {
|
||||||
|
echo json_encode(['success' => false, 'message' => '域名格式无效']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$pdo = getDB();
|
||||||
|
$parts = explode('.', $domain);
|
||||||
|
$subdomain = strtolower($parts[0]);
|
||||||
|
$mainDomain = strtolower(implode('.', array_slice($parts, 1)));
|
||||||
|
$mainDomains = getMainDomains();
|
||||||
|
if (!in_array($mainDomain, $mainDomains)) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'available' => false,
|
||||||
|
'message' => '不支持的域名: ' . $mainDomain,
|
||||||
|
'record_type' => null,
|
||||||
|
'record_value' => null,
|
||||||
|
'username' => null,
|
||||||
|
'created_at' => null
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$stmt = $pdo->prepare("SELECT d.*, u.username FROM domains d LEFT JOIN users u ON d.user_id = u.id WHERE d.main_domain = ? AND d.subdomain = ? AND d.status = 1");
|
||||||
|
$stmt->execute([$mainDomain, $subdomain]);
|
||||||
|
$record = $stmt->fetch();
|
||||||
|
if ($record) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'available' => false,
|
||||||
|
'message' => '该域名已被 ' . htmlspecialchars($record['username'] ?? '用户') . ' 占用',
|
||||||
|
'record_type' => $record['record_type'],
|
||||||
|
'record_value' => $record['record_value'],
|
||||||
|
'username' => $record['username'] ?? null,
|
||||||
|
'created_at' => $record['created_at']
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'available' => true,
|
||||||
|
'message' => '域名可用',
|
||||||
|
'record_type' => null,
|
||||||
|
'record_value' => null,
|
||||||
|
'username' => null,
|
||||||
|
'created_at' => null
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN" data-theme="<?php echo $currentTheme; ?>">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>域名查询 - <?php echo SITE_NAME; ?></title>
|
||||||
|
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<meta name="description" content="查询域名是否被占用。支持 euil.cn、feld.top、lrgdmc.cn 等主域名。">
|
||||||
|
<meta name="keywords" content="域名查询,域名占用,子域名查询">
|
||||||
|
<meta property="og:title" content="域名查询 - <?php echo SITE_NAME; ?>">
|
||||||
|
<meta property="og:description" content="查询域名是否被占用。支持多个主域名。">
|
||||||
|
<meta property="og:image" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/favicon.ico">
|
||||||
|
<meta property="og:image:width" content="1200">
|
||||||
|
<meta property="og:image:height" content="630">
|
||||||
|
<meta property="og:url" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/search">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:site_name" content="<?php echo SITE_NAME; ?>">
|
||||||
|
<meta property="og:locale" content="zh_CN">
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content="域名查询 - <?php echo SITE_NAME; ?>">
|
||||||
|
<meta name="twitter:description" content="查询域名是否被占用。">
|
||||||
|
<meta name="twitter:image" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/favicon.ico">
|
||||||
|
<meta name="robots" content="index, follow">
|
||||||
|
<link rel="alternate" hreflang="zh-CN" href="https://<?php echo $_SERVER['HTTP_HOST']; ?>/search" />
|
||||||
|
<meta name="twitter:site" content="https://<?php echo $_SERVER['HTTP_HOST']; ?>/search">
|
||||||
|
<link rel="canonical" href="https://<?php echo $_SERVER['HTTP_HOST']; ?>/search">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<header>
|
||||||
|
<div class="header-content">
|
||||||
|
<h1><i class="fas fa-globe"></i> <?php echo SITE_NAME; ?></h1>
|
||||||
|
<div class="header-actions">
|
||||||
|
<form method="POST" style="display: inline;">
|
||||||
|
<input type="hidden" name="action" value="toggle_theme">
|
||||||
|
<button type="submit" class="theme-toggle" title="切换主题">
|
||||||
|
<i class="fas <?php echo $currentTheme === 'dark' ? 'fa-sun' : 'fa-moon'; ?>"></i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<a href="auth.php?action=login" class="btn btn-primary btn-sm">
|
||||||
|
<i class="fas fa-sign-in-alt"></i>
|
||||||
|
</a>
|
||||||
|
<a href="auth.php?action=register" class="btn btn-sm" style="background:var(--bg-primary);border:1px solid var(--border-color);color:var(--text-primary);">
|
||||||
|
<i class="fas fa-user-plus"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="search-container">
|
||||||
|
<div class="search-header">
|
||||||
|
<h1><i class="fas fa-search"></i> 域名查询</h1>
|
||||||
|
<p>输入要查询的域名,检查是否已被注册或占用</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="search-box">
|
||||||
|
<input type="text" id="domainInput" placeholder="输入域名,例如:abc.euil.cn" />
|
||||||
|
<button class="btn btn-primary" id="searchBtn">
|
||||||
|
<i class="fas fa-search"></i> 查询
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result-card" id="resultCard">
|
||||||
|
<div class="result-status" id="resultStatus">
|
||||||
|
<div class="icon"><div class="loading-spinner"></div></div>
|
||||||
|
<div class="info">
|
||||||
|
<div class="main" id="resultMain">查询中...</div>
|
||||||
|
<div class="sub" id="resultSub">正在检查域名状态</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="result-details" id="resultDetails">
|
||||||
|
<table>
|
||||||
|
<tr><td>完整域名</td><td id="detailFullDomain">-</td></tr>
|
||||||
|
<tr><td>状态</td><td id="detailStatus">-</td></tr>
|
||||||
|
<tr><td>所属用户</td><td id="detailUser">-</td></tr>
|
||||||
|
<tr><td>创建时间</td><td id="detailTime">-</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="result-actions" id="resultActions">
|
||||||
|
<a href="#" class="btn btn-primary" id="actionAddDomain" style="display:none;">
|
||||||
|
<i class="fas fa-plus"></i> 添加此域名
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card history-card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h2><i class="fas fa-clock"></i> 最近查询</h2>
|
||||||
|
<span class="clear-history" id="clearHistory">清空记录</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body" id="historyContainer">
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="fas fa-inbox"></i>
|
||||||
|
<p>暂无查询记录</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var domainInput = document.getElementById('domainInput');
|
||||||
|
var searchBtn = document.getElementById('searchBtn');
|
||||||
|
var resultCard = document.getElementById('resultCard');
|
||||||
|
var resultStatus = document.getElementById('resultStatus');
|
||||||
|
var resultMain = document.getElementById('resultMain');
|
||||||
|
var resultSub = document.getElementById('resultSub');
|
||||||
|
var detailFullDomain = document.getElementById('detailFullDomain');
|
||||||
|
var detailStatus = document.getElementById('detailStatus');
|
||||||
|
var detailType = document.getElementById('detailType');
|
||||||
|
var detailValue = document.getElementById('detailValue');
|
||||||
|
var detailUser = document.getElementById('detailUser');
|
||||||
|
var detailTime = document.getElementById('detailTime');
|
||||||
|
var actionAddDomain = document.getElementById('actionAddDomain');
|
||||||
|
var historyContainer = document.getElementById('historyContainer');
|
||||||
|
var clearHistoryBtn = document.getElementById('clearHistory');
|
||||||
|
|
||||||
|
loadHistory();
|
||||||
|
|
||||||
|
searchBtn.addEventListener('click', doSearch);
|
||||||
|
domainInput.addEventListener('keydown', function(e) {
|
||||||
|
if (e.key === 'Enter') doSearch();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('.tag').forEach(function(tag) {
|
||||||
|
tag.addEventListener('click', function() {
|
||||||
|
domainInput.value = this.dataset.domain;
|
||||||
|
doSearch();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
clearHistoryBtn.addEventListener('click', function() {
|
||||||
|
if (confirm('确定要清空所有查询记录吗?')) {
|
||||||
|
localStorage.removeItem('domainSearchHistory');
|
||||||
|
loadHistory();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function doSearch() {
|
||||||
|
var domain = domainInput.value.trim();
|
||||||
|
if (!domain) {
|
||||||
|
alert('请输入域名');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!domain.match(/^([a-z0-9\-]+\.)+[a-z]{2,}$/i)) {
|
||||||
|
alert('请输入正确的域名格式,例如:api.euil.cn');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultCard.classList.add('show');
|
||||||
|
resultStatus.className = 'result-status checking';
|
||||||
|
resultStatus.querySelector('.icon').innerHTML = '<div class="loading-spinner"></div>';
|
||||||
|
resultMain.textContent = '查询中...';
|
||||||
|
resultSub.textContent = '正在检查域名 ' + domain;
|
||||||
|
actionAddDomain.style.display = 'none';
|
||||||
|
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', '/search/' + encodeURIComponent(domain) + '&ajax=1', true);
|
||||||
|
xhr.timeout = 30000;
|
||||||
|
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if (xhr.readyState === 4) {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
try {
|
||||||
|
var data = JSON.parse(xhr.responseText);
|
||||||
|
if (data.success) {
|
||||||
|
displayResult(data, domain);
|
||||||
|
saveHistory(domain, data);
|
||||||
|
} else {
|
||||||
|
showError(data.message || '查询失败');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showError('解析响应失败');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showError('服务器错误 (HTTP ' + xhr.status + ')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onerror = function() {
|
||||||
|
showError('网络错误,请稍后重试');
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.ontimeout = function() {
|
||||||
|
showError('请求超时,请稍后重试');
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayResult(data, domain) {
|
||||||
|
resultStatus.className = 'result-status ' + (data.available ? 'available' : 'unavailable');
|
||||||
|
resultStatus.querySelector('.icon').innerHTML = '<i class="fas ' + (data.available ? 'fa-check-circle' : 'fa-times-circle') + '"></i>';
|
||||||
|
|
||||||
|
if (data.available) {
|
||||||
|
resultMain.textContent = '✅ 域名可用!';
|
||||||
|
resultSub.textContent = '该域名尚未被注册,可以添加使用';
|
||||||
|
actionAddDomain.style.display = 'inline-block';
|
||||||
|
actionAddDomain.href = 'dashboard.php?domain=' + encodeURIComponent(domain);
|
||||||
|
} else {
|
||||||
|
resultMain.textContent = '❌ 域名已被占用';
|
||||||
|
resultSub.textContent = data.message || '该域名已被注册或占用';
|
||||||
|
actionAddDomain.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
detailFullDomain.textContent = domain;
|
||||||
|
detailStatus.textContent = data.available ? '可用' : '已占用';
|
||||||
|
detailType.textContent = data.record_type || '-';
|
||||||
|
detailValue.textContent = data.record_value || '-';
|
||||||
|
detailUser.textContent = data.username || '-';
|
||||||
|
detailTime.textContent = data.created_at || '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function showError(message) {
|
||||||
|
resultStatus.className = 'result-status';
|
||||||
|
resultStatus.querySelector('.icon').innerHTML = '<i class="fas fa-exclamation-circle" style="color:var(--accent-red);font-size:28px;"></i>';
|
||||||
|
resultMain.textContent = '❌ 查询失败';
|
||||||
|
resultSub.textContent = message;
|
||||||
|
actionAddDomain.style.display = 'none';
|
||||||
|
detailFullDomain.textContent = '-';
|
||||||
|
detailStatus.textContent = '错误';
|
||||||
|
detailType.textContent = '-';
|
||||||
|
detailValue.textContent = '-';
|
||||||
|
detailUser.textContent = '-';
|
||||||
|
detailTime.textContent = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveHistory(domain, data) {
|
||||||
|
var history = JSON.parse(localStorage.getItem('domainSearchHistory') || '[]');
|
||||||
|
history = history.filter(function(item) { return item.domain !== domain; });
|
||||||
|
history.unshift({
|
||||||
|
domain: domain,
|
||||||
|
available: data.available,
|
||||||
|
time: new Date().toISOString()
|
||||||
|
});
|
||||||
|
if (history.length > 20) history = history.slice(0, 20);
|
||||||
|
localStorage.setItem('domainSearchHistory', JSON.stringify(history));
|
||||||
|
loadHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadHistory() {
|
||||||
|
var history = JSON.parse(localStorage.getItem('domainSearchHistory') || '[]');
|
||||||
|
if (history.length === 0) {
|
||||||
|
historyContainer.innerHTML = '<div class="empty-state"><i class="fas fa-inbox"></i><p>暂无查询记录</p></div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = '<div class="history-list">';
|
||||||
|
history.forEach(function(item) {
|
||||||
|
var statusClass = item.available ? 'available' : 'unavailable';
|
||||||
|
var statusText = item.available ? '✅ 可用' : '❌ 已占用';
|
||||||
|
var time = new Date(item.time).toLocaleString('zh-CN');
|
||||||
|
html += '<div class="history-item" data-domain="' + item.domain + '">' +
|
||||||
|
'<span class="domain-name">' + item.domain + '</span>' +
|
||||||
|
'<span>' +
|
||||||
|
'<span class="status-badge ' + statusClass + '">' + statusText + '</span> ' +
|
||||||
|
'<span class="time-text">' + time + '</span>' +
|
||||||
|
'</span>' +
|
||||||
|
'</div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
historyContainer.innerHTML = html;
|
||||||
|
|
||||||
|
document.querySelectorAll('.history-item').forEach(function(item) {
|
||||||
|
item.addEventListener('click', function() {
|
||||||
|
domainInput.value = this.dataset.domain;
|
||||||
|
doSearch();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var urlParams = new URLSearchParams(window.location.search);
|
||||||
|
var domainParam = urlParams.get('domain');
|
||||||
|
if (domainParam) {
|
||||||
|
domainInput.value = domainParam;
|
||||||
|
doSearch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "SearchResultsPage",
|
||||||
|
"name": "域名查询 - <?php echo SITE_NAME; ?>",
|
||||||
|
"description": "查询域名是否被占用,支持 euil.cn、feld.top、lrgdmc.cn 等主域名。",
|
||||||
|
"url": "https://<?php echo $_SERVER['HTTP_HOST']; ?>/search",
|
||||||
|
"inLanguage": "zh-CN",
|
||||||
|
"about": {
|
||||||
|
"@type": "Thing",
|
||||||
|
"name": "域名查询"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
--accent-orange: #f0883e;
|
--accent-orange: #f0883e;
|
||||||
--radius: 12px;
|
--radius: 12px;
|
||||||
--radius-sm: 6px;
|
--radius-sm: 6px;
|
||||||
--transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
--transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="light"] {
|
[data-theme="light"] {
|
||||||
@@ -969,7 +969,7 @@ tbody tr:last-child td {
|
|||||||
background: var(--bg-primary);
|
background: var(--bg-primary);
|
||||||
padding: 1px 10px;
|
padding: 1px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 11px;
|
font-size: 12px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--accent-yellow);
|
color: var(--accent-yellow);
|
||||||
border: 1px solid var(--border-color)
|
border: 1px solid var(--border-color)
|
||||||
@@ -1386,7 +1386,7 @@ footer .fa-heart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.recent-user {
|
.recent-user {
|
||||||
font-size: 11px
|
font-size: 12px
|
||||||
}
|
}
|
||||||
|
|
||||||
.record-type-help {
|
.record-type-help {
|
||||||
@@ -1419,7 +1419,7 @@ footer .fa-heart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-sm {
|
.btn-sm {
|
||||||
font-size: 11px;
|
font-size: 12px;
|
||||||
padding: 4px 10px
|
padding: 4px 10px
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1439,7 +1439,7 @@ footer .fa-heart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.stat-card .label {
|
.stat-card .label {
|
||||||
font-size: 11px
|
font-size: 12px
|
||||||
}
|
}
|
||||||
|
|
||||||
.features-grid {
|
.features-grid {
|
||||||
@@ -1460,7 +1460,7 @@ footer .fa-heart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.feature-card p {
|
.feature-card p {
|
||||||
font-size: 11px
|
font-size: 12px
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero h1 {
|
.hero h1 {
|
||||||
@@ -1476,7 +1476,7 @@ footer .fa-heart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.domain-badge {
|
.domain-badge {
|
||||||
font-size: 11px;
|
font-size: 12px;
|
||||||
padding: 4px 10px
|
padding: 4px 10px
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1485,7 +1485,7 @@ footer .fa-heart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
font-size: 11px;
|
font-size: 12px;
|
||||||
padding: 1px 10px
|
padding: 1px 10px
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1509,6 +1509,542 @@ footer .fa-heart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.stats-bar .stat-label {
|
.stats-bar .stat-label {
|
||||||
font-size: 11px
|
font-size: 12px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 60px auto 40px
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box-large {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
box-shadow: var(--shadow)
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box-large input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 14px 18px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
background: var(--bg-input);
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 16px;
|
||||||
|
outline: none;
|
||||||
|
transition: all var(--transition)
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box-large input:focus {
|
||||||
|
border-color: var(--accent-blue);
|
||||||
|
box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.15)
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box-large button {
|
||||||
|
padding: 14px 32px;
|
||||||
|
font-size: 16px;
|
||||||
|
white-space: nowrap
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-card {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 30px;
|
||||||
|
margin-top: 25px;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-card.show {
|
||||||
|
display: block;
|
||||||
|
animation: slideDown 0.4s ease
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
margin-bottom: 16px
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-status.available {
|
||||||
|
background: rgba(63, 185, 80, 0.12);
|
||||||
|
border: 1px solid var(--accent-green);
|
||||||
|
color: var(--accent-green)
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-status.unavailable {
|
||||||
|
background: rgba(248, 81, 73, 0.12);
|
||||||
|
border: 1px solid var(--accent-red);
|
||||||
|
color: var(--accent-red)
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-status.checking {
|
||||||
|
background: rgba(88, 166, 255, 0.12);
|
||||||
|
border: 1px solid var(--accent-blue);
|
||||||
|
color: var(--accent-blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-status .icon {
|
||||||
|
font-size: 28px
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-status .info {
|
||||||
|
flex: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-status .info .main {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-status .info .sub {
|
||||||
|
font-size: 13px;
|
||||||
|
opacity: 0.8
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-details {
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
border: 1px solid var(--border-color)
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-details table {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 14px
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-details table td {
|
||||||
|
padding: 6px 10px
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-details table td:first-child {
|
||||||
|
color: var(--text-muted);
|
||||||
|
width: 120px
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-actions {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestions {
|
||||||
|
margin-top: 12px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestions .tag {
|
||||||
|
padding: 4px 14px;
|
||||||
|
border-radius: 20px;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all var(--transition)
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestions .tag:hover {
|
||||||
|
border-color: var(--accent-blue);
|
||||||
|
color: var(--accent-blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-spinner {
|
||||||
|
display: inline-block;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border: 3px solid var(--border-color);
|
||||||
|
border-top-color: var(--accent-blue);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.8s linear infinite
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-20px)
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-list {
|
||||||
|
margin-top: 20px
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 14px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
font-size: 14px
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-item:last-child {
|
||||||
|
border-bottom: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-item .status-badge {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 2px 12px;
|
||||||
|
border-radius: 12px
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.available {
|
||||||
|
background: rgba(63, 185, 80, 0.15);
|
||||||
|
color: var(--accent-green)
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.unavailable {
|
||||||
|
background: rgba(248, 81, 73, 0.15);
|
||||||
|
color: var(--accent-red)
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestions-title {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-top: 12px
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:600px) {
|
||||||
|
.search-box-large {
|
||||||
|
flex-direction: column
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box-large button {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-status {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-details table td {
|
||||||
|
display: block;
|
||||||
|
padding: 4px 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-details table td:first-child {
|
||||||
|
width: auto;
|
||||||
|
font-weight: 600
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-container {
|
||||||
|
max-width: 440px;
|
||||||
|
margin: 60px auto 40px;
|
||||||
|
padding: 0 16px
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 40px;
|
||||||
|
box-shadow: var(--shadow)
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .icon-top {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 48px;
|
||||||
|
color: var(--accent-blue);
|
||||||
|
margin-bottom: 16px
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card h2 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 700
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .subtitle {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 24px
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .form-group {
|
||||||
|
margin-bottom: 18px
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .form-group label {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: 6px
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .form-group label i {
|
||||||
|
margin-right: 6px;
|
||||||
|
color: var(--accent-blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .form-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
background: var(--bg-input);
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 15px;
|
||||||
|
transition: all var(--transition);
|
||||||
|
outline: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .form-group input:focus {
|
||||||
|
border-color: var(--accent-blue);
|
||||||
|
box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.15)
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .btn-primary {
|
||||||
|
width: 100%;
|
||||||
|
padding: 14px;
|
||||||
|
font-size: 16px;
|
||||||
|
justify-content: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .auth-links {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 18px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .auth-links a {
|
||||||
|
color: var(--accent-blue);
|
||||||
|
text-decoration: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .auth-links a:hover {
|
||||||
|
text-decoration: underline
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
margin-bottom: 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 14px
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
background: rgba(63, 185, 80, 0.12);
|
||||||
|
border: 1px solid var(--accent-green);
|
||||||
|
color: var(--accent-green)
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-error {
|
||||||
|
background: rgba(248, 81, 73, 0.12);
|
||||||
|
border: 1px solid var(--accent-red);
|
||||||
|
color: var(--accent-red)
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert i {
|
||||||
|
font-size: 18px
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-box {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-box i {
|
||||||
|
font-size: 64px;
|
||||||
|
color: var(--accent-green);
|
||||||
|
margin-bottom: 16px
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-box h3 {
|
||||||
|
margin-bottom: 8px
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-box p {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 20px
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:600px) {
|
||||||
|
.auth-card {
|
||||||
|
padding: 24px 20px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-container {
|
||||||
|
max-width: 440px;
|
||||||
|
margin: 60px auto 40px;
|
||||||
|
padding: 0 16px
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 40px;
|
||||||
|
box-shadow: var(--shadow)
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .icon-top {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 48px;
|
||||||
|
color: var(--accent-blue);
|
||||||
|
margin-bottom: 16px
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card h2 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 700
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .subtitle {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 24px
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .form-group {
|
||||||
|
margin-bottom: 18px
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .form-group label {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: 6px
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .form-group label i {
|
||||||
|
margin-right: 6px;
|
||||||
|
color: var(--accent-blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .form-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
background: var(--bg-input);
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 15px;
|
||||||
|
transition: all var(--transition);
|
||||||
|
outline: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .form-group input:focus {
|
||||||
|
border-color: var(--accent-blue);
|
||||||
|
box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.15)
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .btn-primary {
|
||||||
|
width: 100%;
|
||||||
|
padding: 14px;
|
||||||
|
font-size: 16px;
|
||||||
|
justify-content: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .auth-links {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 18px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .auth-links a {
|
||||||
|
color: var(--accent-blue);
|
||||||
|
text-decoration: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card .auth-links a:hover {
|
||||||
|
text-decoration: underline
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
margin-bottom: 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 14px
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
background: rgba(63, 185, 80, 0.12);
|
||||||
|
border: 1px solid var(--accent-green);
|
||||||
|
color: var(--accent-green)
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-error {
|
||||||
|
background: rgba(248, 81, 73, 0.12);
|
||||||
|
border: 1px solid var(--accent-red);
|
||||||
|
color: var(--accent-red)
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert i {
|
||||||
|
font-size: 18px
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-box {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-box i {
|
||||||
|
font-size: 64px;
|
||||||
|
color: var(--accent-green);
|
||||||
|
margin-bottom: 16px
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-box h3 {
|
||||||
|
margin-bottom: 8px
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-box p {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 20px
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-top: 4px
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:600px) {
|
||||||
|
.auth-card {
|
||||||
|
padding: 24px 20px
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'functions.php';
|
||||||
|
|
||||||
|
$token = $_GET['token'] ?? '';
|
||||||
|
|
||||||
|
// 添加调试日志
|
||||||
|
error_log("验证请求 - Token: " . $token);
|
||||||
|
|
||||||
|
if (empty($token)) {
|
||||||
|
die('缺少验证令牌');
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = verifyEmail($token);
|
||||||
|
|
||||||
|
// 调试日志
|
||||||
|
error_log("验证结果: " . ($result['success'] ? '成功' : '失败') . " - " . $result['message']);
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN" data-theme="<?php echo getTheme(); ?>">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>邮箱验证 - <?php echo SITE_NAME; ?></title>
|
||||||
|
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container verify-container">
|
||||||
|
<div class="verify-card">
|
||||||
|
<?php if ($result['success']): ?>
|
||||||
|
<div class="icon success">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
</div>
|
||||||
|
<h2>验证成功!</h2>
|
||||||
|
<p>您的邮箱已成功验证,现在可以登录来使用所有功能了。</p>
|
||||||
|
<a href="auth.php?action=login" class="btn btn-primary">
|
||||||
|
<i class="fas fa-sign-in-alt"></i> 去登录
|
||||||
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="icon error">
|
||||||
|
<i class="fas fa-exclamation-circle"></i>
|
||||||
|
</div>
|
||||||
|
<h2>验证失败!</h2>
|
||||||
|
<p><?php echo htmlspecialchars($result['message']); ?></p>
|
||||||
|
<p style="font-size: 13px; color: var(--text-muted);">
|
||||||
|
<i class="fas fa-info-circle"></i>
|
||||||
|
可能的原因:链接已过期、已被使用或无效,请联系管理员手动激活。
|
||||||
|
</p>
|
||||||
|
<div style="display: flex; gap: 12px; justify-content: center; flex-wrap: wrap;">
|
||||||
|
<a href="auth.php?action=login" class="btn btn-outline">
|
||||||
|
<i class="fas fa-sign-in-alt"></i> 登录
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
在新工单中引用
屏蔽一个用户