Merge pull request 'mainprogram' (#1) from mainprogram into main
Reviewed-on: http://114.66.55.103:3000/dyzzyduq/CCSWebsite/pulls/1
This commit is contained in:
commit
3869e29985
@ -0,0 +1 @@
|
||||
PyRloZHAxDYExCkaKSZojdNxaxxdnkzswI17xrhTRgg.FrPeRAfsRh1hY5jyno0CKC5CMPMAZNVuh0kErn8p0BI
|
18
auth/config.php
Executable file
18
auth/config.php
Executable file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
// 数据库配置
|
||||
$host = 'localhost'; // 数据库主机(本地通常为 localhost)
|
||||
$dbname = 'ccsbeta'; // 数据库名(你提供的名称)
|
||||
$username = 'ccsbeta'; // 数据库用户名(默认通常为 root)
|
||||
$password = 'ccsbeta'; // 数据库密码(你提供的密码)
|
||||
|
||||
// 创建数据库连接
|
||||
$conn = new mysqli($host, $username, $password, $dbname);
|
||||
|
||||
// 检查连接是否成功
|
||||
if ($conn->connect_error) {
|
||||
die("数据库连接失败: " . $conn->connect_error);
|
||||
}
|
||||
|
||||
// 设置字符集为 utf8mb4(支持中文)
|
||||
$conn->set_charset("utf8mb4");
|
||||
?>
|
38
auth/index.js
Executable file
38
auth/index.js
Executable file
@ -0,0 +1,38 @@
|
||||
function switchToLogin() {
|
||||
document.getElementById('auth-title').innerText = '登录至CCS';
|
||||
document.getElementById('login-form').style.display = 'block';
|
||||
document.getElementById('register-form').style.display = 'none';
|
||||
}
|
||||
|
||||
function switchToRegister() {
|
||||
document.getElementById('auth-title').innerText = '注册至CCS';
|
||||
document.getElementById('login-form').style.display = 'none';
|
||||
document.getElementById('register-form').style.display = 'block';
|
||||
}
|
||||
|
||||
function validateRegistration() {
|
||||
const agreement = document.getElementById('agreement').checked;
|
||||
if (!agreement) {
|
||||
alert('请先同意用户协议和隐私政策');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function showTerms(event) {
|
||||
event.preventDefault();
|
||||
document.getElementById('terms-modal').style.display = 'block';
|
||||
}
|
||||
|
||||
function showPrivacy(event) {
|
||||
event.preventDefault();
|
||||
document.getElementById('privacy-modal').style.display = 'block';
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.getElementById('terms-modal').style.display = 'none';
|
||||
document.getElementById('privacy-modal').style.display = 'none';
|
||||
}
|
||||
|
||||
// 默认显示登录表单
|
||||
window.onload = switchToLogin;
|
364
auth/index.php
Executable file
364
auth/index.php
Executable file
@ -0,0 +1,364 @@
|
||||
<?php
|
||||
session_start();
|
||||
require 'config.php';
|
||||
if (!isset($conn)) {
|
||||
die("连接失败:\$conn 未定义");
|
||||
}
|
||||
// 检测是否存在有效的 Token
|
||||
$token = isset($_COOKIE['auth_token']) ? $_COOKIE['auth_token'] : null;
|
||||
if ($token) {
|
||||
$stmt = $conn->prepare("SELECT id, username FROM users WHERE token = ?");
|
||||
$stmt->bind_param("s", $token);
|
||||
$stmt->execute();
|
||||
$stmt->store_result();
|
||||
$stmt->bind_result($id, $username);
|
||||
$stmt->fetch();
|
||||
|
||||
if ($stmt->num_rows > 0) {
|
||||
// 存储用户信息到会话
|
||||
$_SESSION['user_id'] = $id;
|
||||
$_SESSION['username'] = $username;
|
||||
|
||||
// 跳转到控制台页面
|
||||
header("Location: ../console/index.php");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>圆周云境</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f4f4f4;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
background-color: white;
|
||||
color: black;
|
||||
padding: 10px 0;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.top-bar-content {
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.top-bar h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.blue-bar {
|
||||
height: 5px;
|
||||
background-color: #007bff;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.auth-container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
margin: 20px auto;
|
||||
width: 80%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.auth-header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.auth-header h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.switch-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.switch-button {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.3s, color 0.3s;
|
||||
background-color: transparent;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
margin: 0 5px;
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.switch-button:hover {
|
||||
border-color: #007bff;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.form-container form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-container label {
|
||||
margin-bottom: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-container input {
|
||||
padding: 10px;
|
||||
margin-bottom: 15px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-container button {
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.3s, color 0.3s;
|
||||
background-color: transparent;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-container button:hover {
|
||||
border-color: #007bff;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.terms-container {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.terms-link {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.terms-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 模态框样式 */
|
||||
.modal {
|
||||
display: none; /* 默认隐藏 */
|
||||
position: fixed;
|
||||
z-index: 1000; /* 确保模态框在最上层 */
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0, 0, 0, 0.4); /* 半透明背景 */
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: #fefefe;
|
||||
margin: 15% auto; /* 垂直居中 */
|
||||
padding: 20px;
|
||||
border: 1px solid #888;
|
||||
width: 80%;
|
||||
max-width: 500px; /* 最大宽度 */
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.close {
|
||||
color: #aaa;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.close:hover,
|
||||
.close:focus {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 标题栏 -->
|
||||
<div class="top-bar">
|
||||
<div class="top-bar-content">
|
||||
<h2>圆周云境</h2>
|
||||
</div>
|
||||
<div class="blue-bar"></div>
|
||||
</div>
|
||||
|
||||
<div class="auth-container">
|
||||
<div class="auth-header">
|
||||
<h1 id="auth-title">登录至CCS</h1>
|
||||
<div class="switch-buttons">
|
||||
<button id="switch-to-login" class="switch-button" onclick="switchToLogin()">登录</button>
|
||||
<button id="switch-to-register" class="switch-button" onclick="switchToRegister()">注册</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 登录表单 -->
|
||||
<div id="login-form" class="form-container" style="display: block;">
|
||||
<form action="./login.php" method="post">
|
||||
<label for="login-username">账号</label>
|
||||
<input type="text" id="login-username" name="username" placeholder="注册时填写的圆周云境用户账号" required>
|
||||
|
||||
<label for="login-password">密码</label>
|
||||
<input type="password" id="login-password" name="password" placeholder="密码" required>
|
||||
|
||||
<button type="submit">登录</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="register-form" class="form-container">
|
||||
<form action="register.php" method="post">
|
||||
<label for="username">用户名:</label>
|
||||
<input type="text" id="username" name="username" required>
|
||||
<label for="email">邮箱:</label>
|
||||
<input type="email" id="email" name="email" required>
|
||||
<label for="password">密码:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
<label for="confirm-password">确认密码:</label>
|
||||
<input type="password" id="confirm-password" name="confirm-password" required>
|
||||
<!--<label for="organization">组织:</label>
|
||||
<input type="text" id="organization" name="organization">
|
||||
<label for="development_field">开发领域:</label>
|
||||
<input type="text" id="development_field" name="development_field">
|
||||
<label for="skill_tags">技能标签:</label>
|
||||
<input type="text" id="skill_tags" name="skill_tags">-->
|
||||
<input type="submit" value="注册">-->
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="terms-modal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close" onclick="closeModal('terms-modal')">×</span>
|
||||
<h2>用户协议</h2>
|
||||
<p id="terms-text">一、协议的范围<br>
|
||||
本用户协议适用于您(以下简称 “用户”)注册、使用 圆周云境(以下简称 “本网站”)所提供的 mc 玩家身份验证服务(以下简称 “本服务”)。<br>
|
||||
二、用户注册<br>
|
||||
用户在注册过程中应提供真实、准确、完整的信息,包括但不限于用户名、电子邮箱地址等。
|
||||
用户不得使用他人的个人信息进行注册,也不得冒用他人身份进行注册。
|
||||
用户注册成功后,应妥善保管自己的账户信息,包括用户名和密码,不得将账户信息透露给他人使用。<br>
|
||||
三、用户权利和义务<br>
|
||||
(一)权利<br>
|
||||
用户有权在本网站上进行 mc 玩家身份验证,获取相关的验证结果和信息。
|
||||
用户有权对本网站提供的服务提出意见和建议,并获得相应的反馈。<br>
|
||||
(二)义务<br>
|
||||
用户应遵守本网站的用户协议和相关规定,不得从事任何违反法律法规或本网站规定的行为,包括但不限于发布违法信息、侵犯他人权益、破坏网站正常运行等。
|
||||
用户应保证所提供的 mc 玩家信息的真实性、合法性和完整性,不得提供虚假或误导性的信息。
|
||||
用户不得将本服务用于任何商业用途或非法活动,未经本网站书面同意,不得以任何形式转让、出租、出借本服务的使用权。
|
||||
四、本网站的权利和义务<br>
|
||||
(一)权利<br>
|
||||
本网站有权对用户提供的信息进行审核、验证和管理,如发现用户违反本协议或存在任何异常行为,有权采取相应的措施,包括但不限于警告、限制使用、终止服务等。
|
||||
本网站有权在必要时修改本协议的内容,并提前在本网站上公布。如用户继续使用本网站的服务,则视为用户已接受修改后的协议。<br>
|
||||
(二)义务<br>
|
||||
本网站应采取合理的技术和管理措施,保障本网站的安全性和稳定性,确保用户能够正常使用本服务。
|
||||
本网站应对用户提供的信息进行严格保密,不得向任何第三方泄露或提供,但法律法规另有规定的除外。
|
||||
本网站应为用户提供信息查询、验证等必要的技术支持和服务。<br>
|
||||
五、服务的中断和终止<br>
|
||||
因系统维护、升级或不可抗力等因素导致本网站服务中断或终止的,本网站应及时通知用户,并尽可能减少对用户的影响。
|
||||
如用户违反本协议或法律法规的规定,本网站有权随时中断或终止向该用户提供个性服务,并保留追究其法律责任的权利。
|
||||
本协议终止后,本网站有权删除用户在本网站上的所有信息和数据,但法律法规另有规定的除外。<br>
|
||||
六、免责声明<br>
|
||||
本网站尽力提供准确、完整的信息和验证服务,但本网站不对验证结果的准确性、完整性、及时性或可靠性做出任何保证或承诺。因验证结果产生的任何纠纷或损失,本网站不承担任何责任。
|
||||
使用本网站的服务可能存在一定的风险,用户应自行承担因使用本网站服务而产生的一切风险和后果。本网站不对用户因使用本网站服务所遭受的任何直接、间接、偶然、特殊及后续的损害负责。<br>
|
||||
七、法律适用与争议解决<br>
|
||||
本协议的订立、执行和解释均适用中华人民共和国法律。<br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 隐私政策模态框 -->
|
||||
<div id="privacy-modal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close" onclick="closeModal('privacy-modal')">×</span>
|
||||
<h2>隐私政策</h2>
|
||||
<p id="privacy-text">一、我们收集的信息<br>
|
||||
为了向您提供 mc 玩家身份验证服务,我们会收集以下您的个人信息:<br>
|
||||
基本注册信息 :包括您设置的用户名、电子邮箱地址和登录密码。
|
||||
mc 玩家身份信息 :包括您提供的 mc 玩家用户名、uuid。<br>
|
||||
二、我们如何使用您的信息<br>
|
||||
用于身份验证:我们将您的 mc 玩家信息用于验证您在 mc 游戏中的身份,以便您能够正常使用本网站提供的相关服务。
|
||||
提供个性化服务:根据您的身份信息,为您推荐和展示与您相关的 mc 游戏内容、活动等。
|
||||
服务运营与改进:我们会对收集到的信息进行统计和分析,以优化网站功能、提高服务质量和用户体验。<br>
|
||||
三、我们如何保护您的信息<br>
|
||||
本网站采取加密技术,采用HTTPS技术传输数据,对您的个人信息进行加密处理,确保数据在传输和存储过程中的安全性。
|
||||
我们建立了完善的信息安全管理体系,采取物理、技术和管理等多重安全措施,防止您的信息被泄露、篡改或损坏。
|
||||
本网站限制了内部员工对用户个人信息的访问权限,只有经过授权的人员在必要情况下才能接触到您的信息,并且他们需要遵守严格的保密义务。<br>
|
||||
四、我们如何共享您的信息<br>
|
||||
未经您的明确同意,我们不会向任何第三方共享您的个人信息,但以下情况除外:
|
||||
法律法规要求或政府机关依职权要求披露时;
|
||||
为维护本网站的合法权益,如防止欺诈、追究法律责任等;
|
||||
与我们的合作伙伴共享,但前提是我们的合作伙伴必须遵守本隐私政策的规定,并且仅在为实现服务目的所必需的范围内使用您的信息。
|
||||
本网站可能会与第三方服务提供商合作,他们可能会收集您的某些信息用于提供相关服务,如数据分析、技术支持等。但我们会对这些第三方进行严格的管理和监督,确保他们遵守我们的隐私政策和安全要求。<br>
|
||||
五、您对个人信息的控制<br>
|
||||
您有权随时登录本网站查看、修改或更新您的个人信息。如发现信息有误或不完整,您可以在个人账户设置中进行更正或补充。
|
||||
您也可以选择删除您的账户和相关信息,但在删除账户后,您将无法继续使用本网站的服务,并且我们可能会保留部分必要的信息以满足法律、安全或统计等要求。<br>
|
||||
六、未成年人信息保护<br>
|
||||
如果您是未成年人,请在监护人的授权下使用本网站的一切功能<br>
|
||||
七、隐私政策的更新<br>
|
||||
我们可能会根据业务的发展或法律法规的变化适时更新本隐私政策。我们会通过在本网站上发布更新后的隐私政策等方式通知您。如果您继续使用本网站的服务,则表示您接受更新后的隐私政策。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function switchToLogin() {
|
||||
document.getElementById('auth-title').innerText = '登录至CCS';
|
||||
document.getElementById('login-form').style.display = 'block';
|
||||
document.getElementById('register-form').style.display = 'none';
|
||||
}
|
||||
|
||||
function switchToRegister() {
|
||||
document.getElementById('auth-title').innerText = '注册至CCS';
|
||||
document.getElementById('login-form').style.display = 'none';
|
||||
document.getElementById('register-form').style.display = 'block';
|
||||
}
|
||||
|
||||
function validateRegistration() {
|
||||
const agreement = document.getElementById('agreement').checked;
|
||||
if (!agreement) {
|
||||
alert('请先同意用户协议和隐私政策');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function showModal(modalId) {
|
||||
document.getElementById(modalId).style.display = 'block';
|
||||
}
|
||||
|
||||
function closeModal(modalId) {
|
||||
document.getElementById(modalId).style.display = 'none';
|
||||
}
|
||||
|
||||
// 默认显示登录表单
|
||||
window.onload = switchToLogin;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
110
auth/login.php
Executable file
110
auth/login.php
Executable file
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
session_start(); // 确保会话启动
|
||||
require './config.php';
|
||||
|
||||
if (!isset($conn)) {
|
||||
die("连接失败:\$conn 未定义");
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$username = isset($_POST['username']) ? trim($_POST['username']) : '';
|
||||
$password = isset($_POST['password']) ? $_POST['password'] : '';
|
||||
|
||||
if (empty($username) || empty($password)) {
|
||||
echo "用户名和密码不能为空,请重新输入。";
|
||||
exit();
|
||||
}
|
||||
|
||||
// 修改查询语句,将 created_at 替换为 register_time
|
||||
$stmt = $conn->prepare("SELECT id, username, password, register_time, level FROM users WHERE username = ?");
|
||||
if (!$stmt) {
|
||||
echo "数据库查询准备失败: " . $conn->error;
|
||||
exit();
|
||||
}
|
||||
$stmt->bind_param("s", $username);
|
||||
if (!$stmt->execute()) {
|
||||
echo "数据库查询执行失败: " . $stmt->error;
|
||||
$stmt->close();
|
||||
exit();
|
||||
}
|
||||
$stmt->store_result();
|
||||
// 修改绑定结果,将 created_at 替换为 register_time
|
||||
$stmt->bind_result($id, $db_username, $hashed_password, $register_time, $current_level);
|
||||
$stmt->fetch();
|
||||
|
||||
if ($stmt->num_rows > 0 && password_verify($password, $hashed_password)) {
|
||||
// 如果用户已经是等级 4,跳过等级更新
|
||||
if ($current_level == 4) {
|
||||
$level = 4;
|
||||
} else {
|
||||
// 计算等级,将 created_at 替换为 register_time
|
||||
try {
|
||||
$registration_date = new DateTime($register_time);
|
||||
$now = new DateTime();
|
||||
$interval = $registration_date->diff($now);
|
||||
|
||||
if ($interval->m >= 6) {
|
||||
$level = 3; // 6 个月到 1 年
|
||||
} elseif ($interval->m >= 1) {
|
||||
$level = 2; // 1 个月到 6 个月
|
||||
} else {
|
||||
$level = 1; // 不足 1 个月
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo "日期计算出错: " . $e->getMessage();
|
||||
$stmt->close();
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新等级到数据库
|
||||
if ($level != $current_level) {
|
||||
$update_stmt = $conn->prepare("UPDATE users SET level = ? WHERE id = ?");
|
||||
if (!$update_stmt) {
|
||||
echo "数据库更新准备失败: " . $conn->error;
|
||||
$stmt->close();
|
||||
exit();
|
||||
}
|
||||
$update_stmt->bind_param("ii", $level, $id);
|
||||
if (!$update_stmt->execute()) {
|
||||
echo "数据库更新执行失败: " . $update_stmt->error;
|
||||
$update_stmt->close();
|
||||
$stmt->close();
|
||||
exit();
|
||||
}
|
||||
$update_stmt->close();
|
||||
}
|
||||
|
||||
// 存储用户信息到会话
|
||||
$_SESSION['user_id'] = $id;
|
||||
$_SESSION['username'] = $db_username;
|
||||
|
||||
// 生成 Token
|
||||
$token = bin2hex(random_bytes(32)); // 生成一个唯一的 Token
|
||||
|
||||
// 将 Token 存储到数据库
|
||||
$stmt = $conn->prepare("UPDATE users SET token = ? WHERE id = ?");
|
||||
if (!$stmt) {
|
||||
echo "数据库更新 Token 准备失败: " . $conn->error;
|
||||
exit();
|
||||
}
|
||||
$stmt->bind_param("si", $token, $id);
|
||||
if (!$stmt->execute()) {
|
||||
echo "数据库更新 Token 执行失败: " . $stmt->error;
|
||||
$stmt->close();
|
||||
exit();
|
||||
}
|
||||
|
||||
// 设置 Token 到 Cookie
|
||||
setcookie('auth_token', $token, time() + 3600, '/', '', false, true); // 设置 Cookie,有效期为 1 小时
|
||||
|
||||
// 跳转到控制台页面并附加参数
|
||||
header("Location: ../console/index.php?login=success");
|
||||
exit();
|
||||
} else {
|
||||
echo "用户名或密码错误,请重新输入。";
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
}
|
||||
$conn->close();
|
83
auth/register.php
Executable file
83
auth/register.php
Executable file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
// 引入数据库配置文件
|
||||
require './config.php';
|
||||
|
||||
if (!isset($conn)) {
|
||||
die("连接失败:数据库连接地址未定义");
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$username = $_POST['username'];
|
||||
$email = $_POST['email'];
|
||||
$password = $_POST['password'];
|
||||
$confirm_password = $_POST['confirm-password'];
|
||||
// 获取新增字段
|
||||
$organization = $_POST['organization'] ?? '';
|
||||
$development_field = $_POST['development_field'] ?? '';
|
||||
$skill_tags = $_POST['skill_tags'] ?? '';
|
||||
$register_time = date('Y-m-d H:i:s');
|
||||
|
||||
// 输入验证逻辑
|
||||
if (empty($email)) {
|
||||
echo "邮箱不能为空,请填写邮箱地址。";
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
echo "邮箱格式不正确,请输入有效的邮箱地址。";
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($password !== $confirm_password) {
|
||||
echo "密码不一致,请重新输入。";
|
||||
exit();
|
||||
}
|
||||
|
||||
// 密码必须包含大小写字母
|
||||
if (!preg_match('/[a-z]/', $password) || !preg_match('/[A-Z]/', $password)) {
|
||||
echo "密码必须包含大小写字母。";
|
||||
exit();
|
||||
}
|
||||
|
||||
// 检查用户名是否已存在
|
||||
$stmt = $conn->prepare("SELECT username FROM users WHERE username = ?");
|
||||
$stmt->bind_param("s", $username);
|
||||
$stmt->execute();
|
||||
$stmt->store_result();
|
||||
|
||||
if ($stmt->num_rows > 0) {
|
||||
echo "用户名已存在,请选择其他用户名。";
|
||||
exit();
|
||||
}
|
||||
|
||||
// 检查邮箱是否已存在
|
||||
$stmt = $conn->prepare("SELECT email FROM users WHERE email = ?");
|
||||
$stmt->bind_param("s", $email);
|
||||
$stmt->execute();
|
||||
$stmt->store_result();
|
||||
|
||||
if ($stmt->num_rows > 0) {
|
||||
echo "邮箱已被注册,请使用其他邮箱。";
|
||||
exit();
|
||||
}
|
||||
|
||||
// 密码散列
|
||||
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
// 设置默认等级和积分
|
||||
$level = 1;
|
||||
$points = 100;
|
||||
|
||||
// 修改插入语句,添加新字段
|
||||
$stmt = $conn->prepare("INSERT INTO users (username, email, password, level, points, register_time, organization, development_field, skill_tags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->bind_param("sssisssss", $username, $email, $hashed_password, $level, $points, $register_time, $organization, $development_field, $skill_tags);
|
||||
if ($stmt->execute()) {
|
||||
echo "注册成功!请登录。";
|
||||
} else {
|
||||
echo "本地数据库创建用户失败: " . $stmt->error;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
}
|
||||
$conn->close();
|
||||
?>
|
169
auth/styles.css
Executable file
169
auth/styles.css
Executable file
@ -0,0 +1,169 @@
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f4f4f4;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
background-color: white;
|
||||
color: black;
|
||||
padding: 10px 0;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.top-bar-content {
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.top-bar h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.blue-bar {
|
||||
height: 5px;
|
||||
background-color: #007bff;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.auth-container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
margin: 20px auto;
|
||||
width: 80%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.auth-header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.auth-header h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.switch-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.switch-button {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.3s, color 0.3s;
|
||||
background-color: transparent;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
margin: 0 5px;
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.switch-button:hover {
|
||||
border-color: #007bff;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.form-container form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-container label {
|
||||
margin-bottom: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-container input {
|
||||
padding: 10px;
|
||||
margin-bottom: 15px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-container button {
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.3s, color 0.3s;
|
||||
background-color: transparent;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-container button:hover {
|
||||
border-color: #007bff;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.terms-container {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.terms-link {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.terms-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 模态框样式 */
|
||||
.modal {
|
||||
display: none; /* 默认隐藏 */
|
||||
position: fixed;
|
||||
z-index: 1000; /* 确保模态框在最上层 */
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0, 0, 0, 0.4); /* 半透明背景 */
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: #fefefe;
|
||||
margin: 15% auto; /* 垂直居中 */
|
||||
padding: 20px;
|
||||
border: 1px solid #888;
|
||||
width: 80%;
|
||||
max-width: 500px; /* 最大宽度 */
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.close {
|
||||
color: #aaa;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.close:hover,
|
||||
.close:focus {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
BIN
back.jpeg
Executable file
BIN
back.jpeg
Executable file
Binary file not shown.
After Width: | Height: | Size: 1.1 MiB |
83
console/admin/admin_delete_player.php
Executable file
83
console/admin/admin_delete_player.php
Executable file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once('./config.php');
|
||||
|
||||
// 验证用户是否登录
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
die("未授权访问!");
|
||||
}
|
||||
|
||||
// 获取当前用户ID
|
||||
$currentUserId = $_SESSION['user_id'];
|
||||
|
||||
// 查询当前用户等级
|
||||
$stmt = $conn->prepare("SELECT level FROM users WHERE id = ?");
|
||||
$stmt->bind_param("i", $currentUserId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$userData = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
// 检查是否为管理员(等级4)
|
||||
if ($userData['level'] != 4) {
|
||||
die("权限不足!");
|
||||
}
|
||||
|
||||
// 处理删除玩家逻辑
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$playerId = $_POST['player_id'];
|
||||
|
||||
// 查询玩家名称
|
||||
$stmt = $conn->prepare("SELECT player_name FROM players WHERE id = ?");
|
||||
$stmt->bind_param("i", $playerId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows == 1) {
|
||||
$playerData = $result->fetch_assoc();
|
||||
$playerName = $playerData['player_name'];
|
||||
$stmt->close();
|
||||
|
||||
// 开始事务处理
|
||||
$conn->begin_transaction();
|
||||
|
||||
try {
|
||||
$stmt = $conn->prepare("DELETE FROM players WHERE id = ?");
|
||||
$stmt->bind_param("i", $playerId);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
|
||||
if ($easyauthConn->connect_error) {
|
||||
throw new Exception("EasyAuth数据库连接失败: " . $easyauthConn->connect_error);
|
||||
}
|
||||
|
||||
// 在easyauth数据库中删除对应记录
|
||||
$stmt = $easyauthConn->prepare("DELETE FROM easyauth WHERE username = ?");
|
||||
$stmt->bind_param("s", $playerName);
|
||||
$stmt->execute();
|
||||
|
||||
if ($stmt->affected_rows == 0) {
|
||||
throw new Exception("EasyAuth中未找到对应记录或删除失败");
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
$easyauthConn->close();
|
||||
|
||||
// 提交事务
|
||||
$conn->commit();
|
||||
echo "玩家删除成功!";
|
||||
|
||||
} catch (Exception $e) {
|
||||
// 回滚事务
|
||||
$conn->rollback();
|
||||
echo "操作失败:" . $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$stmt->close();
|
||||
echo "未找到该玩家!";
|
||||
}
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
?>
|
49
console/admin/admin_delete_user.php
Executable file
49
console/admin/admin_delete_user.php
Executable file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once('./config.php');
|
||||
|
||||
// 验证用户是否登录
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
die("未授权访问!");
|
||||
}
|
||||
|
||||
// 获取当前用户ID
|
||||
$currentUserId = $_SESSION['user_id'];
|
||||
|
||||
// 查询当前用户等级
|
||||
$stmt = $conn->prepare("SELECT level FROM users WHERE id = ?");
|
||||
$stmt->bind_param("i", $currentUserId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$userData = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
// 检查是否为管理员(等级4)
|
||||
if ($userData['level'] != 4) {
|
||||
die("权限不足!");
|
||||
}
|
||||
|
||||
// 处理删除用户逻辑
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$userId = $_POST['user_id'];
|
||||
|
||||
// 先删除关联的玩家数据
|
||||
$stmt = $conn->prepare("DELETE FROM players WHERE user_id = ?");
|
||||
$stmt->bind_param("i", $userId);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
// 再删除用户
|
||||
$stmt = $conn->prepare("DELETE FROM users WHERE id = ?");
|
||||
$stmt->bind_param("i", $userId);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
echo "用户删除成功!";
|
||||
} else {
|
||||
echo "删除失败:" . $stmt->error;
|
||||
}
|
||||
$stmt->close();
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
?>
|
44
console/admin/admin_edit_player.php
Executable file
44
console/admin/admin_edit_player.php
Executable file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once('./config.php');
|
||||
|
||||
// 验证用户是否登录
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
die("未授权访问!");
|
||||
}
|
||||
|
||||
// 获取当前用户ID
|
||||
$currentUserId = $_SESSION['user_id'];
|
||||
|
||||
// 查询当前用户等级
|
||||
$stmt = $conn->prepare("SELECT level FROM users WHERE id = ?");
|
||||
$stmt->bind_param("i", $currentUserId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$userData = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
// 检查是否为管理员(等级4)
|
||||
if ($userData['level'] != 4) {
|
||||
die("权限不足!");
|
||||
}
|
||||
|
||||
// 处理编辑玩家逻辑
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$playerId = $_POST['player_id'];
|
||||
$playerName = $_POST['player_name'];
|
||||
$description = $_POST['description'];
|
||||
|
||||
$stmt = $conn->prepare("UPDATE players SET player_name=?, description=? WHERE id=?");
|
||||
$stmt->bind_param("ssi", $playerName, $description, $playerId);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
echo "玩家信息更新成功!";
|
||||
} else {
|
||||
echo "更新失败:" . $stmt->error;
|
||||
}
|
||||
$stmt->close();
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
?>
|
46
console/admin/admin_edit_user.php
Executable file
46
console/admin/admin_edit_user.php
Executable file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once('./config.php');
|
||||
|
||||
// 验证用户是否登录
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
die("未授权访问!");
|
||||
}
|
||||
|
||||
// 获取当前用户ID
|
||||
$currentUserId = $_SESSION['user_id'];
|
||||
|
||||
// 查询当前用户等级
|
||||
$stmt = $conn->prepare("SELECT level FROM users WHERE id = ?");
|
||||
$stmt->bind_param("i", $currentUserId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$userData = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
// 检查是否为管理员(等级4)
|
||||
if ($userData['level'] != 4) {
|
||||
die("权限不足!");
|
||||
}
|
||||
|
||||
// 处理编辑用户逻辑
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$userId = $_POST['user_id'];
|
||||
$username = $_POST['username'];
|
||||
$level = $_POST['level'];
|
||||
$points = $_POST['points'];
|
||||
$email = $_POST['email'];
|
||||
|
||||
$stmt = $conn->prepare("UPDATE users SET username=?, level=?, points=?, email=? WHERE id=?");
|
||||
$stmt->bind_param("siisi", $username, $level, $points, $email, $userId);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
echo "用户更新成功!";
|
||||
} else {
|
||||
echo "更新失败:" . $stmt->error;
|
||||
}
|
||||
$stmt->close();
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
?>
|
22
console/admin/config.php
Executable file
22
console/admin/config.php
Executable file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
// 数据库配置
|
||||
$host = 'localhost'; // 数据库主机(本地通常为 localhost)
|
||||
$dbname = 'ccsbeta'; // 数据库名(你提供的名称)
|
||||
$username = 'ccsbeta'; // 数据库用户名(默认通常为 root)
|
||||
$password = 'ccsbeta'; // 数据库密码(你提供的密码)
|
||||
|
||||
// 创建数据库连接
|
||||
$conn = new mysqli($host, $username, $password, $dbname);
|
||||
|
||||
// 检查连接是否成功
|
||||
if ($conn->connect_error) {
|
||||
die("数据库连接失败: " . $conn->connect_error);
|
||||
}
|
||||
$host = 'localhost';
|
||||
$dbname = 'easyauth';
|
||||
$username = 'ccs';
|
||||
$password = 'ccsdatabase';
|
||||
$easyauthConn = new mysqli($host, $username, $password, $dbname);
|
||||
|
||||
$conn->set_charset("utf8mb4");
|
||||
?>
|
BIN
console/back.jpeg
Executable file
BIN
console/back.jpeg
Executable file
Binary file not shown.
After Width: | Height: | Size: 956 KiB |
64
console/change_password.php
Executable file
64
console/change_password.php
Executable file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../auth/config.php';
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header("Location: ../auth/");
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$current_password = $_POST['current_password'];
|
||||
$new_password = $_POST['new_password'];
|
||||
$confirm_password = $_POST['confirm_password'];
|
||||
|
||||
// 输入验证
|
||||
if (empty($current_password) || empty($new_password) || empty($confirm_password)) {
|
||||
echo "所有字段都是必需的。";
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($new_password !== $confirm_password) {
|
||||
echo "新密码和确认密码不一致,请重新输入。";
|
||||
exit();
|
||||
}
|
||||
|
||||
// 密码必须包含大小写字母
|
||||
if (!preg_match('/[a-z]/', $new_password) || !preg_match('/[A-Z]/', $new_password)) {
|
||||
echo "新密码必须包含大小写字母。";
|
||||
exit();
|
||||
}
|
||||
|
||||
// 验证当前密码
|
||||
$stmt = $conn->prepare("SELECT password FROM users WHERE id = ?");
|
||||
$stmt->bind_param("i", $user_id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows === 1) {
|
||||
$row = $result->fetch_assoc();
|
||||
if (password_verify($current_password, $row['password'])) {
|
||||
// 密码验证通过,更新新密码
|
||||
$hashed_password = password_hash($new_password, PASSWORD_DEFAULT);
|
||||
$update_stmt = $conn->prepare("UPDATE users SET password = ? WHERE id = ?");
|
||||
$update_stmt->bind_param("si", $hashed_password, $user_id);
|
||||
|
||||
if ($update_stmt->execute()) {
|
||||
echo "密码更新成功!";
|
||||
} else {
|
||||
echo "密码更新失败: " . $update_stmt->error;
|
||||
}
|
||||
$update_stmt->close();
|
||||
} else {
|
||||
echo "当前密码不正确,请重新输入。";
|
||||
}
|
||||
} else {
|
||||
echo "用户不存在,请重新登录。";
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
?>
|
18
console/config.php
Executable file
18
console/config.php
Executable file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
// 数据库配置
|
||||
$host = 'localhost'; // 数据库主机(本地通常为 localhost)
|
||||
$dbname = 'ccsbeta'; // 数据库名(你提供的名称)
|
||||
$username = 'ccsbeta'; // 数据库用户名(默认通常为 root)
|
||||
$password = 'ccsbeta'; // 数据库密码(你提供的密码)
|
||||
|
||||
// 创建数据库连接
|
||||
$conn = new mysqli($host, $username, $password, $dbname);
|
||||
|
||||
// 检查连接是否成功
|
||||
if ($conn->connect_error) {
|
||||
die("数据库连接失败: " . $conn->connect_error);
|
||||
}
|
||||
|
||||
// 设置字符集为 utf8mb4(支持中文)
|
||||
$conn->set_charset("utf8mb4");
|
||||
?>
|
125
console/cookie.html
Executable file
125
console/cookie.html
Executable file
@ -0,0 +1,125 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Cookies政策 - 圆周云境</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
padding: 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
padding: 20px 0;
|
||||
border-bottom: 1px solid #e1e4e8;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
color: #2c3e50;
|
||||
font-size: 2.2rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
header p {
|
||||
color: #7f8c8d;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
padding: 30px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
h2 {
|
||||
color: #2c3e50;
|
||||
font-size: 1.5rem;
|
||||
margin: 30px 0 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #3498db;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 20px;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
color: #7f8c8d;
|
||||
font-size: 0.9rem;
|
||||
border-top: 1px solid #e1e4e8;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Cookies政策 - 圆周云境</h1>
|
||||
<p>最后更新: 2025年5月24日</p>
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
<section>
|
||||
<h2>1. Cookies的使用</h2>
|
||||
<p>圆周云境使用Cookies来增强用户体验和实现登录注册的token验证功能。Cookies是您访问我们的服务时,我们存储在您设备上的小文本文件。</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>2. Cookies的用途</h2>
|
||||
<p>我们使用Cookies的主要目的包括:</p>
|
||||
<ul>
|
||||
<li>保持您的登录状态,确保在登录过程中和登录后的会话持续性。</li>
|
||||
<li>存储您的注册token信息,以便验证您的身份并提供相关服务。</li>
|
||||
<li>个性化您的体验,基于您的偏好来定制网站内容。</li>
|
||||
<li>分析网站使用情况,以改进我们的服务。</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>3. Cookies的管理</h2>
|
||||
<p>大多数浏览器允许您控制和管理Cookies。您可以配置浏览器来接受、拒绝或删除Cookies。</p>
|
||||
<p>请注意,如果禁用Cookies,某些网站功能可能无法正常工作,包括登录和注册功能。</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>4. 第三方Cookies</h2>
|
||||
<p>我们的网站可能包含第三方服务的Cookies,如社交媒体插件或分析工具。这些第三方可能使用Cookies来收集有关您访问我们网站的信息。</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>5. 您的选择</h2>
|
||||
<p>您可以通过浏览器设置来管理和控制Cookies的使用。请访问浏览器的帮助部分获取相关信息。</p>
|
||||
<p>如果您不想接收Cookies,可以将浏览器设置为拒绝对Cookies的请求。请注意,这样做可能会影响您使用我们网站的某些功能。</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p>© 2025 圆周云境. 保留所有权利。</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
67
console/delete_account.php
Executable file
67
console/delete_account.php
Executable file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../auth/config.php';
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header("Location: ../auth/");
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$password = $_POST['password'];
|
||||
$confirm = $_POST['confirm'];
|
||||
|
||||
// 输入验证
|
||||
if (empty($password) || empty($confirm)) {
|
||||
echo "所有字段都是必需的。";
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($confirm !== "确认注销") {
|
||||
echo "请输入'确认注销'进行确认。";
|
||||
exit();
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
$stmt = $conn->prepare("SELECT password FROM users WHERE id = ?");
|
||||
$stmt->bind_param("i", $user_id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows === 1) {
|
||||
$row = $result->fetch_assoc();
|
||||
if (password_verify($password, $row['password'])) {
|
||||
// 密码验证通过,开始删除账户
|
||||
// 先删除关联的玩家数据
|
||||
$delete_players = $conn->prepare("DELETE FROM players WHERE user_id = ?");
|
||||
$delete_players->bind_param("i", $user_id);
|
||||
$delete_players->execute();
|
||||
$delete_players->close();
|
||||
|
||||
// 再删除用户数据
|
||||
$delete_user = $conn->prepare("DELETE FROM users WHERE id = ?");
|
||||
$delete_user->bind_param("i", $user_id);
|
||||
|
||||
if ($delete_user->execute()) {
|
||||
// 删除成功,销毁会话并重定向到登录页
|
||||
session_destroy();
|
||||
echo "账户已成功注销!";
|
||||
header("refresh:2;url=../auth/");
|
||||
exit();
|
||||
} else {
|
||||
echo "账户注销失败: " . $delete_user->error;
|
||||
}
|
||||
$delete_user->close();
|
||||
} else {
|
||||
echo "密码不正确,请重新输入。";
|
||||
}
|
||||
} else {
|
||||
echo "用户不存在,请重新登录。";
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
?>
|
709
console/doc.html
Executable file
709
console/doc.html
Executable file
@ -0,0 +1,709 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>帮助文档</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
background-color: #f8f9fa;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 280px;
|
||||
background-color: #fff;
|
||||
border-right: 1px solid #e1e4e8;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.05);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #e1e4e8;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebar-header h1 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #e1e4e8;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
list-style: none;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: block;
|
||||
padding: 10px 20px;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
background-color: #e8f4ff;
|
||||
color: #0366d6;
|
||||
border-left: 3px solid #0366d6;
|
||||
}
|
||||
|
||||
.nav-item h3 {
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #6e7781;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 30px;
|
||||
margin-left: 280px;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 1px solid #e1e4e8;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.content-header h1 {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #24292e;
|
||||
}
|
||||
|
||||
.content-body {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.content-body h2 {
|
||||
margin: 20px 0 10px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content-body h3 {
|
||||
margin: 15px 0 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content-body p {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.content-body ul, .content-body ol {
|
||||
margin-bottom: 15px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.content-body li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.content-body code {
|
||||
background-color: #f6f8fa;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
background-color: #f6f8fa;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin: 15px 0;
|
||||
font-family: monospace;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.content-body img {
|
||||
max-width: 100%;
|
||||
border-radius: 4px;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.content-body a {
|
||||
color: #0366d6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.content-body a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.content-body .note {
|
||||
background-color: #f8f9fa;
|
||||
border-left: 3px solid #0366d6;
|
||||
padding: 15px;
|
||||
margin: 15px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.search-results {
|
||||
margin-top: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.search-result-item {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #e1e4e8;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.search-result-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.search-result-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
color: #0366d6;
|
||||
}
|
||||
|
||||
.search-result-desc {
|
||||
font-size: 14px;
|
||||
color: #586069;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.search-result-url {
|
||||
font-size: 12px;
|
||||
color: #3b444b;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.sidebar {
|
||||
width: 240px;
|
||||
}
|
||||
.main-content {
|
||||
margin-left: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
width: 0;
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
transition: width 0.3s;
|
||||
}
|
||||
.sidebar.active {
|
||||
width: 240px;
|
||||
}
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
}
|
||||
.mobile-nav-toggle {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-nav-toggle {
|
||||
display: none;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="sidebar" id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h1>帮助文档</h1>
|
||||
<button class="mobile-nav-toggle" id="mobile-nav-close">×</button>
|
||||
</div>
|
||||
<div class="search-box">
|
||||
<input type="text" placeholder="搜索文档..." id="search-input">
|
||||
</div>
|
||||
<ul class="nav-menu">
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link active" data-section="getting-started">入门指南</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link" data-section="features">功能概览</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<h3>基础操作</h3>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link" data-section="create-account">创建账户</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link" data-section="dashboard">仪表盘使用</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link" data-section="settings">账户设置</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<h3>常见问题</h3>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link" data-section="faq">常见问题</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link" data-section="troubleshooting">故障排除</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<button class="mobile-nav-toggle" id="sidebar-toggle">☰</button>
|
||||
|
||||
<div class="content-header">
|
||||
<h1 id="current-section-title">入门指南</h1>
|
||||
</div>
|
||||
|
||||
<div class="search-results" id="search-results" style="display: none;">
|
||||
</div>
|
||||
|
||||
<div class="content-body" id="getting-started-content">
|
||||
<h2>欢迎使用我们的服务</h2>
|
||||
<p>本帮助文档将指导您如何使用我们的产品。无论您是新手还是有经验的用户,都可以在这里找到有价值的帮助。</p>
|
||||
|
||||
<h2>开始使用</h2>
|
||||
<p>要开始使用我们的服务,请按照以下步骤操作:</p>
|
||||
<ol>
|
||||
<li>访问我们的官方网站并注册一个账户</li>
|
||||
<li>完成账户验证过程</li>
|
||||
<li>登录到您的账户</li>
|
||||
<li>熟悉界面布局和主要功能</li>
|
||||
</ol>
|
||||
|
||||
<div class="note">
|
||||
<p><strong>提示:</strong>建议您在首次登录时花几分钟时间浏览主要功能区域,这将帮助您更快地上手。</p>
|
||||
</div>
|
||||
|
||||
<h2>界面概览</h2>
|
||||
<p>我们的产品界面设计简洁直观,主要分为以下几个区域:</p>
|
||||
<ul>
|
||||
<li><strong>顶部导航栏</strong> - 包含主要功能入口和用户信息</li>
|
||||
<li><strong>左侧菜单</strong> - 提供快速访问主要功能的链接</li>
|
||||
<li><strong>主内容区域</strong> - 显示当前操作的主要内容</li>
|
||||
<li><strong>底部状态栏</strong> - 显示系统状态和相关信息</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-body" id="features-content" style="display: none;">
|
||||
<h2>功能概览</h2>
|
||||
<p>我们的产品提供以下核心功能:</p>
|
||||
|
||||
<h3>数据管理</h3>
|
||||
<p>轻松创建、编辑和管理您的数据。支持多种数据类型和格式。</p>
|
||||
|
||||
<h3>协作工具</h3>
|
||||
<p>与团队成员实时协作,提高工作效率。支持评论、共享和权限管理。</p>
|
||||
|
||||
<h3>自动化工作流</h3>
|
||||
<p>创建自定义工作流,自动化重复性任务,节省宝贵时间。</p>
|
||||
|
||||
<h3>分析与报告</h3>
|
||||
<p>强大的分析工具和定制报告功能,帮助您做出数据驱动的决策。</p>
|
||||
|
||||
<div class="code-block">
|
||||
function fetchData(apiEndpoint) {
|
||||
const response = await fetch(apiEndpoint);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
fetchData("/api/data").then(data => console.log(data));
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-body" id="create-account-content" style="display: none;">
|
||||
<h2>创建账户</h2>
|
||||
<p>创建账户是使用我们服务的第一步。以下是详细的创建流程:</p>
|
||||
|
||||
<h3>注册流程</h3>
|
||||
<ol>
|
||||
<li>访问我们的官方网站</li>
|
||||
<li>点击"注册"按钮</li>
|
||||
<li>填写必要的注册信息(姓名、邮箱、密码等)</li>
|
||||
<li>阅读并同意服务条款</li>
|
||||
<li>点击"创建账户"按钮</li>
|
||||
</ol>
|
||||
|
||||
<h3>账户验证</h3>
|
||||
<p>注册完成后,您需要验证您的邮箱地址:</p>
|
||||
<ol>
|
||||
<li>登录您的邮箱</li>
|
||||
<li>查找来自我们的验证邮件</li>
|
||||
<li>点击邮件中的验证链接</li>
|
||||
<li>按照指引完成验证过程</li>
|
||||
</ol>
|
||||
|
||||
<div class="note">
|
||||
<p><strong>注意:</strong>如果您没有收到验证邮件,请检查垃圾邮件文件夹,或者尝试重新发送验证邮件。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-body" id="dashboard-content" style="display: none;">
|
||||
<h2>仪表盘使用指南</h2>
|
||||
<p>仪表盘是您管理所有活动的中心位置。以下是仪表盘的主要功能和使用方法:</p>
|
||||
|
||||
<h3>查看概览</h3>
|
||||
<p>仪表盘主页面提供以下关键信息:</p>
|
||||
<ul>
|
||||
<li>最近活动列表</li>
|
||||
<li>待处理任务</li>
|
||||
<li>重要通知</li>
|
||||
<li>数据统计摘要</li>
|
||||
</ul>
|
||||
|
||||
<h3>自定义仪表盘</h3>
|
||||
<p>您可以根据自己的需求自定义仪表盘:</p>
|
||||
<ol>
|
||||
<li>点击右上角的"设置"图标</li>
|
||||
<li>选择"仪表盘布局"</li>
|
||||
<li>拖拽组件到所需位置</li>
|
||||
<li>保存您的布局设置</li>
|
||||
</ol>
|
||||
|
||||
<h3>快捷操作</h3>
|
||||
<p>仪表盘提供以下快捷操作:</p>
|
||||
<ul>
|
||||
<li>快速创建新项目</li>
|
||||
<li>一键访问常用功能</li>
|
||||
<li>批量管理任务</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-body" id="settings-content" style="display: none;">
|
||||
<h2>账户设置</h2>
|
||||
<p>您可以根据自己的需求自定义账户设置,包括个人资料、密码、通知等。</p>
|
||||
|
||||
<h3>个人资料</h3>
|
||||
<p>在个人资料设置中,您可以修改您的姓名、邮箱地址和头像。</p>
|
||||
|
||||
<h3>密码管理</h3>
|
||||
<p>定期更新您的密码可以提高账户安全性。点击"更改密码"按钮开始更新过程。</p>
|
||||
|
||||
<h3>通知设置</h3>
|
||||
<p>自定义您的通知偏好,选择您希望接收的通知类型和频率。</p>
|
||||
</div>
|
||||
|
||||
<div class="content-body" id="api-content" style="display: none;">
|
||||
<h2>API 使用</h2>
|
||||
<p>API 使您能够集成我们的服务到您自己的应用程序中。要开始使用 API,请先获取 API 密钥。</p>
|
||||
|
||||
<h3>获取 API 密钥</h3>
|
||||
<p>要获取 API 密钥,请按照以下步骤操作:</p>
|
||||
<ol>
|
||||
<li>登录到您的账户</li>
|
||||
<li>导航到"开发人员"或"API"设置页面</li>
|
||||
<li>点击"创建新密钥"</li>
|
||||
<li>设置密钥的名称和权限</li>
|
||||
<li>点击"生成密钥"</li>
|
||||
</ol>
|
||||
|
||||
<h3>API 请求示例</h3>
|
||||
<p>以下是一个简单的 API 请求示例:</p>
|
||||
<div class="code-block">
|
||||
const apiKey = 'your_api_key_here';
|
||||
const response = await fetch('https://api.example.com/data', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${apiKey}`
|
||||
}
|
||||
});
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
</div>
|
||||
|
||||
<h3>API 限制</h3>
|
||||
<p>请注意以下 API 使用限制:</p>
|
||||
<ul>
|
||||
<li>免费账户每分钟最多 100 次请求</li>
|
||||
<li>付费账户每分钟最多 1000 次请求</li>
|
||||
<li>所有账户每天最多 10,000 次请求</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-body" id="integrations-content" style="display: none;">
|
||||
<h2>系统集成</h2>
|
||||
<p>我们的服务可以与其他系统集成,以增强您的工作流程。支持的集成包括:电子邮件、项目管理工具、客户关系管理工具等。</p>
|
||||
|
||||
<h3>电子邮件集成</h3>
|
||||
<p>将我们的服务与您的电子邮件系统集成,可以实现自动通知和提醒功能。</p>
|
||||
|
||||
<h3>项目管理工具</h3>
|
||||
<p>与流行项目管理工具(如 Jira、Trello 等)集成,可以同步任务和项目进度。</p>
|
||||
|
||||
<h3>客户关系管理工具</h3>
|
||||
<p>与客户关系管理工具(如 Salesforce、HubSpot 等)集成,可以统一管理和分析客户数据。</p>
|
||||
</div>
|
||||
|
||||
<div class="content-body" id="faq-content" style="display: none;">
|
||||
<h2>常见问题</h2>
|
||||
<p>如果您在使用过程中遇到问题,可以查阅以下常见问题解答:</p>
|
||||
|
||||
<h3>如何重置密码?</h3>
|
||||
<p>要重置密码,请访问登录页面,点击"忘记密码"链接,按照提示操作即可。</p>
|
||||
|
||||
<h3>如何升级账户?</h3>
|
||||
<p>要升级您的账户,请导航到"账户设置"页面,选择"订阅"或"计划"选项卡,然后选择您希望升级到的计划。</p>
|
||||
|
||||
<h3>如何导出数据?</h3>
|
||||
<p>要导出您的数据,请导航到"数据管理"页面,选择您要导出的数据集,然后点击"导出"按钮。支持的导出格式包括 CSV、Excel 和 JSON。</p>
|
||||
</div>
|
||||
|
||||
<div class="content-body" id="troubleshooting-content" style="display: none;">
|
||||
<h2>故障排除</h2>
|
||||
<p>在遇到问题时,您可以参考以下故障排除指南:</p>
|
||||
|
||||
<h3>登录问题</h3>
|
||||
<p>如果您无法登录,请尝试以下步骤:</p>
|
||||
<ol>
|
||||
<li>检查您的用户名和密码是否正确</li>
|
||||
<li>确保您的账户已激活</li>
|
||||
<li>尝试使用不同的浏览器或设备</li>
|
||||
<li>清除浏览器缓存和 cookies</li>
|
||||
<li>联系支持团队获取进一步帮助</li>
|
||||
</ol>
|
||||
|
||||
<h3>数据同步问题</h3>
|
||||
<p>如果您的数据不同步,请尝试以下步骤:</p>
|
||||
<ol>
|
||||
<li>检查您的互联网连接</li>
|
||||
<li>确保您的账户有权限访问相关数据</li>
|
||||
<li>检查数据源是否可用</li>
|
||||
<li>尝试重新启动同步过程</li>
|
||||
<li>联系支持团队获取进一步帮助</li>
|
||||
</ol>
|
||||
|
||||
<h3>性能问题</h3>
|
||||
<p>如果遇到性能问题,请尝试以下步骤:</p>
|
||||
<ol>
|
||||
<li>确保您使用的是最新版本的浏览器</li>
|
||||
<li>减少同时打开的标签页和应用程序</li>
|
||||
<li>清除浏览器缓存</li>
|
||||
<li>联系支持团队获取进一步帮助</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="content-body" id="contact-content" style="display: none;">
|
||||
<h2>联系我们</h2>
|
||||
<p>如果您需要进一步的帮助,可以通过以下方式联系我们:</p>
|
||||
|
||||
<h3>电子邮件</h3>
|
||||
<p>发送电子邮件到 support@example.com,我们的支持团队会在24小时内回复。</p>
|
||||
|
||||
<h3>电话</h3>
|
||||
<p>拨打我们的支持热线:400-123-4567(工作日 9:00-18:00)</p>
|
||||
|
||||
<h3>在线聊天</h3>
|
||||
<p>登录您的账户后,点击右下角的聊天图标,与我们的客服人员实时交流。</p>
|
||||
|
||||
<h3>提交工单</h3>
|
||||
<p>在"帮助中心"页面,点击"提交工单"按钮,填写详细问题描述,我们的团队会尽快处理。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const documents = [
|
||||
{
|
||||
id: 1,
|
||||
title: "入门指南",
|
||||
content: "欢迎使用我们的服务。本帮助文档将指导您如何使用我们的产品。无论您是新手还是有经验的用户,都可以在这里找到有价值的帮助。开始使用请按照以下步骤操作:1. 访问我们的官方网站并注册一个账户;2. 完成账户验证过程;3. 登录到您的账户;4. 熟悉界面布局和主要功能。",
|
||||
url: "#getting-started"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "功能概览",
|
||||
content: "我们的产品提供以下核心功能:数据管理、协作工具、自动化工作流、分析与报告。",
|
||||
url: "#features"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "创建账户",
|
||||
content: "要开始使用我们的服务,请访问我们的官方网站并注册一个账户。注册流程包括填写必要的信息、阅读并同意服务条款、创建账户。",
|
||||
url: "#create-account"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "仪表盘使用",
|
||||
content: "仪表盘是您管理所有活动的中心位置。仪表盘主页面提供以下关键信息:最近活动列表、待处理任务、重要通知、数据统计摘要。",
|
||||
url: "#dashboard"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "账户设置",
|
||||
content: "您可以根据自己的需求自定义账户设置,包括个人资料、密码、通知等。",
|
||||
url: "#settings"
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: "API 使用",
|
||||
content: "API 使您能够集成我们的服务到您自己的应用程序中。要开始使用 API,请先获取 API 密钥。",
|
||||
url: "#api"
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: "系统集成",
|
||||
content: "我们的服务可以与其他系统集成,以增强您的工作流程。支持的集成包括:电子邮件、项目管理工具、客户关系管理工具等。",
|
||||
url: "#integrations"
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: "常见问题",
|
||||
content: "如果您在使用过程中遇到问题,可以查阅常见问题解答。我们整理了一些常见问题,以帮助您快速找到解决方案。",
|
||||
url: "#faq"
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: "故障排除",
|
||||
content: "在遇到问题时,您可以参考我们的故障排除指南。这些指南提供了详细的步骤,帮助您诊断和解决常见问题。",
|
||||
url: "#troubleshooting"
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
title: "联系我们",
|
||||
content: "如果您需要进一步的帮助,可以通过以下方式联系我们:电子邮件、电话、在线聊天。",
|
||||
url: "#contact"
|
||||
}
|
||||
];
|
||||
|
||||
document.getElementById('search-input').addEventListener('input', function() {
|
||||
const searchTerm = this.value.trim().toLowerCase();
|
||||
|
||||
if (searchTerm === '') {
|
||||
document.getElementById('search-results').style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const results = searchDocuments(searchTerm);
|
||||
displaySearchResults(results);
|
||||
});
|
||||
|
||||
function searchDocuments(term) {
|
||||
return documents.filter(doc =>
|
||||
doc.title.toLowerCase().includes(term) || doc.content.toLowerCase().includes(term));
|
||||
}
|
||||
|
||||
function displaySearchResults(results) {
|
||||
const resultsContainer = document.getElementById('search-results');
|
||||
resultsContainer.style.display = 'block';
|
||||
|
||||
if (results.length === 0) {
|
||||
resultsContainer.innerHTML = '<p>没有找到匹配的结果。</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
results.forEach(result => {
|
||||
html += `
|
||||
<div class="search-result-item">
|
||||
<div class="search-result-title">${result.title}</div>
|
||||
<div class="search-result-desc">${getExcerpt(result.content, document.getElementById('search-input').value)}</div>
|
||||
<div class="search-result-url">${result.url}</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
resultsContainer.innerHTML = html;
|
||||
}
|
||||
|
||||
function getExcerpt(content, searchTerm) {
|
||||
const words = searchTerm.split(' ');
|
||||
let excerpt = content;
|
||||
for (let word of words) {
|
||||
if (word.trim() !== '') {
|
||||
const regex = new RegExp(word, 'gi');
|
||||
excerpt = excerpt.replace(regex, match => `<strong>${match}</strong>`);
|
||||
}
|
||||
}
|
||||
const start = content.indexOf(searchTerm);
|
||||
if (start !== -1) {
|
||||
const end = start + searchTerm.length;
|
||||
let excerptStart = Math.max(0, start - 30);
|
||||
let excerptEnd = Math.min(content.length, end + 30);
|
||||
excerpt = content.substring(excerptStart, excerptEnd);
|
||||
if (excerptStart > 0) excerpt = '...' + excerpt;
|
||||
if (excerptEnd < content.length) excerpt += '...';
|
||||
const regex = new RegExp(searchTerm, 'gi');
|
||||
excerpt = excerpt.replace(regex, match => `<strong>${match}</strong>`);
|
||||
}
|
||||
return excerpt;
|
||||
}
|
||||
|
||||
document.querySelectorAll('.nav-link').forEach(link => {
|
||||
link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
document.querySelectorAll('.nav-link').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
|
||||
this.classList.add('active');
|
||||
|
||||
const section = this.getAttribute('data-section');
|
||||
|
||||
document.querySelectorAll('.content-body').forEach(content => {
|
||||
content.style.display = 'none';
|
||||
});
|
||||
|
||||
document.getElementById(`${section}-content`).style.display = 'block';
|
||||
|
||||
document.getElementById('current-section-title').textContent = this.textContent;
|
||||
|
||||
if (window.innerWidth <= 768) {
|
||||
document.getElementById('sidebar').classList.remove('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('sidebar-toggle').addEventListener('click', function() {
|
||||
document.getElementById('sidebar').classList.toggle('active');
|
||||
});
|
||||
|
||||
document.getElementById('mobile-nav-close').addEventListener('click', function() {
|
||||
document.getElementById('sidebar').classList.remove('active');
|
||||
});
|
||||
|
||||
window.onload = function() {
|
||||
document.getElementById('getting-started-content').style.display = 'block';
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
1317
console/index.php
Executable file
1317
console/index.php
Executable file
File diff suppressed because it is too large
Load Diff
1
console/ins/.user.ini
Normal file
1
console/ins/.user.ini
Normal file
@ -0,0 +1 @@
|
||||
open_basedir=/www/wwwroot/ccsbeta/console/ins/:/tmp/
|
3
console/ins/config.php
Executable file
3
console/ins/config.php
Executable file
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
$apiKey = '5ffc6f20166340fb9de0a12a02252b98';
|
||||
$apiUrl = 'http://154.222.30.227:23333/api';
|
271
console/ins/files.php
Executable file
271
console/ins/files.php
Executable file
@ -0,0 +1,271 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MCSManager 文件管理</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#165DFF',
|
||||
secondary: '#36D399',
|
||||
danger: '#F87272',
|
||||
warning: '#FBBD23',
|
||||
dark: '#1E293B',
|
||||
light: '#F8FAFC'
|
||||
},
|
||||
fontFamily: {
|
||||
inter: ['Inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.file-card {
|
||||
@apply bg-white rounded-lg shadow-md p-4 mb-4 transition-all duration-300 hover:shadow-lg;
|
||||
}
|
||||
.btn {
|
||||
@apply inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2;
|
||||
}
|
||||
.btn-primary {
|
||||
@apply bg-primary text-white hover:bg-primary/90 focus:ring-primary/50;
|
||||
}
|
||||
.btn-danger {
|
||||
@apply bg-danger text-white hover:bg-danger/90 focus:ring-danger/50;
|
||||
}
|
||||
.loading-overlay {
|
||||
@apply fixed inset-0 bg-black/50 flex items-center justify-center z-50;
|
||||
}
|
||||
.notification {
|
||||
@apply fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transition-all duration-300 transform translate-y-20 opacity-0;
|
||||
}
|
||||
.notification-visible {
|
||||
@apply translate-y-0 opacity-100;
|
||||
}
|
||||
.notification-success {
|
||||
@apply bg-green-500 text-white;
|
||||
}
|
||||
.notification-error {
|
||||
@apply bg-red-500 text-white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 font-inter text-gray-800 min-h-screen flex flex-col">
|
||||
<!-- 顶部导航栏 -->
|
||||
<header class="bg-primary text-white shadow-md sticky top-0 z-50">
|
||||
<div class="container mx-auto px-4 py-3 flex justify-between items-center">
|
||||
<div class="flex items-center space-x-2">
|
||||
<i class="fa fa-server text-xl"></i>
|
||||
<h1 class="text-xl font-bold">CCS文件管理</h1>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<main class="flex-grow container mx-auto px-4 py-6">
|
||||
<!-- 文件上传表单 -->
|
||||
<div class="mb-6">
|
||||
<form id="uploadForm" class="bg-white rounded-xl shadow-lg p-6 mb-6">
|
||||
<h3 class="text-lg font-semibold mb-4">上传文件</h3>
|
||||
<input type="file" id="fileInput" class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-primary file:text-white hover:file:bg-primary/90">
|
||||
<button type="button" id="uploadBtn" class="btn btn-primary mt-4">上传文件</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- 文件列表 -->
|
||||
<div class="bg-white rounded-xl shadow-lg p-6">
|
||||
<h3 class="text-lg font-semibold mb-4">文件列表</h3>
|
||||
<div id="fileList" class="divide-y divide-gray-200">
|
||||
<div class="file-card flex justify-between items-center">
|
||||
<span class="text-gray-800">文件名</span>
|
||||
<div class="flex space-x-2">
|
||||
<button class="btn btn-primary text-xs">下载</button>
|
||||
<button class="btn btn-danger text-xs">删除</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 加载遮罩层 -->
|
||||
<div class="loading-overlay hidden" id="loading-overlay">
|
||||
<div class="bg-white p-6 rounded-lg shadow-xl flex flex-col items-center">
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary mb-4"></div>
|
||||
<p class="text-lg font-medium" id="loading-message">正在处理...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 通知组件 -->
|
||||
<div class="notification notification-info" id="notification">
|
||||
<p id="notification-message">这是一条通知消息</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 从URL获取参数
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const uuid = urlParams.get('uuid') || 'default-uuid';
|
||||
const daemonId = urlParams.get('daemonId') || 'default-daemon-id';
|
||||
|
||||
const uploadBtn = document.getElementById('uploadBtn');
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const fileList = document.getElementById('fileList');
|
||||
|
||||
uploadBtn.addEventListener('click', () => {
|
||||
const file = fileInput.files[0];
|
||||
if (!file) {
|
||||
showNotification('请选择一个文件', 'error');
|
||||
return;
|
||||
}
|
||||
showLoading('正在上传文件...');
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
fetch(`proxy.php?path=files/upload&uuid=${uuid}&daemonId=${daemonId}`, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 200) {
|
||||
showNotification('文件上传成功', 'success');
|
||||
fetchFileList();
|
||||
} else {
|
||||
throw new Error(data.message || '上传失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('上传失败:', error);
|
||||
showNotification('上传失败: ' + error.message, 'error');
|
||||
})
|
||||
.finally(() => {
|
||||
hideLoading();
|
||||
});
|
||||
});
|
||||
|
||||
function fetchFileList() {
|
||||
showLoading('正在获取文件列表...');
|
||||
fetch(`proxy.php?path=files/list&uuid=${uuid}&daemonId=${daemonId}&target=/&page=1&page_size=50`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 200) {
|
||||
fileList.innerHTML = '';
|
||||
data.data.items.forEach(file => {
|
||||
const fileCard = document.createElement('div');
|
||||
fileCard.className = 'file-card flex justify-between items-center';
|
||||
fileCard.innerHTML = `
|
||||
<span class="text-gray-800">${file.name}</span>
|
||||
<div class="flex space-x-2">
|
||||
<button class="btn btn-primary text-xs" onclick="downloadFile('${file.name}')">下载</button>
|
||||
<button class="btn btn-danger text-xs" onclick="deleteFile('${file.name}')">删除</button>
|
||||
</div>
|
||||
`;
|
||||
fileList.appendChild(fileCard);
|
||||
});
|
||||
} else {
|
||||
throw new Error(data.message || '获取文件列表失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取文件列表失败:', error);
|
||||
showNotification('获取文件列表失败: ' + error.message, 'error');
|
||||
})
|
||||
.finally(() => {
|
||||
hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
function downloadFile(fileName) {
|
||||
showLoading('正在下载文件...');
|
||||
fetch(`proxy.php?path=files/download&uuid=${uuid}&daemonId=${daemonId}&file=${fileName}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('下载失败');
|
||||
}
|
||||
return response.blob();
|
||||
})
|
||||
.then(blob => {
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = fileName;
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
showNotification('文件下载成功', 'success');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('下载失败:', error);
|
||||
showNotification('下载失败: ' + error.message, 'error');
|
||||
})
|
||||
.finally(() => {
|
||||
hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
function deleteFile(fileName) {
|
||||
if (!confirm('确定要删除文件 ' + fileName + ' 吗?')) return;
|
||||
showLoading('正在删除文件...');
|
||||
fetch(`proxy.php?path=files/delete&uuid=${uuid}&daemonId=${daemonId}&file=${fileName}`, {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 200) {
|
||||
showNotification('文件删除成功', 'success');
|
||||
fetchFileList();
|
||||
} else {
|
||||
throw new Error(data.message || '删除失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('删除失败:', error);
|
||||
showNotification('删除失败: ' + error.message, 'error');
|
||||
})
|
||||
.finally(() => {
|
||||
hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
function showLoading(message) {
|
||||
const loadingOverlay = document.getElementById('loading-overlay');
|
||||
const loadingMessage = document.getElementById('loading-message');
|
||||
if (loadingOverlay && loadingMessage) {
|
||||
loadingMessage.textContent = message;
|
||||
loadingOverlay.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
const loadingOverlay = document.getElementById('loading-overlay');
|
||||
if (loadingOverlay) {
|
||||
loadingOverlay.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function showNotification(message, type = 'info') {
|
||||
const notification = document.getElementById('notification');
|
||||
const notificationMessage = document.getElementById('notification-message');
|
||||
if (!notification || !notificationMessage) {
|
||||
console.error('Notification elements not found');
|
||||
return;
|
||||
}
|
||||
notificationMessage.textContent = message;
|
||||
notification.classList.remove('notification-success', 'notification-error', 'notification-info');
|
||||
notification.classList.add(`notification-${type}`);
|
||||
notification.classList.add('notification-visible');
|
||||
setTimeout(() => {
|
||||
notification.classList.remove('notification-visible');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
fetchFileList();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
892
console/ins/index.php
Executable file
892
console/ins/index.php
Executable file
@ -0,0 +1,892 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MCSManager服务器管理</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#3b82f6',
|
||||
secondary: '#10b981',
|
||||
danger: '#ef4444',
|
||||
warning: '#f59e0b',
|
||||
info: '#06b6d4',
|
||||
dark: '#1e293b',
|
||||
light: '#f8fafc'
|
||||
},
|
||||
fontFamily: {
|
||||
inter: ['Inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
.server-card {
|
||||
@apply bg-white rounded-lg shadow-md p-4 mb-4 transition-all duration-300 hover:shadow-lg;
|
||||
}
|
||||
.server-status {
|
||||
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
|
||||
}
|
||||
.btn {
|
||||
@apply inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2;
|
||||
}
|
||||
.btn-primary {
|
||||
@apply bg-primary text-white hover:bg-primary/90 focus:ring-primary/50;
|
||||
}
|
||||
.btn-secondary {
|
||||
@apply bg-secondary text-white hover:bg-secondary/90 focus:ring-secondary/50;
|
||||
}
|
||||
.btn-danger {
|
||||
@apply bg-danger text-white hover:bg-danger/90 focus:ring-danger/50;
|
||||
}
|
||||
.btn-warning {
|
||||
@apply bg-warning text-white hover:bg-warning/90 focus:ring-warning/50;
|
||||
}
|
||||
.btn-outline {
|
||||
@apply border-gray-300 text-gray-700 hover:bg-gray-50 focus:ring-primary/50;
|
||||
}
|
||||
.badge {
|
||||
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
|
||||
}
|
||||
.badge-online {
|
||||
@apply bg-green-100 text-green-800;
|
||||
}
|
||||
.badge-offline {
|
||||
@apply bg-gray-100 text-gray-800;
|
||||
}
|
||||
.badge-starting {
|
||||
@apply bg-blue-100 text-blue-800;
|
||||
}
|
||||
.badge-stopping {
|
||||
@apply bg-yellow-100 text-yellow-800;
|
||||
}
|
||||
.console-line {
|
||||
@apply whitespace-pre font-mono text-sm;
|
||||
}
|
||||
.console-line-info {
|
||||
@apply text-gray-300;
|
||||
}
|
||||
.console-line-warning {
|
||||
@apply text-yellow-300;
|
||||
}
|
||||
.console-line-error {
|
||||
@apply text-red-400;
|
||||
}
|
||||
.console-line-command {
|
||||
@apply text-green-400;
|
||||
}
|
||||
.console-line-player {
|
||||
@apply text-blue-300;
|
||||
}
|
||||
.loading-overlay {
|
||||
@apply fixed inset-0 bg-black/50 flex items-center justify-center z-50;
|
||||
}
|
||||
.notification {
|
||||
@apply fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transition-all duration-300 transform translate-y-20 opacity-0;
|
||||
}
|
||||
.notification-visible {
|
||||
@apply translate-y-0 opacity-100;
|
||||
}
|
||||
.notification-success {
|
||||
@apply bg-green-500 text-white;
|
||||
}
|
||||
.notification-error {
|
||||
@apply bg-red-500 text-white;
|
||||
}
|
||||
.notification-info {
|
||||
@apply bg-blue-500 text-white;
|
||||
}
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 font-inter text-gray-800 min-h-screen flex flex-col">
|
||||
<!-- 导航栏 -->
|
||||
<header class="bg-dark text-white shadow-lg">
|
||||
<div class="container mx-auto px-4 py-3 flex items-center justify-between">
|
||||
<div class="flex items-center space-x-2">
|
||||
<i class="fa fa-server text-primary text-2xl"></i>
|
||||
<h1 class="text-xl font-bold">MCSManager 服务器管理</h1>
|
||||
</div>
|
||||
<nav>
|
||||
<ul class="flex space-x-6">
|
||||
<li><a href="#" class="flex items-center hover:text-primary transition-colors duration-200"><i class="fa fa-home mr-1"></i> 首页</a></li>
|
||||
<li><a href="#" class="flex items-center hover:text-primary transition-colors duration-200"><i class="fa fa-server mr-1"></i> 服务器</a></li>
|
||||
<li><a href="#" class="flex items-center hover:text-primary transition-colors duration-200"><i class="fa fa-cog mr-1"></i> 设置</a></li>
|
||||
<li><a href="#" class="flex items-center hover:text-primary transition-colors duration-200"><i class="fa fa-question-circle mr-1"></i> 帮助</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<main class="flex-grow container mx-auto px-4 py-6">
|
||||
<!-- 面包屑导航 -->
|
||||
<div class="mb-4 text-sm text-gray-500">
|
||||
<a href="#" class="hover:text-primary">首页</a> > <a href="#" class="hover:text-primary">服务器列表</a> > <span class="text-gray-700 font-medium" id="breadcrumb-server-name">生存服务器</span>
|
||||
</div>
|
||||
|
||||
<!-- 服务器详情 -->
|
||||
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
||||
<!-- 服务器状态和基本信息 -->
|
||||
<div class="p-6 border-b">
|
||||
<div class="flex flex-col md:flex-row md:items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold" id="detail-server-name">生存服务器</h2>
|
||||
<div class="flex items-center mt-2">
|
||||
<span class="badge badge-online mr-3" id="detail-server-status">在线</span>
|
||||
<span class="text-sm text-gray-500" id="detail-server-ip">127.0.0.1:25565</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 md:mt-0 flex space-x-3">
|
||||
<button class="btn btn-danger" id="action-stop-server">
|
||||
<i class="fa fa-stop mr-1"></i> 停止
|
||||
</button>
|
||||
<button class="btn btn-warning" id="action-restart-server">
|
||||
<i class="fa fa-refresh mr-1"></i> 重启
|
||||
</button>
|
||||
<button class="btn btn-secondary" id="action-start-server">
|
||||
<i class="fa fa-play mr-1"></i> 启动
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 服务器信息和性能 -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 p-6 border-b">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold mb-3">基本信息</h3>
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600">UUID:</span>
|
||||
<span class="font-medium" id="detail-uuid">5f8b4c2a-3d7e-4b1a-9c5d-6e7f8a9b0c1d</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600">守护进程ID:</span>
|
||||
<span class="font-medium" id="detail-daemonid">daemon-1</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600">版本:</span>
|
||||
<span class="font-medium" id="detail-version">1.19.2</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600">运行时间:</span>
|
||||
<span class="font-medium" id="detail-uptime">8小时24分钟</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold mb-3">玩家信息</h3>
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600">在线玩家:</span>
|
||||
<span class="font-medium" id="detail-players">12/20</span>
|
||||
</div>
|
||||
<div class="space-y-1 mt-2" id="detail-player-list">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm">Player1</span>
|
||||
<span class="text-xs text-gray-500">2小时前加入</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm">Player2</span>
|
||||
<span class="text-xs text-gray-500">1小时前加入</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm">Player3</span>
|
||||
<span class="text-xs text-gray-500">30分钟前加入</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="text-primary text-sm mt-2 hover:underline" id="action-view-all-players">
|
||||
查看全部 <i class="fa fa-angle-right ml-1"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold mb-3">性能指标</h3>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<div class="flex justify-between mb-1">
|
||||
<span class="text-sm text-gray-600">内存使用</span>
|
||||
<span class="text-sm font-medium" id="detail-memory">1536 MB / 2048 MB</span>
|
||||
</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2">
|
||||
<div class="bg-blue-600 h-2 rounded-full" style="width: 75%" id="detail-memory-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex justify-between mb-1">
|
||||
<span class="text-sm text-gray-600">CPU使用率</span>
|
||||
<span class="text-sm font-medium" id="detail-cpu">24%</span>
|
||||
</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2">
|
||||
<div class="bg-green-600 h-2 rounded-full" style="width: 24%" id="detail-cpu-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex justify-between mb-1">
|
||||
<span class="text-sm text-gray-600">网络流量</span>
|
||||
<span class="text-sm font-medium" id="detail-network">1.2 MB/s 入 / 0.8 MB/s 出</span>
|
||||
</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2">
|
||||
<div class="bg-purple-600 h-2 rounded-full" style="width: 40%" id="detail-network-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 服务器控制台 -->
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-semibold">服务器控制台</h3>
|
||||
<div class="flex space-x-2">
|
||||
<button class="btn btn-outline text-sm" id="action-clear-console">
|
||||
<i class="fa fa-trash mr-1"></i> 清空
|
||||
</button>
|
||||
<button class="btn btn-outline text-sm" id="action-refresh-console">
|
||||
<i class="fa fa-refresh mr-1"></i> 刷新
|
||||
</button>
|
||||
<button class="btn btn-outline text-sm" id="action-expand-console">
|
||||
<i class="fa fa-expand mr-1"></i> 全屏
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<div class="bg-gray-900 text-gray-100 rounded-t-md p-3 h-64 overflow-y-auto" id="server-console">
|
||||
<pre class="font-mono text-sm">[CCSNetwork]: 正在获取终端
|
||||
</div>
|
||||
|
||||
<!-- 命令输入 -->
|
||||
<div class="flex bg-gray-800 rounded-b-md">
|
||||
<div class="flex items-center px-3 text-gray-400">
|
||||
<span class="font-mono">></span>
|
||||
</div>
|
||||
<input type="text" id="server-command-input" placeholder="输入命令..." class="flex-grow bg-gray-800 text-gray-100 px-2 py-3 focus:outline-none font-mono">
|
||||
<button class="px-4 py-3 bg-primary text-white hover:bg-primary/90 transition-colors" id="action-send-command">
|
||||
<i class="fa fa-paper-plane"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 配置文件管理 -->
|
||||
<div class="p-6 border-t">
|
||||
<h3 class="text-lg font-semibold mb-4">配置文件管理</h3>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">文件名</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">大小</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">修改时间</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<i class="fa fa-file-text-o text-blue-500 mr-2"></i>
|
||||
<span>server.properties</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">2.4 KB</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">2025-06-07 12:30:45</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<button class="text-primary hover:text-primary/80 mr-3" onclick="editConfig('server.properties')">
|
||||
<i class="fa fa-edit mr-1"></i> 编辑
|
||||
</button>
|
||||
<button class="text-gray-500 hover:text-gray-700">
|
||||
<i class="fa fa-download mr-1"></i> 下载
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<i class="fa fa-file-text-o text-blue-500 mr-2"></i>
|
||||
<span>ops.json</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">0.5 KB</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">2025-06-07 10:15:20</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<button class="text-primary hover:text-primary/80 mr-3" onclick="editConfig('ops.json')">
|
||||
<i class="fa fa-edit mr-1"></i> 编辑
|
||||
</button>
|
||||
<button class="text-gray-500 hover:text-gray-700">
|
||||
<i class="fa fa-download mr-1"></i> 下载
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<i class="fa fa-file-text-o text-blue-500 mr-2"></i>
|
||||
<span>whitelist.json</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">1.2 KB</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">2025-06-07 09:45:10</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<button class="text-primary hover:text-primary/80 mr-3" onclick="editConfig('whitelist.json')">
|
||||
<i class="fa fa-edit mr-1"></i> 编辑
|
||||
</button>
|
||||
<button class="text-gray-500 hover:text-gray-700">
|
||||
<i class="fa fa-download mr-1"></i> 下载
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<button class="mt-4 btn btn-outline" onclick="uploadConfig()">
|
||||
<i class="fa fa-upload mr-1"></i> 上传配置文件
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 备份管理 -->
|
||||
<div class="p-6 border-t">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-semibold">备份管理</h3>
|
||||
<button class="btn btn-primary" id="action-create-backup">
|
||||
<i class="fa fa-plus mr-1"></i> 创建备份
|
||||
</button>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">备份名称</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">大小</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">创建时间</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<i class="fa fa-file-archive-o text-purple-500 mr-2"></i>
|
||||
<span>world_backup_20250607_1200</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">128 MB</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">2025-06-07 12:00:00</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<button class="text-secondary hover:text-secondary/80 mr-3" onclick="restoreBackup('world_backup_20250607_1200')">
|
||||
<i class="fa fa-undo mr-1"></i> 恢复
|
||||
</button>
|
||||
<button class="text-gray-500 hover:text-gray-700 mr-3">
|
||||
<i class="fa fa-download mr-1"></i> 下载
|
||||
</button>
|
||||
<button class="text-danger hover:text-danger/80" onclick="deleteBackup('world_backup_20250607_1200')">
|
||||
<i class="fa fa-trash mr-1"></i> 删除
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<i class="fa fa-file-archive-o text-purple-500 mr-2"></i>
|
||||
<span>world_backup_20250606_2359</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">126 MB</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">2025-06-06 23:59:59</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<button class="text-secondary hover:text-secondary/80 mr-3" onclick="restoreBackup('world_backup_20250606_2359')">
|
||||
<i class="fa fa-undo mr-1"></i> 恢复
|
||||
</button>
|
||||
<button class="text-gray-500 hover:text-gray-700 mr-3">
|
||||
<i class="fa fa-download mr-1"></i> 下载
|
||||
</button>
|
||||
<button class="text-danger hover:text-danger/80" onclick="deleteBackup('world_backup_20250606_2359')">
|
||||
<i class="fa fa-trash mr-1"></i> 删除
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<i class="fa fa-file-archive-o text-purple-500 mr-2"></i>
|
||||
<span>world_backup_20250606_1200</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">125 MB</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">2025-06-06 12:00:00</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<button class="text-secondary hover:text-secondary/80 mr-3" onclick="restoreBackup('world_backup_20250606_1200')">
|
||||
<i class="fa fa-undo mr-1"></i> 恢复
|
||||
</button>
|
||||
<button class="text-gray-500 hover:text-gray-700 mr-3">
|
||||
<i class="fa fa-download mr-1"></i> 下载
|
||||
</button>
|
||||
<button class="text-danger hover:text-danger/80" onclick="deleteBackup('world_backup_20250606_1200')">
|
||||
<i class="fa fa-trash mr-1"></i> 删除
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mt-4 text-sm text-gray-500">
|
||||
<i class="fa fa-info-circle mr-1"></i> 自动备份计划: 每天凌晨2点执行全量备份
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="bg-dark text-white py-4">
|
||||
<div class="container mx-auto px-4 text-center">
|
||||
<p>© 2025 MCSManager 服务器管理系统 | 版本 1.0.0</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- 加载遮罩层 -->
|
||||
<div class="loading-overlay hidden" id="loading-overlay">
|
||||
<div class="bg-white p-6 rounded-lg shadow-xl flex flex-col items-center">
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary mb-4"></div>
|
||||
<p class="text-lg font-medium" id="loading-message">正在处理...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 通知组件 -->
|
||||
<div class="notification notification-info" id="notification">
|
||||
<p id="notification-message">这是一条通知消息</p>
|
||||
</div>
|
||||
|
||||
<!-- 配置文件编辑模态框 -->
|
||||
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 hidden" id="config-modal">
|
||||
<div class="bg-white rounded-lg shadow-xl w-full max-w-4xl max-h-[80vh] flex flex-col">
|
||||
<div class="p-4 border-b flex justify-between items-center">
|
||||
<h3 class="text-lg font-semibold" id="config-modal-title">编辑配置文件</h3>
|
||||
<button class="text-gray-500 hover:text-gray-700" onclick="closeConfigModal()">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4 flex-grow overflow-y-auto">
|
||||
<div class="mb-3">
|
||||
<label class="block text-sm font-medium text-gray-700">文件名</label>
|
||||
<input type="text" id="config-filename" class="mt-1 block w-full px-3 py-2 border rounded-md shadow-sm" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">文件内容</label>
|
||||
<textarea id="config-content" rows="20" class="mt-1 block w-full px-3 py-2 border rounded-md shadow-sm font-mono text-sm"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border-t flex justify-end space-x-3">
|
||||
<button class="btn btn-outline" onclick="closeConfigModal()">取消</button>
|
||||
<button class="btn btn-primary" onclick="saveConfig()">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// DOM 元素
|
||||
const serverStatusEl = document.getElementById('detail-server-status');
|
||||
const serverNameEl = document.getElementById('detail-server-name');
|
||||
const serverIpEl = document.getElementById('detail-server-ip');
|
||||
const uuidEl = document.getElementById('detail-uuid');
|
||||
const daemonIdEl = document.getElementById('detail-daemonid');
|
||||
const versionEl = document.getElementById('detail-version');
|
||||
const uptimeEl = document.getElementById('detail-uptime');
|
||||
const playersEl = document.getElementById('detail-players');
|
||||
const playerListEl = document.getElementById('detail-player-list');
|
||||
const memoryEl = document.getElementById('detail-memory');
|
||||
const memoryBarEl = document.getElementById('detail-memory-bar');
|
||||
const cpuEl = document.getElementById('detail-cpu');
|
||||
const cpuBarEl = document.getElementById('detail-cpu-bar');
|
||||
const networkEl = document.getElementById('detail-network');
|
||||
const networkBarEl = document.getElementById('detail-network-bar');
|
||||
const serverConsoleEl = document.getElementById('server-console');
|
||||
const serverCommandInputEl = document.getElementById('server-command-input');
|
||||
const breadcrumbServerNameEl = document.getElementById('breadcrumb-server-name');
|
||||
|
||||
// 从URL获取参数
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const uuid = urlParams.get('uuid') || '5f8b4c2a-3d7e-4b1a-9c5d-6e7f8a9b0c1d';
|
||||
const daemonId = urlParams.get('daemonId') || 'daemon-1';
|
||||
|
||||
// 按钮
|
||||
const startServerBtn = document.getElementById('action-start-server');
|
||||
const stopServerBtn = document.getElementById('action-stop-server');
|
||||
const restartServerBtn = document.getElementById('action-restart-server');
|
||||
const sendCommandBtn = document.getElementById('action-send-command');
|
||||
const clearConsoleBtn = document.getElementById('action-clear-console');
|
||||
const refreshConsoleBtn = document.getElementById('action-refresh-console');
|
||||
const expandConsoleBtn = document.getElementById('action-expand-console');
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 加载服务器详情
|
||||
loadServerDetails();
|
||||
|
||||
// 添加事件监听器
|
||||
startServerBtn.addEventListener('click', startServer);
|
||||
stopServerBtn.addEventListener('click', stopServer);
|
||||
restartServerBtn.addEventListener('click', restartServer);
|
||||
sendCommandBtn.addEventListener('click', sendCommand);
|
||||
serverCommandInputEl.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
sendCommand();
|
||||
}
|
||||
});
|
||||
clearConsoleBtn.addEventListener('click', clearConsole);
|
||||
refreshConsoleBtn.addEventListener('click', refreshConsole);
|
||||
expandConsoleBtn.addEventListener('click', toggleFullscreenConsole);
|
||||
|
||||
// 定时刷新服务器状态和控制台
|
||||
setInterval(loadServerDetails, 5000);
|
||||
});
|
||||
|
||||
let isLoading = true; // 添加一个标志变量
|
||||
|
||||
function loadServerDetails() {
|
||||
if (!isLoading) return; // 如果不是第一次加载,直接返回
|
||||
|
||||
showLoading("正在加载服务器信息...");
|
||||
isLoading = false; // 设置标志变量为false
|
||||
|
||||
fetch(`proxy.php?path=instance&uuid=${uuid}&daemonId=${daemonId}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`请求失败,状态码: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('后端返回的数据:', data); // 打印返回的数据
|
||||
if (!data || !data.data) {
|
||||
throw new Error('无效的服务器数据');
|
||||
}
|
||||
|
||||
const server = data.data;
|
||||
|
||||
// 更新基本信息
|
||||
serverNameEl.textContent = server.config.nickname || '未知服务器';
|
||||
breadcrumbServerNameEl.textContent = serverNameEl.textContent;
|
||||
|
||||
serverStatusEl.textContent = server.status === 1 ? '在线' : '离线';
|
||||
serverStatusEl.className = `badge ${server.status === 1 ? 'badge-online' : 'badge-offline'}`;
|
||||
|
||||
serverIpEl.textContent = server.config.pingConfig.ip || '未知IP';
|
||||
uuidEl.textContent = server.instanceUuid || '未知UUID';
|
||||
daemonIdEl.textContent = daemonId;
|
||||
versionEl.textContent = server.info.version || '未知版本';
|
||||
uptimeEl.textContent = server.processInfo.elapsed || '未知运行时间';
|
||||
playersEl.textContent = `${server.info.currentPlayers || 0}/${server.info.maxPlayers || 0}`;
|
||||
|
||||
hideLoading(); // 隐藏加载遮罩
|
||||
// 更新玩家列表
|
||||
updatePlayerList(server.info.playersChart || []);
|
||||
|
||||
// 更新性能指标
|
||||
updatePerformanceMetrics(server);
|
||||
|
||||
// 获取并更新控制台日志
|
||||
fetchConsoleLog();
|
||||
|
||||
// 更新按钮状态
|
||||
updateButtonStates();
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取服务器详情失败:', error);
|
||||
showNotification('获取服务器详情失败', 'error');
|
||||
hideLoading(); // 隐藏加载遮罩
|
||||
});
|
||||
}
|
||||
|
||||
function updatePlayerList(players) {
|
||||
playerListEl.innerHTML = '';
|
||||
|
||||
if (!players || players.length === 0) {
|
||||
const emptyEl = document.createElement('div');
|
||||
emptyEl.className = 'text-sm text-gray-500 italic';
|
||||
emptyEl.textContent = '没有在线玩家';
|
||||
playerListEl.appendChild(emptyEl);
|
||||
return;
|
||||
}
|
||||
|
||||
players.forEach(player => {
|
||||
const playerEl = document.createElement('div');
|
||||
playerEl.className = 'flex items-center justify-between';
|
||||
playerEl.innerHTML = `
|
||||
<span class="text-sm">${player.name}</span>
|
||||
<span class="text-xs text-gray-500">${player.joinTime || '刚刚加入'}</span>
|
||||
`;
|
||||
playerListEl.appendChild(playerEl);
|
||||
});
|
||||
}
|
||||
|
||||
function updatePerformanceMetrics(server) {
|
||||
const memoryUsed = server.processInfo.memory || 0;
|
||||
const memoryTotal = 1024; // 假设总内存为1024MB
|
||||
const memoryPercent = Math.round((memoryUsed / memoryTotal) * 100);
|
||||
|
||||
memoryEl.textContent = `${memoryUsed} MB / ${memoryTotal} MB`;
|
||||
memoryBarEl.style.width = `${memoryPercent}%`;
|
||||
|
||||
const cpuPercent = server.processInfo.cpu || 0;
|
||||
cpuEl.textContent = `${cpuPercent}%`;
|
||||
cpuBarEl.style.width = `${cpuPercent}%`;
|
||||
|
||||
const networkIn = (server.processInfo.networkIn || 0).toFixed(1);
|
||||
const networkOut = (server.processInfo.networkOut || 0).toFixed(1);
|
||||
const networkPercent = Math.round(((networkIn + networkOut) / 10) * 100); // 假设最大10MB/s
|
||||
|
||||
networkEl.textContent = `${networkIn} MB/s 入 / ${networkOut} MB/s 出`;
|
||||
networkBarEl.style.width = `${Math.min(100, networkPercent)}%`;
|
||||
}
|
||||
|
||||
function updateButtonStates() {
|
||||
const isOnline = serverStatusEl.textContent.trim().toLowerCase() === '在线';
|
||||
|
||||
startServerBtn.disabled = isOnline;
|
||||
stopServerBtn.disabled = !isOnline;
|
||||
restartServerBtn.disabled = !isOnline;
|
||||
}
|
||||
|
||||
async function fetchConsoleLog() {
|
||||
try {
|
||||
const response = await fetch(`proxy.php?path=protected_instance/outputlog&uuid=${uuid}&daemonId=${daemonId}&size=4096`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`请求失败,状态码: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
if (data && data.data) {
|
||||
serverConsoleEl.innerHTML = '';
|
||||
|
||||
const lines = data.data.split('\n');
|
||||
|
||||
lines.forEach(line => {
|
||||
const lineEl = document.createElement('div');
|
||||
lineEl.className = 'console-line font-mono text-sm';
|
||||
|
||||
if (line.includes('[INFO]')) {
|
||||
lineEl.classList.add('text-gray-700');
|
||||
} else if (line.includes('[WARNING]')) {
|
||||
lineEl.classList.add('text-yellow-600');
|
||||
} else if (line.includes('[ERROR]')) {
|
||||
lineEl.classList.add('text-red-600');
|
||||
} else if (line.includes('issued server command')) {
|
||||
lineEl.classList.add('text-blue-600');
|
||||
} else if (line.includes('joined the game') || line.includes('left the game')) {
|
||||
lineEl.classList.add('text-green-600');
|
||||
}
|
||||
|
||||
lineEl.textContent = line;
|
||||
serverConsoleEl.appendChild(lineEl);
|
||||
});
|
||||
|
||||
serverConsoleEl.scrollTop = serverConsoleEl.scrollHeight;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取控制台日志失败:', error);
|
||||
}
|
||||
}
|
||||
function startServer() {
|
||||
showLoading("正在启动服务器...");
|
||||
|
||||
fetch(`proxy.php?path=protected_instance/open&uuid=${uuid}&daemonId=${daemonId}`, {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// 获取并打印终端日志
|
||||
fetchConsoleLog();
|
||||
|
||||
// 如果有终端输出,无论状态如何都打印
|
||||
console.log('服务器启动响应:', data);
|
||||
|
||||
if (data.code === 500) {
|
||||
// 处理服务器返回500错误的情况
|
||||
console.error('服务器返回错误: ', data.message);
|
||||
hideLoading();
|
||||
}
|
||||
|
||||
showNotification('启动命令已发送', 'success');
|
||||
const checkInterval = setInterval(() => {
|
||||
fetch(`proxy.php?path=instance&uuid=${uuid}&daemonId=${daemonId}`)
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
// 获取并打印终端日志
|
||||
fetchConsoleLog();
|
||||
hideLoading();
|
||||
console.log('服务器状态响应:', res);
|
||||
|
||||
if (res.data?.status !== 2) { // 不是启动中状态
|
||||
clearInterval(checkInterval);
|
||||
loadServerDetails();
|
||||
hideLoading();
|
||||
}
|
||||
});
|
||||
}, 2000);
|
||||
})
|
||||
.catch(error => {
|
||||
// 获取并打印终端日志
|
||||
fetchConsoleLog();
|
||||
|
||||
console.error('启动失败错误:', error);
|
||||
showNotification(`启动失败: ${error.message}`, 'error');
|
||||
hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function stopServer() {
|
||||
if (!confirm('确定要停止服务器吗?')) return;
|
||||
|
||||
showLoading("正在停止服务器...");
|
||||
|
||||
fetch(`proxy.php?path=protected_instance/stop&uuid=${uuid}&daemonId=${daemonId}`, {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
showNotification('服务器停止请求已发送', 'success');
|
||||
setTimeout(loadServerDetails, 2000); // 延迟刷新状态
|
||||
} else {
|
||||
throw new Error(data.message || '停止失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('停止服务器失败:', error);
|
||||
showNotification('停止服务器失败: ' + error.message, 'error');
|
||||
hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
function restartServer() {
|
||||
if (!confirm('确定要重启服务器吗?')) return;
|
||||
|
||||
showLoading("正在重启服务器...");
|
||||
|
||||
fetch(`proxy.php?path=protected_instance/restart&uuid=${uuid}&daemonId=${daemonId}`, {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
showNotification('服务器重启请求已发送', 'success');
|
||||
setTimeout(loadServerDetails, 2000); // 延迟刷新状态
|
||||
} else {
|
||||
throw new Error(data.message || '重启失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('重启服务器失败:', error);
|
||||
showNotification('重启服务器失败: ' + error.message, 'error');
|
||||
hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
function sendCommand() {
|
||||
const command = serverCommandInputEl.value.trim();
|
||||
if (!command) return;
|
||||
|
||||
showLoading("正在发送命令...");
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('uuid', uuid);
|
||||
formData.append('daemonId', daemonId);
|
||||
formData.append('command', command);
|
||||
|
||||
fetch(`proxy.php?path=protected_instance/command&uuid=${uuid}&daemonId=${daemonId}&command=${command}`, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
serverCommandInputEl.value = '';
|
||||
showNotification('命令已发送', 'success');
|
||||
setTimeout(fetchConsoleLog, 500); // 延迟刷新控制台
|
||||
} else {
|
||||
throw new Error(data.message || '命令发送失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('发送命令失败:', error);
|
||||
showNotification('发送命令失败: ' + error.message, 'error');
|
||||
hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
function clearConsole() {
|
||||
serverConsoleEl.innerHTML = '';
|
||||
}
|
||||
|
||||
function refreshConsole() {
|
||||
fetchConsoleLog();
|
||||
}
|
||||
|
||||
function toggleFullscreenConsole() {
|
||||
const consoleContainer = serverConsoleEl.parentElement;
|
||||
consoleContainer.classList.toggle('fixed');
|
||||
consoleContainer.classList.toggle('inset-0');
|
||||
consoleContainer.classList.toggle('z-40');
|
||||
consoleContainer.classList.toggle('bg-gray-900');
|
||||
consoleContainer.classList.toggle('p-6');
|
||||
|
||||
serverConsoleEl.classList.toggle('h-64');
|
||||
serverConsoleEl.classList.toggle('h-full');
|
||||
|
||||
if (consoleContainer.classList.contains('fixed')) {
|
||||
expandConsoleBtn.innerHTML = '<i class="fa fa-compress mr-1"></i> 退出全屏';
|
||||
} else {
|
||||
expandConsoleBtn.innerHTML = '<i class="fa fa-expand mr-1"></i> 全屏';
|
||||
}
|
||||
}
|
||||
|
||||
function showLoading(message) {
|
||||
const loadingOverlay = document.getElementById('loading-overlay');
|
||||
const loadingMessage = document.getElementById('loading-message');
|
||||
|
||||
loadingMessage.textContent = message;
|
||||
loadingOverlay.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
const loadingOverlay = document.getElementById('loading-overlay');
|
||||
loadingOverlay.classList.add('hidden');
|
||||
}
|
||||
|
||||
function showNotification(message, type = 'info') {
|
||||
const notification = document.getElementById('notification');
|
||||
const notificationMessage = document.getElementById('notification-message');
|
||||
|
||||
// 设置通知类型
|
||||
notification.className = 'notification fixed bottom-4 right-4 p-4 rounded-lg shadow-lg transform transition-all duration-300 opacity-0 translate-y-4';
|
||||
notification.classList.add(`bg-${type === 'success' ? 'green' : type === 'error' ? 'red' : 'blue'}-500`, `text-white`);
|
||||
|
||||
// 设置通知内容
|
||||
notificationMessage.textContent = message;
|
||||
|
||||
// 显示通知
|
||||
setTimeout(() => {
|
||||
notification.classList.remove('opacity-0', 'translate-y-4');
|
||||
}, 10);
|
||||
|
||||
// 3秒后隐藏通知
|
||||
setTimeout(() => {
|
||||
notification.classList.add('opacity-0', 'translate-y-4');
|
||||
}, 3000);
|
||||
}
|
||||
</script>
|
678
console/ins/indexa.php
Executable file
678
console/ins/indexa.php
Executable file
@ -0,0 +1,678 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MCSManager 实例管理</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#165DFF',
|
||||
secondary: '#36D399',
|
||||
danger: '#F87272',
|
||||
warning: '#FBBD23',
|
||||
dark: '#1E293B',
|
||||
light: '#F8FAFC'
|
||||
},
|
||||
fontFamily: {
|
||||
inter: ['Inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.stat-card {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.stat-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
@layer utilities {
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
.server-card {
|
||||
@apply bg-white rounded-lg shadow-md p-4 mb-4 transition-all duration-300 hover:shadow-lg;
|
||||
}
|
||||
.server-status {
|
||||
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
|
||||
}
|
||||
.btn {
|
||||
@apply inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2;
|
||||
}
|
||||
.btn-primary {
|
||||
@apply bg-primary text-white hover:bg-primary/90 focus:ring-primary/50;
|
||||
}
|
||||
.btn-secondary {
|
||||
@apply bg-secondary text-white hover:bg-secondary/90 focus:ring-secondary/50;
|
||||
}
|
||||
.btn-danger {
|
||||
@apply bg-danger text-white hover:bg-danger/90 focus:ring-danger/50;
|
||||
}
|
||||
.btn-warning {
|
||||
@apply bg-warning text-white hover:bg-warning/90 focus:ring-warning/50;
|
||||
}
|
||||
.btn-outline {
|
||||
@apply border-gray-300 text-gray-700 hover:bg-gray-50 focus:ring-primary/50;
|
||||
}
|
||||
.badge {
|
||||
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
|
||||
}
|
||||
.badge-online {
|
||||
@apply bg-green-100 text-green-800;
|
||||
}
|
||||
.badge-offline {
|
||||
@apply bg-gray-100 text-gray-800;
|
||||
}
|
||||
.badge-starting {
|
||||
@apply bg-blue-100 text-blue-800;
|
||||
}
|
||||
.badge-stopping {
|
||||
@apply bg-yellow-100 text-yellow-800;
|
||||
}
|
||||
.console-line {
|
||||
@apply whitespace-pre font-mono text-sm;
|
||||
}
|
||||
.console-line-info {
|
||||
@apply text-gray-300;
|
||||
}
|
||||
.console-line-warning {
|
||||
@apply text-yellow-300;
|
||||
}
|
||||
.console-line-error {
|
||||
@apply text-red-400;
|
||||
}
|
||||
.console-line-command {
|
||||
@apply text-green-400;
|
||||
}
|
||||
.console-line-player {
|
||||
@apply text-blue-300;
|
||||
}
|
||||
.loading-overlay {
|
||||
@apply fixed inset-0 bg-black/50 flex items-center justify-center z-50;
|
||||
}
|
||||
.notification {
|
||||
@apply fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transition-all duration-300 transform translate-y-20 opacity-0;
|
||||
}
|
||||
.notification-visible {
|
||||
@apply translate-y-0 opacity-100;
|
||||
}
|
||||
.notification-success {
|
||||
@apply bg-green-500 text-white;
|
||||
}
|
||||
.notification-error {
|
||||
@apply bg-red-500 text-white;
|
||||
}
|
||||
.notification-info {
|
||||
@apply bg-blue-500 text-white;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 font-inter text-gray-800 min-h-screen flex flex-col">
|
||||
<!-- 顶部导航栏 -->
|
||||
<header class="bg-primary text-white shadow-md sticky top-0 z-50">
|
||||
<div class="container mx-auto px-4 py-3 flex justify-between items-center">
|
||||
<div class="flex items-center space-x-2">
|
||||
<i class="fa fa-server text-xl"></i>
|
||||
<h1 class="text-xl font-bold">CCS实例管理</h1>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<main class="flex-grow container mx-auto px-4 py-6">
|
||||
<!-- 实例信息头部 -->
|
||||
<div class="mb-6">
|
||||
<div class="bg-white rounded-xl shadow-lg p-6 mb-6">
|
||||
<div class="flex flex-col md:flex-row md:items-center justify-between mb-4">
|
||||
<div>
|
||||
<h2 class="text-[clamp(1.5rem,3vw,2rem)] font-bold text-gray-800" id="instanceName">加载中...</h2>
|
||||
<p class="text-gray-500 mt-1" id="instanceDescription">加载中...</p>
|
||||
</div>
|
||||
<div class="flex items-center mt-4 md:mt-0 space-x-3">
|
||||
<span id="instanceStatus" class="px-3 py-1 rounded-full text-sm font-medium bg-gray-200 text-gray-700">
|
||||
<i class="fa fa-circle-o-notch fa-spin mr-1"></i>加载中
|
||||
</span>
|
||||
<div class="flex space-x-2">
|
||||
<button id="startBtn" class="px-4 py-2 bg-secondary text-white rounded-lg shadow hover:bg-secondary/90 transition-all disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
<i class="fa fa-play mr-1"></i>启动
|
||||
</button>
|
||||
<button id="stopBtn" class="px-4 py-2 bg-danger text-white rounded-lg shadow hover:bg-danger/90 transition-all disabled:opacity-50 disabled:cursor-not-allowed hidden">
|
||||
<i class="fa fa-stop mr-1"></i>停止
|
||||
</button>
|
||||
<button id="restartBtn" class="px-4 py-2 bg-warning text-white rounded-lg shadow hover:bg-warning/90 transition-all disabled:opacity-50 disabled:cursor-not-allowed hidden">
|
||||
<i class="fa fa-refresh mr-1"></i>重启
|
||||
</button>
|
||||
<button id="killBtn" class="px-4 py-2 bg-gray-700 text-white rounded-lg shadow hover:bg-gray-800 transition-all disabled:opacity-50 disabled:cursor-not-allowed hidden">
|
||||
<i class="fa fa-power-off mr-1"></i>强制停止
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 基本信息卡片 -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div class="stat-card bg-gray-50 rounded-lg p-4 border border-gray-100">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-500">CPU 使用率</p>
|
||||
<p class="text-2xl font-bold mt-1" id="cpuUsage">--%</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center">
|
||||
<i class="fa fa-microchip text-primary text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 w-full bg-gray-200 rounded-full h-2">
|
||||
<div id="cpuUsageBar" class="bg-primary h-2 rounded-full" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card bg-gray-50 rounded-lg p-4 border border-gray-100">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-500">内存使用率</p>
|
||||
<p class="text-2xl font-bold mt-1" id="memoryUsage">-- MB / -- MB</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 rounded-full bg-secondary/10 flex items-center justify-center">
|
||||
<i class="fa fa-memory text-secondary text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 w-full bg-gray-200 rounded-full h-2">
|
||||
<div id="memoryUsageBar" class="bg-secondary h-2 rounded-full" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card bg-gray-50 rounded-lg p-4 border border-gray-100">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-500">网络带宽</p>
|
||||
<p class="text-2xl font-bold mt-1" id="networkUsage">-- KB/s</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 rounded-full bg-warning/10 flex items-center justify-center">
|
||||
<i class="fa fa-wifi text-warning text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 w-full bg-gray-200 rounded-full h-2">
|
||||
<div id="networkUsageBar" class="bg-warning h-2 rounded-full" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card bg-gray-50 rounded-lg p-4 border border-gray-100">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-500">在线时间</p>
|
||||
<p class="text-2xl font-bold mt-1" id="uptime">--</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center">
|
||||
<i class="fa fa-clock-o text-primary text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center text-sm text-gray-500">
|
||||
<i class="fa fa-calendar-o mr-1"></i>
|
||||
<span id="lastUpdate">最后更新: --</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-semibold">服务器控制台</h3>
|
||||
<div class="flex space-x-2">
|
||||
<button class="btn btn-outline text-sm" id="clearTerminalBtn">
|
||||
<i class="fa fa-trash mr-1"></i> 清空
|
||||
</button>
|
||||
<button class="btn btn-outline text-sm" id="action-refresh-console">
|
||||
<i class="fa fa-refresh mr-1"></i> 刷新
|
||||
</button>
|
||||
<button class="btn btn-outline text-sm" id="copyTerminalBtn">
|
||||
<i class="fa fa-expand mr-1"></i> 复制
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<div class="bg-gray-900 text-gray-100 rounded-t-md p-3 h-64 overflow-y-auto" id="terminalOutput">
|
||||
<pre class="font-mono text-sm">[CCSNetwork]: 正在获取终端
|
||||
</div>
|
||||
<div class="flex bg-gray-800 rounded-b-md w-full">
|
||||
<div class="flex items-center px-3 text-gray-400">
|
||||
<span class="font-mono">></span>
|
||||
</div>
|
||||
<input type="text" id="commandInput" placeholder="输入命令..." class="flex-grow bg-gray-800 text-gray-100 px-2 py-3 focus:outline-none font-mono w-full">
|
||||
<button class="px-4 py-3 bg-primary text-white hover:bg-primary/90 transition-colors" id="sendCommandBtn">
|
||||
<i class="fa fa-paper-plane"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<!-- 加载遮罩层 -->
|
||||
<div class="loading-overlay hidden" id="loading-overlay">
|
||||
<div class="bg-white p-6 rounded-lg shadow-xl flex flex-col items-center">
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary mb-4"></div>
|
||||
<p class="text-lg font-medium" id="loading-message">正在处理...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 通知组件 -->
|
||||
<div class="notification notification-info" id="notification">
|
||||
<p id="notification-message">这是一条通知消息</p>
|
||||
</div>
|
||||
<!-- 页脚 -->
|
||||
<footer class="bg-gray-800 text-white py-6">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="flex flex-col md:flex-row justify-between items-center">
|
||||
<div class="mb-4 md:mb-0">
|
||||
<div class="flex items-center space-x-2">
|
||||
<i class="fa fa-server"></i>
|
||||
<span class="font-bold">CCS实例管理</span>
|
||||
</div>
|
||||
<p class="text-gray-400 text-sm mt-1">CCS实例管理面板</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6 pt-6 border-t border-gray-700 text-center text-gray-400 text-sm">
|
||||
© 2025 圆周云境 | 版本 2.1.3
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script>
|
||||
// DOM 元素
|
||||
const instanceNameEl = document.getElementById('instanceName');
|
||||
const instanceDescriptionEl = document.getElementById('instanceDescription');
|
||||
const instanceStatusEl = document.getElementById('instanceStatus');
|
||||
const cpuUsageEl = document.getElementById('cpuUsage');
|
||||
const cpuUsageBarEl = document.getElementById('cpuUsageBar');
|
||||
const memoryUsageEl = document.getElementById('memoryUsage');
|
||||
const memoryUsageBarEl = document.getElementById('memoryUsageBar');
|
||||
const networkUsageEl = document.getElementById('networkUsage');
|
||||
const networkUsageBarEl = document.getElementById('networkUsageBar');
|
||||
const uptimeEl = document.getElementById('uptime');
|
||||
const lastUpdateEl = document.getElementById('lastUpdate');
|
||||
const terminalOutputEl = document.getElementById('terminalOutput');
|
||||
const commandInputEl = document.getElementById('commandInput');
|
||||
const sendCommandBtn = document.getElementById('sendCommandBtn');
|
||||
const clearTerminalBtn = document.getElementById('clearTerminalBtn');
|
||||
const copyTerminalBtn = document.getElementById('copyTerminalBtn');
|
||||
|
||||
// 从URL获取参数
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const uuid = urlParams.get('uuid') || '5f8b4c2a-3d7e-4b1a-9c5d-6e7f8a9b0c1d';
|
||||
const daemonId = urlParams.get('daemonId') || 'daemon-1';
|
||||
|
||||
// 按钮
|
||||
const startBtn = document.getElementById('startBtn');
|
||||
const stopBtn = document.getElementById('stopBtn');
|
||||
const restartBtn = document.getElementById('restartBtn');
|
||||
const killBtn = document.getElementById('killBtn');
|
||||
|
||||
// 标志变量,控制加载遮罩的显示
|
||||
let isFirstLoad = true;
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
showLoading("正在加载服务器信息...");
|
||||
|
||||
// 加载服务器详情
|
||||
loadServerDetails();
|
||||
fetchConsoleLog();
|
||||
|
||||
// 添加事件监听器
|
||||
startBtn.addEventListener('click', startServer);
|
||||
stopBtn.addEventListener('click', stopServer);
|
||||
restartBtn.addEventListener('click', restartServer);
|
||||
killBtn.addEventListener('click', killServer);
|
||||
sendCommandBtn.addEventListener('click', sendCommand);
|
||||
commandInputEl.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
sendCommand();
|
||||
}
|
||||
});
|
||||
if (clearTerminalBtn) {
|
||||
clearTerminalBtn.addEventListener('click', clearTerminal);
|
||||
}
|
||||
if (copyTerminalBtn) {
|
||||
copyTerminalBtn.addEventListener('click', copyTerminal);
|
||||
}
|
||||
|
||||
// 定时刷新服务器状态和控制台
|
||||
setInterval(loadServerDetails, 5000);
|
||||
setInterval(fetchConsoleLog, 2000);
|
||||
});
|
||||
|
||||
function loadServerDetails() {
|
||||
fetch(`proxy.php?path=instance&uuid=${uuid}&daemonId=${daemonId}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`请求失败,状态码: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('后端返回的数据:', data); // 打印返回的数据
|
||||
if (!data || !data.data) {
|
||||
throw new Error('无效的服务器数据');
|
||||
}
|
||||
|
||||
const server = data.data;
|
||||
|
||||
// 更新基本信息
|
||||
instanceNameEl.textContent = server.config.nickname || '未知服务器';
|
||||
instanceDescriptionEl.textContent = server.config.description || '无描述';
|
||||
|
||||
instanceStatusEl.textContent = server.status === 1 ? '在线' : '离线';
|
||||
instanceStatusEl.className = `px-3 py-1 rounded-full text-sm font-medium bg-${server.status === 1 ? 'success' : 'danger'}-500 text-white`;
|
||||
|
||||
cpuUsageEl.textContent = `${server.processInfo.cpu || 0}%`;
|
||||
cpuUsageBarEl.style.width = `${server.processInfo.cpu || 0}%`;
|
||||
|
||||
const memoryUsed = server.processInfo.memory || 0;
|
||||
const memoryTotal = 1024; // 假设总内存为1024MB
|
||||
const memoryPercent = Math.round((memoryUsed / memoryTotal) * 100);
|
||||
|
||||
memoryUsageEl.textContent = `${memoryUsed} MB / ${memoryTotal} MB`;
|
||||
memoryUsageBarEl.style.width = `${memoryPercent}%`;
|
||||
|
||||
const networkIn = (server.processInfo.networkIn || 0).toFixed(1);
|
||||
const networkOut = (server.processInfo.networkOut || 0).toFixed(1);
|
||||
const networkPercent = Math.round(((networkIn + networkOut) / 10) * 100); // 假设最大10MB/s
|
||||
|
||||
networkUsageEl.textContent = `${networkIn} MB/s 入 / ${networkOut} MB/s 出`;
|
||||
networkUsageBarEl.style.width = `${Math.min(100, networkPercent)}%`;
|
||||
|
||||
uptimeEl.textContent = server.processInfo.elapsed || '未知运行时间';
|
||||
lastUpdateEl.textContent = `最后更新: ${new Date().toLocaleString()}`;
|
||||
|
||||
if (isFirstLoad) {
|
||||
hideLoading(); // 隐藏加载遮罩
|
||||
isFirstLoad = false; // 设置标志变量为false
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取服务器详情失败:', error);
|
||||
showNotification('获取服务器详情失败', 'error');
|
||||
if (isFirstLoad) {
|
||||
hideLoading(); // 隐藏加载遮罩
|
||||
isFirstLoad = false; // 设置标志变量为false
|
||||
}
|
||||
});
|
||||
}
|
||||
let isFirstFetch = true; // 添加一个标志变量,用于控制第一次加载
|
||||
|
||||
async function fetchConsoleLog() {
|
||||
try {
|
||||
const response = await fetch(`proxy.php?path=protected_instance/outputlog&uuid=${uuid}&daemonId=${daemonId}&size=4096`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`请求失败,状态码: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
if (data && data.data) {
|
||||
terminalOutputEl.innerHTML = '';
|
||||
|
||||
const lines = data.data.split('\n');
|
||||
|
||||
lines.forEach(line => {
|
||||
const lineEl = document.createElement('div');
|
||||
lineEl.className = 'console-line font-mono text-sm';
|
||||
|
||||
if (line.includes('[INFO]')) {
|
||||
lineEl.classList.add('text-gray-700');
|
||||
} else if (line.includes('[WARNING]')) {
|
||||
lineEl.classList.add('text-yellow-600');
|
||||
} else if (line.includes('[ERROR]')) {
|
||||
lineEl.classList.add('text-red-600');
|
||||
} else if (line.includes('issued server command')) {
|
||||
lineEl.classList.add('text-blue-600');
|
||||
} else if (line.includes('joined the game') || line.includes('left the game')) {
|
||||
lineEl.classList.add('text-green-600');
|
||||
}
|
||||
|
||||
lineEl.textContent = line;
|
||||
terminalOutputEl.appendChild(lineEl);
|
||||
});
|
||||
|
||||
// 只在第一次加载时滚动到最底部
|
||||
if (isFirstFetch) {
|
||||
terminalOutputEl.scrollTop = terminalOutputEl.scrollHeight;
|
||||
isFirstFetch = false; // 设置标志变量为false
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取控制台日志失败:', error);
|
||||
} finally {
|
||||
if (isFirstLoad) {
|
||||
hideLoading(); // 确保在所有情况下都隐藏加载遮罩
|
||||
isFirstLoad = false; // 设置标志变量为false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startServer() {
|
||||
showLoading("正在启动服务器...");
|
||||
|
||||
fetch(`proxy.php?path=protected_instance/open&uuid=${uuid}&daemonId=${daemonId}`, {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// 获取并打印终端日志
|
||||
fetchConsoleLog();
|
||||
|
||||
// 如果有终端输出,无论状态如何都打印
|
||||
console.log('服务器启动响应:', data);
|
||||
|
||||
if (data.code === 500) {
|
||||
// 处理服务器返回500错误的情况
|
||||
console.error('服务器返回错误: ', data.message);
|
||||
hideLoading();
|
||||
}
|
||||
|
||||
showNotification('启动命令已发送', 'success');
|
||||
const checkInterval = setInterval(() => {
|
||||
fetch(`proxy.php?path=instance&uuid=${uuid}&daemonId=${daemonId}`)
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
// 获取并打印终端日志
|
||||
fetchConsoleLog();
|
||||
terminalOutputEl.scrollTop = terminalOutputEl.scrollHeight;
|
||||
hideLoading();
|
||||
console.log('服务器状态响应:', res);
|
||||
|
||||
if (res.data?.status !== 2) { // 不是启动中状态
|
||||
clearInterval(checkInterval);
|
||||
loadServerDetails();
|
||||
hideLoading();
|
||||
}
|
||||
});
|
||||
}, 2000);
|
||||
})
|
||||
.catch(error => {
|
||||
// 获取并打印终端日志
|
||||
fetchConsoleLog();
|
||||
terminalOutputEl.scrollTop = terminalOutputEl.scrollHeight;
|
||||
console.error('启动失败错误:', error);
|
||||
showNotification(`启动失败: ${error.message}`, 'error');
|
||||
hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
function stopServer() {
|
||||
if (!confirm('确定要停止服务器吗?')) return;
|
||||
|
||||
showLoading("正在停止服务器...");
|
||||
|
||||
fetch(`proxy.php?path=protected_instance/stop&uuid=${uuid}&daemonId=${daemonId}`, {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
showNotification('服务器停止请求已发送', 'success');
|
||||
setTimeout(loadServerDetails, 2000); // 延迟刷新状态
|
||||
fetchConsoleLog();
|
||||
} else {
|
||||
throw new Error(data.message || '停止失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('停止服务器失败:', error);
|
||||
showNotification('停止服务器失败: ' + error.message, 'error');
|
||||
fetchConsoleLog();
|
||||
hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
function restartServer() {
|
||||
if (!confirm('确定要重启服务器吗?')) return;
|
||||
|
||||
showLoading("正在重启服务器...");
|
||||
|
||||
fetch(`proxy.php?path=protected_instance/restart&uuid=${uuid}&daemonId=${daemonId}`, {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
showNotification('服务器重启请求已发送', 'success');
|
||||
setTimeout(loadServerDetails, 2000); // 延迟刷新状态
|
||||
} else {
|
||||
throw new Error(data.message || '重启失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('重启服务器失败:', error);
|
||||
showNotification('重启服务器失败: ' + error.message, 'error');
|
||||
hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
function killServer() {
|
||||
if (!confirm('确定要强制停止服务器吗?')) return;
|
||||
|
||||
showLoading("正在强制停止服务器...");
|
||||
|
||||
fetch(`proxy.php?path=protected_instance/kill&uuid=${uuid}&daemonId=${daemonId}`, {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
showNotification('服务器强制停止请求已发送', 'success');
|
||||
setTimeout(loadServerDetails, 2000); // 延迟刷新状态
|
||||
} else {
|
||||
throw new Error(data.message || '强制停止失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('强制停止服务器失败:', error);
|
||||
showNotification('强制停止服务器失败: ' + error.message, 'error');
|
||||
hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
function sendCommand() {
|
||||
const command = commandInputEl.value.trim();
|
||||
if (!command) return;
|
||||
|
||||
showLoading("正在发送命令...");
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('uuid', uuid);
|
||||
formData.append('daemonId', daemonId);
|
||||
formData.append('command', command);
|
||||
fetch(`proxy.php?path=protected_instance/command&uuid=${uuid}&daemonId=${daemonId}&command=${command}`, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
commandInputEl.value = '';
|
||||
showNotification('命令已发送', 'success');
|
||||
fetchConsoleLog();terminalOutputEl.scrollTop = terminalOutputEl.scrollHeight;
|
||||
} else {
|
||||
throw new Error(data.message || '命令发送失败');
|
||||
fetchConsoleLog();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('发送命令失败:', error);
|
||||
showNotification('错误: ' + error.message, 'error');
|
||||
fetchConsoleLog();
|
||||
hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
function clearTerminal() {
|
||||
terminalOutputEl.innerHTML = '';
|
||||
}
|
||||
|
||||
function copyTerminal() {
|
||||
navigator.clipboard.writeText(terminalOutputEl.textContent)
|
||||
.then(() => {
|
||||
showNotification('终端内容已复制', 'success');
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('复制失败', err);
|
||||
showNotification('复制失败', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
function showLoading(message) {
|
||||
const loadingOverlay = document.getElementById('loading-overlay');
|
||||
const loadingMessage = document.getElementById('loading-message');
|
||||
|
||||
if (loadingOverlay && loadingMessage) {
|
||||
loadingMessage.textContent = message;
|
||||
loadingOverlay.classList.remove('hidden'); // 显示遮罩
|
||||
}
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
const loadingOverlay = document.getElementById('loading-overlay');
|
||||
if (loadingOverlay) {
|
||||
loadingOverlay.classList.add('hidden'); // 隐藏遮罩
|
||||
}
|
||||
}
|
||||
function showNotification(message, type = 'info') {
|
||||
const notification = document.getElementById('notification');
|
||||
const notificationMessage = document.getElementById('notification-message');
|
||||
|
||||
if (!notification || !notificationMessage) {
|
||||
console.error('Notification elements not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置通知内容
|
||||
notificationMessage.textContent = message;
|
||||
|
||||
// 设置通知类型
|
||||
notification.classList.remove('notification-success', 'notification-error', 'notification-info');
|
||||
notification.classList.add(`notification-${type}`);
|
||||
|
||||
// 显示通知
|
||||
notification.classList.add('notification-visible');
|
||||
|
||||
// 3秒后隐藏通知
|
||||
setTimeout(() => {
|
||||
notification.classList.remove('notification-visible');
|
||||
}, 3000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
88
console/ins/proxy.php
Executable file
88
console/ins/proxy.php
Executable file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* MCSManager API 代理
|
||||
* 用于转发前端请求到 MCSManager 后端 API,避免跨域问题
|
||||
*/
|
||||
|
||||
// 配置信息 - 请根据实际情况修改
|
||||
$config = [
|
||||
'api_base_url' => 'http://154.222.30.227:23333/api', // 请替换为实际的 API 基础 URL
|
||||
'api_key' => 'c507ddc820254ea8a0ea8f279a961b01', // 重要:请替换为实际 API Key
|
||||
'timeout' => 10,
|
||||
];
|
||||
|
||||
// 处理预检请求
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
exit;
|
||||
}
|
||||
|
||||
// 获取请求路径
|
||||
$path = $_GET['path'] ?? '';
|
||||
unset($_GET['path']);
|
||||
|
||||
// 构建API URL(自动添加api_key参数)
|
||||
$url = "{$config['api_base_url']}/{$path}?apikey={$config['api_key']}";
|
||||
|
||||
// 添加GET参数(如果有)
|
||||
if (!empty($_GET)) {
|
||||
$url .= '&' . http_build_query($_GET);
|
||||
}
|
||||
|
||||
// 初始化CURL
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $config['timeout']);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
// 添加自定义请求头
|
||||
$headers = [
|
||||
'X-Requested-With: XMLHttpRequest', // 添加此行
|
||||
'Content-Type: application/json', // 确保 JSON 格式请求头
|
||||
];
|
||||
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
// 处理POST请求
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
|
||||
// 根据Content-Type处理请求体
|
||||
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
|
||||
if (strpos($contentType, 'application/json') !== false) {
|
||||
// JSON格式请求体
|
||||
$requestBody = file_get_contents('php://input');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge($headers, [
|
||||
'Content-Length: ' . strlen($requestBody)
|
||||
]));
|
||||
} else {
|
||||
// 表单格式请求体
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($_POST));
|
||||
}
|
||||
}
|
||||
|
||||
// 执行请求
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
// 处理响应
|
||||
if ($error) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'code' => 500,
|
||||
'message' => '代理请求失败',
|
||||
'error' => $error,
|
||||
'api_url' => $url // 添加请求的地址
|
||||
]);
|
||||
} else {
|
||||
http_response_code($httpCode);
|
||||
// 将请求的地址添加到响应中
|
||||
$responseJson = json_decode($response, true);
|
||||
echo $response;
|
||||
}
|
||||
exit;
|
||||
?>
|
17
console/ins/send-command.php
Executable file
17
console/ins/send-command.php
Executable file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
require_once 'index.php'; // 引入配置和 API 函数
|
||||
|
||||
$uuid = $_POST['uuid'] ?? '';
|
||||
$daemonId = $_POST['daemonId'] ?? '';
|
||||
$command = $_POST['command'] ?? '';
|
||||
|
||||
if ($uuid && $daemonId && $command) {
|
||||
callApi('protected_instance/command', 'GET', [
|
||||
'instanceUuid' => $uuid,
|
||||
'daemonId' => $daemonId,
|
||||
'command' => $command
|
||||
]);
|
||||
}
|
||||
|
||||
header("Location: index.php?uuid={$uuid}&daemonId={$daemonId}");
|
||||
exit;
|
19
console/ins/test.php
Executable file
19
console/ins/test.php
Executable file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
$curl = curl_init();
|
||||
|
||||
curl_setopt_array($curl, array(
|
||||
CURLOPT_URL => 'http://154.222.30.227:23333/api/protected_instance/outputlog?uuid=d4cadb66e037430889c0d149abaa8e0e&apikey=c507ddc820254ea8a0ea8f279a961b01&daemonId=454abd2b1a004e449cdd8c0e452a2f60',
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => '',
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 0,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => 'GET',
|
||||
));
|
||||
|
||||
$response = curl_exec($curl);
|
||||
|
||||
curl_close($curl);
|
||||
echo $response;
|
13
console/logout.php
Executable file
13
console/logout.php
Executable file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// 清除会话
|
||||
unset($_SESSION['user_id']);
|
||||
unset($_SESSION['username']);
|
||||
|
||||
// 清除 Token Cookie
|
||||
setcookie('auth_token', '', time() - 3600, '/');
|
||||
|
||||
// 跳转到登录页面
|
||||
header("Location: ../auth/");
|
||||
exit();
|
18
console/new/config.php
Executable file
18
console/new/config.php
Executable file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
// 数据库配置
|
||||
$host = 'localhost'; // 数据库主机(本地通常为 localhost)
|
||||
$dbname = 'ccsbeta'; // 数据库名(你提供的名称)
|
||||
$username = 'ccsbeta'; // 数据库用户名(默认通常为 root)
|
||||
$password = 'ccsbeta'; // 数据库密码(你提供的密码)
|
||||
|
||||
// 创建数据库连接
|
||||
$ccs_conn = new mysqli($host, $username, $password, $dbname);
|
||||
$conn = new mysqli($host, 'ccs', 'ccsdatabase', 'easyauth');
|
||||
// 检查连接是否成功
|
||||
if ($conn->connect_error) {
|
||||
die("数据库连接失败: " . $conn->connect_error);
|
||||
}
|
||||
|
||||
// 设置字符集为 utf8mb4(支持中文)
|
||||
$conn->set_charset("utf8mb4");
|
||||
?>
|
40
console/new/error.json
Executable file
40
console/new/error.json
Executable file
@ -0,0 +1,40 @@
|
||||
[
|
||||
{
|
||||
"timestamp": "2025-04-07 12:24:20",
|
||||
"error": {
|
||||
"success": false,
|
||||
"message": "创建玩家失败,请稍后重试!错误: Duplicate entry 'admin' for key 'username'",
|
||||
"sql": "INSERT INTO easyauth (username, username_lower, uuid, data) VALUES (?, ?, NULL, ?)",
|
||||
"player_name": "admin",
|
||||
"data": "{\"last_ip\":\"\",\"password\":\"$2y$10$5gSySPi9H02rdVbbz0Ez2uzg04EFLMIW1M3Iz5YRQPOqZkkSo6u0a\",\"login_tries\":0,\"data_version\":1,\"online_account\":\"UNKNOWN\",\"last_kicked_date\":\"1970-01-01T00:00:00Z\",\"registration_date\":\"2025-04-07T12:24:20+0800\",\"last_authenticated_date\":\"2025-04-07T12:24:20+0800\"}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-04-07 13:21:49",
|
||||
"error": {
|
||||
"success": false,
|
||||
"message": "连接到 ccs_auth 数据库失败: Access denied for user 'admin'@'localhost' (using password: YES)",
|
||||
"sql": "INSERT INTO easyauth (username, username_lower, uuid, data) VALUES (?, ?, NULL, ?)",
|
||||
"player_name": "admin",
|
||||
"data": "{\"last_ip\":\"\",\"password\":\"$2y$10$eE3Yk7e42KYyFgPjdyoFVObcRq.pt8QfL0k5IleH5wBslqTPzCC.G\",\"login_tries\":0,\"data_version\":1,\"online_account\":\"UNKNOWN\",\"last_kicked_date\":\"1970-01-01T00:00:00Z\",\"registration_date\":\"2025-04-07T13:21:49+08:00\",\"last_authenticated_date\":\"2025-04-07T13:21:49+08:00\"}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-05-24 21:31:10",
|
||||
"error": {
|
||||
"success": false,
|
||||
"message": "准备语句失败: Table 'ccsbeta.easyauth' doesn't exist",
|
||||
"sql": "SELECT username FROM easyauth WHERE username = ?",
|
||||
"player_name": "dyzzyduq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-05-24 21:33:06",
|
||||
"error": {
|
||||
"success": false,
|
||||
"message": "准备语句失败: Table 'ccsbeta.easyauth' doesn't exist",
|
||||
"sql": "SELECT username FROM easyauth WHERE username = ?",
|
||||
"player_name": "dyzzyduq"
|
||||
}
|
||||
}
|
||||
]
|
196
console/new/index.php
Executable file
196
console/new/index.php
Executable file
@ -0,0 +1,196 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../config.php'; // 确保此文件中有数据库连接
|
||||
|
||||
// 检查是否登录
|
||||
if (!isset($_SESSION['user_id']) || !isset($_SESSION['username'])) {
|
||||
header("Location: https://ccsnetwork.cn/auth"); // 未登录时跳转到登录页面
|
||||
exit();
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$username = $_SESSION['username'];
|
||||
|
||||
$servers = [];
|
||||
$apiUrl = 'https://api.none.pw/mcserver/index.php'; // 替换为实际的 API 地址
|
||||
|
||||
// 使用 cURL 获取节点列表
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $apiUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Accept: application/json',
|
||||
'User-Agent: Your-App-Name/1.0'
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response !== false) {
|
||||
$apiData = json_decode($response, true);
|
||||
if (isset($apiData['servers']) && is_array($apiData['servers'])) {
|
||||
$servers = $apiData['servers'];
|
||||
} else {
|
||||
$servers = []; // 如果解析失败或格式不正确,设置为空数组
|
||||
}
|
||||
} else {
|
||||
$servers = []; // 如果请求失败,设置为空数组
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
?><!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>创建玩家 - 圆周云境</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<style>
|
||||
.create-player-container {
|
||||
max-width: 600px;
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-group select, .form-group input, .form-group textarea {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.form-group button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
padding: 10px 15px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-group button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: green;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="create-player-container">
|
||||
<h2>创建玩家</h2>
|
||||
<div id="response-message"></div>
|
||||
<form id="create-player-form">
|
||||
<div class="form-group">
|
||||
<label for="player_name">玩家名字</label>
|
||||
<input type="text" id="player_name" name="player_name" placeholder="请输入玩家名字" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">密码</label>
|
||||
<input type="password" id="password" name="password" placeholder="请输入密码" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="server_id">选择服务器</label>
|
||||
<select id="server_id" name="server_id" required>
|
||||
<option value="">请选择服务器</option>
|
||||
<?php foreach ($servers as $server): ?>
|
||||
<option value="<?php echo htmlspecialchars($server['id']); ?>">
|
||||
<?php echo htmlspecialchars($server['name']); ?> (<?php echo htmlspecialchars($server['location']); ?>)
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit">创建玩家</button>
|
||||
</div>
|
||||
</form>
|
||||
<p>已有玩家?<a href="../index.php" class="menu-item">返回用户后台</a></p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('create-player-form').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(this);
|
||||
|
||||
fetch('process_form.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.text(); // 先获取原始文本
|
||||
})
|
||||
.then(text => {
|
||||
try {
|
||||
const data = JSON.parse(text);
|
||||
const responseMessage = document.getElementById('response-message');
|
||||
if (data.success) {
|
||||
responseMessage.innerHTML = `<p class="success">${data.message}</p>`;
|
||||
// 如果创建成功,可以跳转到玩家列表页面
|
||||
setTimeout(() => {
|
||||
window.location.href = '../index.php?login=success';
|
||||
}, 2000);
|
||||
} else {
|
||||
// 显示详细的错误信息
|
||||
let errorMessage = data.message;
|
||||
if (data.sql) {
|
||||
errorMessage += `<br><br>SQL: ${data.sql}`;
|
||||
}
|
||||
if (data.player_name) {
|
||||
errorMessage += `<br><br>玩家名称: ${data.player_name}`;
|
||||
}
|
||||
if (data.data) {
|
||||
errorMessage += `<br><br>数据: ${data.data}`;
|
||||
}
|
||||
responseMessage.innerHTML = `<p class="error">${errorMessage}</p>`;
|
||||
|
||||
// 输出到控制台
|
||||
console.error('后端返回的错误信息:', data);
|
||||
}
|
||||
} catch (e) {
|
||||
// 如果解析 JSON 失败,显示原始文本
|
||||
console.error('解析 JSON 失败:', e);
|
||||
console.error('后端返回的原始文本:', text);
|
||||
document.getElementById('response-message').innerHTML = `<p class="error">创建玩家失败,请稍后重试!错误信息: ${text}</p>`;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
// 检查 error 是否包含 response
|
||||
if (error.response) {
|
||||
error.response.text().then(text => {
|
||||
console.error('Response text:', text);
|
||||
document.getElementById('response-message').innerHTML = `<p class="error">创建玩家失败,请稍后重试!错误信息: ${text}</p>`;
|
||||
});
|
||||
} else {
|
||||
document.getElementById('response-message').innerHTML = '<p class="error">创建玩家失败,请稍后重试!</p>';
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
240
console/new/process_form.php
Executable file
240
console/new/process_form.php
Executable file
@ -0,0 +1,240 @@
|
||||
<?php
|
||||
session_start();
|
||||
require './config.php'; // 确保此文件中有数据库连接
|
||||
date_default_timezone_set('Asia/Shanghai');
|
||||
|
||||
// 检查是否登录
|
||||
if (!isset($_SESSION['user_id']) || !isset($_SESSION['username'])) {
|
||||
echo json_encode(['success' => false, 'message' => '未登录!']);
|
||||
exit();
|
||||
}
|
||||
|
||||
// 获取当前用户信息
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$username = $_SESSION['username'];
|
||||
|
||||
// 处理表单提交
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// 获取表单数据
|
||||
$player_name = trim($_POST['player_name'] ?? '');
|
||||
$server_id = trim($_POST['server_id'] ?? '');
|
||||
$password = trim($_POST['password'] ?? '');
|
||||
|
||||
// 验证表单数据
|
||||
if (empty($player_name)) {
|
||||
echo json_encode(['success' => false, 'message' => '玩家名字不能为空!']);
|
||||
exit();
|
||||
} elseif (empty($server_id)) {
|
||||
echo json_encode(['success' => false, 'message' => '请选择服务器!']);
|
||||
exit();
|
||||
} elseif (empty($password)) {
|
||||
echo json_encode(['success' => false, 'message' => '请输入密码!']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$check_easyauth_sql = "SELECT username FROM easyauth WHERE username = ?";
|
||||
if (!$check_easyauth_stmt = $conn->prepare($check_easyauth_sql)) {
|
||||
$errorDetails = [
|
||||
'success' => false,
|
||||
'message' => "准备语句失败: " . $conn->error,
|
||||
'sql' => $check_easyauth_sql,
|
||||
'player_name' => $player_name
|
||||
];
|
||||
logError($errorDetails);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($errorDetails);
|
||||
exit();
|
||||
}
|
||||
$check_easyauth_stmt->bind_param("s", $player_name);
|
||||
$check_easyauth_stmt->execute();
|
||||
$check_easyauth_stmt->store_result();
|
||||
|
||||
if ($check_easyauth_stmt->num_rows > 0) {
|
||||
$errorDetails = [
|
||||
'success' => false,
|
||||
'message' => "用户名已存在,请选择其他用户名!",
|
||||
'player_name' => $player_name
|
||||
];
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($errorDetails);
|
||||
$check_easyauth_stmt->close();
|
||||
exit();
|
||||
}
|
||||
$check_easyauth_stmt->close();
|
||||
|
||||
$check_ccs_sql = "SELECT player_name FROM players WHERE player_name = ?";
|
||||
if (!$check_ccs_stmt = $ccs_conn->prepare($check_ccs_sql)) {
|
||||
// 准备语句失败
|
||||
$errorDetails = [
|
||||
'success' => false,
|
||||
'message' => "准备语句失败: " . $ccs_conn->error,
|
||||
'sql' => $check_ccs_sql,
|
||||
'player_name' => $player_name
|
||||
];
|
||||
logError($errorDetails);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($errorDetails);
|
||||
exit();
|
||||
}
|
||||
$check_ccs_stmt->bind_param("s", $player_name);
|
||||
$check_ccs_stmt->execute();
|
||||
$check_ccs_stmt->store_result();
|
||||
|
||||
if ($check_ccs_stmt->num_rows > 0) {
|
||||
// 用户名已存在
|
||||
$errorDetails = [
|
||||
'success' => false,
|
||||
'message' => "用户名已存在,请选择其他用户名!",
|
||||
'player_name' => $player_name
|
||||
];
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($errorDetails);
|
||||
$check_ccs_stmt->close();
|
||||
exit();
|
||||
}
|
||||
$check_ccs_stmt->close();
|
||||
|
||||
// 生成 $2a$ 哈希值
|
||||
$hash = generateBcryptHash($password, 12);
|
||||
|
||||
// 获取当前时间,包含时区信息
|
||||
$registration_date = (new DateTime())->setTimezone(new DateTimeZone('Asia/Shanghai'))->format('Y-m-d\TH:i:sO');
|
||||
$last_authenticated_date = (new DateTime())->setTimezone(new DateTimeZone('Asia/Shanghai'))->format('Y-m-d\TH:i:sO');
|
||||
|
||||
// 手动处理时区信息,将 +0800 转换为 +08:00
|
||||
$registration_date_with_colon = preg_replace('/(\+\d{2})(\d{2})$/', '$1:$2', $registration_date);
|
||||
$last_authenticated_date_with_colon = preg_replace('/(\+\d{2})(\d{2})$/', '$1:$2', $last_authenticated_date);
|
||||
|
||||
// 构造data字段的JSON数据
|
||||
$data = json_encode([
|
||||
"last_ip" => "", // 现在将 last_ip 设置为空
|
||||
"password" => $hash,
|
||||
"login_tries" => 0,
|
||||
"data_version" => 1,
|
||||
"online_account" => "UNKNOWN",
|
||||
"last_kicked_date" => "1970-01-01T00:00:00Z",
|
||||
"registration_date" => $registration_date_with_colon, // 当前时间
|
||||
"last_authenticated_date" => $last_authenticated_date_with_colon // 当前时间
|
||||
]);
|
||||
|
||||
// 插入到 easyauth 数据库的 easyauth 表
|
||||
$easyauth_sql = "INSERT INTO easyauth (username, username_lower, uuid, data) VALUES (?, ?, NULL, ?)";
|
||||
if (!$easyauth_stmt = $conn->prepare($easyauth_sql)) {
|
||||
// 打印更详细的错误信息
|
||||
$errorDetails = [
|
||||
'success' => false,
|
||||
'message' => "准备语句失败: " . $conn->error,
|
||||
'sql' => $easyauth_sql,
|
||||
'player_name' => $player_name,
|
||||
'data' => $data
|
||||
];
|
||||
logError($errorDetails);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($errorDetails);
|
||||
exit();
|
||||
}
|
||||
// 绑定参数
|
||||
$lowercase_player_name = strtolower($player_name);
|
||||
$easyauth_stmt->bind_param("sss", $player_name, $lowercase_player_name, $data);
|
||||
if ($easyauth_stmt->execute()) {
|
||||
// 插入到 ccs_auth 数据库的 players 表
|
||||
$ccs_sql = "INSERT INTO players (user_id, player_name, description, server_id, created_at) VALUES (?, ?, ?, ?, NOW())";
|
||||
if (!$ccs_stmt = $ccs_conn->prepare($ccs_sql)) {
|
||||
// 回滚 easyauth 的插入操作
|
||||
$conn->query("DELETE FROM easyauth WHERE username = '$player_name'");
|
||||
// 打印更详细的错误信息
|
||||
$errorDetails = [
|
||||
'success' => false,
|
||||
'message' => "准备语句失败: " . $ccs_conn->error,
|
||||
'sql' => $ccs_sql,
|
||||
'player_name' => $player_name,
|
||||
'data' => $data
|
||||
];
|
||||
logError($errorDetails);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($errorDetails);
|
||||
exit();
|
||||
}
|
||||
// 将 '普通玩家' 存储在变量中
|
||||
$description = '普通玩家';
|
||||
$ccs_stmt->bind_param("issi", $user_id, $player_name, $description, $server_id);
|
||||
if ($ccs_stmt->execute()) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => true, 'message' => '玩家创建成功!']);
|
||||
} else {
|
||||
// 回滚 easyauth 的插入操作
|
||||
$conn->query("DELETE FROM easyauth WHERE username = '$player_name'");
|
||||
// 打印更详细的错误信息
|
||||
$errorDetails = [
|
||||
'success' => false,
|
||||
'message' => "插入 ccs_auth 数据库失败: " . $ccs_stmt->error,
|
||||
'sql' => $ccs_sql,
|
||||
'player_name' => $player_name,
|
||||
'data' => $data
|
||||
];
|
||||
logError($errorDetails);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($errorDetails);
|
||||
}
|
||||
$ccs_stmt->close();
|
||||
} else {
|
||||
// 打印更详细的错误信息
|
||||
$errorDetails = [
|
||||
'success' => false,
|
||||
'message' => "创建玩家失败,请稍后重试!错误: " . $easyauth_stmt->error,
|
||||
'sql' => $easyauth_sql,
|
||||
'player_name' => $player_name,
|
||||
'data' => $data
|
||||
];
|
||||
logError($errorDetails);
|
||||
header('Content: application/json');
|
||||
echo json_encode($errorDetails);
|
||||
}
|
||||
$easyauth_stmt->close();
|
||||
} else {
|
||||
// 如果请求方法不是 POST
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'message' => '请求方法不正确!']);
|
||||
exit();
|
||||
}
|
||||
|
||||
// 关闭数据库连接
|
||||
$conn->close();
|
||||
$ccs_conn->close();
|
||||
|
||||
// 添加一个函数来记录错误到 error.json 文件
|
||||
function logError($errorDetails) {
|
||||
$errorFile = __DIR__ . '/error.json'; // 指定错误文件的路径
|
||||
$timestamp = date('Y-m-d H:i:s');
|
||||
|
||||
// 读取现有的错误记录
|
||||
if (file_exists($errorFile)) {
|
||||
$existingErrors = json_decode(file_get_contents($errorFile), true);
|
||||
} else {
|
||||
$existingErrors = [];
|
||||
}
|
||||
|
||||
// 添加新的错误记录
|
||||
$newError = [
|
||||
'timestamp' => $timestamp,
|
||||
'error' => $errorDetails
|
||||
];
|
||||
$existingErrors[] = $newError;
|
||||
|
||||
// 将错误记录写回文件
|
||||
file_put_contents($errorFile, json_encode($existingErrors, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
// 生成 $2a$ 哈希值的函数
|
||||
function generateBcryptHash($password, $cost) {
|
||||
// 确保成本因子在 4 到 31 之间
|
||||
$cost = max(4, min(31, $cost));
|
||||
|
||||
// 生成随机盐值
|
||||
$salt = '$2a$' . str_pad($cost, 2, '0', STR_PAD_LEFT) . '$';
|
||||
$salt .= substr(str_replace(['+', '/'], ['.', '_'], base64_encode(random_bytes(16))), 0, 22);
|
||||
|
||||
// 生成哈希值
|
||||
return crypt($password, $salt);
|
||||
}
|
||||
?>
|
0
console/new/test.php
Executable file
0
console/new/test.php
Executable file
260
console/player/index.php
Executable file
260
console/player/index.php
Executable file
@ -0,0 +1,260 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../admin/config.php'; // 确保此文件中有数据库连接
|
||||
// 检查是否登录
|
||||
if (!isset($_SESSION['user_id']) || !isset($_SESSION['username'])) {
|
||||
header("Location: /auth"); // 未登录时跳转到登录页面
|
||||
exit();
|
||||
}
|
||||
|
||||
// 获取当前用户信息
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$username = $_SESSION['username'];
|
||||
|
||||
// 检查Token是否匹配
|
||||
if (!isset($_GET['token']) || $_GET['token'] != $_SESSION['player_token']) {
|
||||
header("Location: ../index.php"); // Token不匹配时跳转回玩家列表页面
|
||||
exit();
|
||||
}
|
||||
|
||||
// 获取传递的玩家名字
|
||||
$player_name = $_GET['player_name'];
|
||||
|
||||
// 验证玩家名字是否有效
|
||||
if (empty($player_name)) {
|
||||
header("Location: ../index.php"); // 玩家名字为空时跳转回玩家列表页面
|
||||
exit();
|
||||
}
|
||||
|
||||
// 验证玩家是否属于当前用户,并获取服务器ID
|
||||
$stmt = $conn->prepare("SELECT player_name, description, created_at, server_id FROM players WHERE user_id = ? AND player_name = ?");
|
||||
$stmt->bind_param("is", $user_id, $player_name);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$player = $result->fetch_assoc();
|
||||
|
||||
if (!$player) {
|
||||
header("Location: ../index.php"); // 玩家不存在或不属于当前用户时跳转回玩家列表页面
|
||||
exit();
|
||||
}
|
||||
|
||||
// 获取服务器信息
|
||||
$server_id = $player['server_id'];
|
||||
$stmt = $conn->prepare("SELECT ip_address, port FROM mc_servers WHERE id = ?");
|
||||
$stmt->bind_param("i", $server_id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$server = $result->fetch_assoc();
|
||||
|
||||
|
||||
// 检查 easyauth 数据库连接是否成功
|
||||
if ($easyauthConn->connect_error) {
|
||||
die("连接到 easyauth 数据库失败: " . $easyauthConn->connect_error);
|
||||
}
|
||||
|
||||
// 在 easyauth 表中查找相同玩家名的 uuid
|
||||
$easyauth_stmt = $easyauthConn->prepare("SELECT uuid FROM easyauth WHERE username = ?");
|
||||
$easyauth_stmt->bind_param("s", $player_name);
|
||||
$easyauth_stmt->execute();
|
||||
$easyauth_result = $easyauth_stmt->get_result();
|
||||
$easyauth_player = $easyauth_result->fetch_assoc();
|
||||
|
||||
// 处理注销请求
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['logout'])) {
|
||||
// 连接主数据库
|
||||
// $conn = new mysqli('localhost', 'awa', 'yzculture', 'ccs_auth');
|
||||
if ($conn->connect_error) {
|
||||
die("连接到主数据库失败: " . $conn->connect_error);
|
||||
}
|
||||
|
||||
// 连接 easyauth 数据库
|
||||
// $easyauth_conn = new mysqli('localhost', 'ccs', 'ccsdatabase', 'easyauth');
|
||||
if ($easyauthConn->connect_error) {
|
||||
die("连接到 easyauth 数据库失败: " . $easyauthConn->connect_error);
|
||||
}
|
||||
|
||||
// 启用事务
|
||||
$conn->autocommit(false);
|
||||
$easyauthConn->autocommit(false);
|
||||
|
||||
try {
|
||||
// 删除主数据库中的玩家记录
|
||||
$stmt = $conn->prepare("DELETE FROM players WHERE user_id = ? AND player_name = ?");
|
||||
$stmt->bind_param("is", $user_id, $player_name);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
// 删除 easyauth 数据库中的玩家记录
|
||||
$easyauth_stmt = $easyauthConn->prepare("DELETE FROM easyauth WHERE username = ?");
|
||||
$easyauth_stmt->bind_param("s", $player_name);
|
||||
$easyauth_stmt->execute();
|
||||
$easyauth_stmt->close();
|
||||
|
||||
// 提交事务
|
||||
$conn->commit();
|
||||
$easyauthConn->commit();
|
||||
|
||||
// 关闭数据库连接
|
||||
$conn->close();
|
||||
$easyauthConn->close();
|
||||
|
||||
// 销毁会话并跳转到登录页面
|
||||
session_unset();
|
||||
session_destroy();
|
||||
header("Location: /auth");
|
||||
exit();
|
||||
} catch (Exception $e) {
|
||||
// 回滚事务
|
||||
$conn->rollback();
|
||||
$easyauthConn->rollback();
|
||||
$conn->close();
|
||||
$easyauthConn->close();
|
||||
die("注销失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
// 关闭数据库连接
|
||||
$conn->close();
|
||||
$easyauthConn->close();
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>玩家详情 - 圆周云境</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<style>
|
||||
.player-info-container {
|
||||
max-width: 600px;
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.player-info {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.player-info p {
|
||||
margin-bottom: 10px;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.server-info {
|
||||
margin-top: 20px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.server-info p {
|
||||
margin-bottom: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: inline-block;
|
||||
padding: 8px 15px;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
padding: 10px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.top-bar-content {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.blue-bar {
|
||||
height: 3px;
|
||||
background-color: #007bff;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.combined-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.logout-container {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logout-button {
|
||||
display: inline-block;
|
||||
padding: 8px 15px;
|
||||
background-color: #ff4d4d;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
border: 2px solid #ff4d4d;
|
||||
transition: background-color 0.3s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.logout-button:hover {
|
||||
background-color: #ff0000;
|
||||
border-color: #ff0000;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="combined-container">
|
||||
<div class="header">
|
||||
<h1>玩家详情</h1>
|
||||
</div>
|
||||
|
||||
<div class="player-info-container">
|
||||
<div class="player-info">
|
||||
<p>玩家名: <?php echo htmlspecialchars($player['player_name']); ?></p>
|
||||
<p>称号: <?php echo htmlspecialchars($player['description']); ?></p>
|
||||
<p>UUID: <?php echo htmlspecialchars($easyauth_player['uuid'] ?? '玩家未登录,未找到UUID'); ?></p>
|
||||
</div>
|
||||
|
||||
<div class="server-info">
|
||||
<h3>服务器连接信息</h3>
|
||||
<p>服务器IP: <?php echo htmlspecialchars($server['ip_address'] . ":" . $server['port']); ?></p>
|
||||
</div>
|
||||
|
||||
<div class="back-button">
|
||||
<a href="../index.php" class="menu-item">返回玩家列表</a>
|
||||
</div>
|
||||
|
||||
<div class="logout-container">
|
||||
<form action="" method="post">
|
||||
<button type="submit" name="logout" value="1" class="logout-button" onclick="return confirm('您确定要注销账号吗?此操作不可逆!')">注销账号</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
107
console/stop.html
Executable file
107
console/stop.html
Executable file
@ -0,0 +1,107 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>此网站已归档,等待重启</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #f0f2f5;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
text-align: center;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.logo {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin: 0 auto 20px;
|
||||
background-color: #e0e0e0;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 40px;
|
||||
color: #666;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2.2rem;
|
||||
margin-bottom: 15px;
|
||||
color: #1a73e8;
|
||||
}
|
||||
p {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 20px;
|
||||
color: #555;
|
||||
}
|
||||
.divider {
|
||||
width: 100px;
|
||||
height: 3px;
|
||||
background-color: #1a73e8;
|
||||
margin: 30px auto;
|
||||
}
|
||||
.contact {
|
||||
margin-top: 25px;
|
||||
}
|
||||
.contact a {
|
||||
color: #1a73e8;
|
||||
text-decoration: none;
|
||||
margin: 0 10px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.contact a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
margin-top: 25px;
|
||||
padding: 12px 30px;
|
||||
background-color: #1a73e8;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
.button:hover {
|
||||
background-color: #0d62d1;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="logo">🔒</div>
|
||||
<h1>此网站已归档,等待重启</h1>
|
||||
<p>我们正在整理和改进内容,以提供更好的体验。感谢您的耐心等待!</p>
|
||||
<div class="divider"></div>
|
||||
<p>预计重启时间:未知</p>
|
||||
<p>在此期间,您可以:</p>
|
||||
<ul style="text-align: left; list-style-position: inside; margin-top: 20px;">
|
||||
<li style="margin-bottom: 10px;">关注我们的社交媒体获取最新动态</li>
|
||||
<li style="margin-bottom: 10px;">查看我们的博客了解相关更新</li>
|
||||
<li>订阅我们的邮件通知,第一时间获取网站重启消息</li>
|
||||
</ul>
|
||||
<div class="contact">
|
||||
<a href="mailto:jasper@none.pw">jasper@none.pw</a>
|
||||
</div>
|
||||
<a href="https://example.com" class="button">返回首页</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
237
console/terms.html
Executable file
237
console/terms.html
Executable file
@ -0,0 +1,237 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>服务条款 - 圆周云境</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
padding: 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
padding: 20px 0;
|
||||
border-bottom: 1px solid #e1e4e8;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
color: #2c3e50;
|
||||
font-size: 2.2rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
header p {
|
||||
color: #7f8c8d;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
padding: 30px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
h2 {
|
||||
color: #2c3e50;
|
||||
font-size: 1.5rem;
|
||||
margin: 30px 0 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #3498db;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #34495e;
|
||||
font-size: 1.2rem;
|
||||
margin: 20px 0 10px;
|
||||
padding-left: 10px;
|
||||
border-left: 4px solid #3498db;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 20px;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background-color: #f0f7ff;
|
||||
border-left: 4px solid #3498db;
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
color: #7f8c8d;
|
||||
font-size: 0.9rem;
|
||||
border-top: 1px solid #e1e4e8;
|
||||
}
|
||||
|
||||
nav {
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: #3498db;
|
||||
text-decoration: none;
|
||||
margin: 0 10px;
|
||||
font-weight: 500;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
color: #2980b9;
|
||||
text-decoration: underline;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>服务条款 - 圆周云境</h1>
|
||||
<p>最后更新: 2025年5月24日</p>
|
||||
</header>
|
||||
|
||||
<nav>
|
||||
<a href="#scope">协议的范围</a>
|
||||
<a href="#registration">用户注册</a>
|
||||
<a href="#user-rights">用户权利和义务</a>
|
||||
<a href="#website-rights">本网站的权利和义务</a>
|
||||
<a href="#service-termination">服务的中断和终止</a>
|
||||
<a href="#disclaimer">免责声明</a>
|
||||
<a href="#legal">法律适用与争议解决</a>
|
||||
<a href="#contact">联系我们</a>
|
||||
</nav>
|
||||
|
||||
<div class="content">
|
||||
<section id="scope">
|
||||
<h2>一、协议的范围</h2>
|
||||
<p>本用户协议适用于您(以下简称 "用户")注册、使用 圆周云境(以下简称 "本网站")所提供的 mc 玩家身份验证服务(以下简称 "本服务")。</p>
|
||||
</section>
|
||||
|
||||
<section id="registration">
|
||||
<h2>二、用户注册</h2>
|
||||
<p>用户在注册过程中应提供真实、准确、完整的信息,包括但不限于用户名、电子邮箱地址等。</p>
|
||||
<p>用户不得使用他人的个人信息进行注册,也不得冒用他人身份进行注册。</p>
|
||||
<p>用户注册成功后,应妥善保管自己的账户信息,包括用户名和密码,不得将账户信息透露给他人使用。</p>
|
||||
</section>
|
||||
|
||||
<section id="user-rights">
|
||||
<h2>三、用户权利和义务</h2>
|
||||
|
||||
<h3>(一)权利</h3>
|
||||
<p>用户有权在本网站上进行 mc 玩家身份验证,获取相关的验证结果和信息。</p>
|
||||
<p>用户有权对本网站提供的服务提出意见和建议,并获得相应的反馈。</p>
|
||||
|
||||
<h3>(二)义务</h3>
|
||||
<p>用户应遵守本网站的用户协议和相关规定,不得从事任何违反法律法规或本网站规定的行为,包括但不限于发布违法信息、侵犯他人权益、破坏网站正常运行等。</p>
|
||||
<p>用户应保证所提供的 mc 玩家信息的真实性、合法性和完整性,不得提供虚假或误导性的信息。</p>
|
||||
<p>用户不得将本服务用于任何商业用途或非法活动,未经本网站书面同意,不得以任何形式转让、出租、出借本服务的使用权。</p>
|
||||
</section>
|
||||
|
||||
<section id="website-rights">
|
||||
<h2>四、本网站的权利和义务</h2>
|
||||
|
||||
<h3>(一)权利</h3>
|
||||
<p>本网站有权对用户提供的信息进行审核、验证和管理,如发现用户违反本协议或存在任何异常行为,有权采取相应的措施,包括但不限于警告、限制使用、终止服务等。</p>
|
||||
<p>本网站有权在必要时修改本协议的内容,并提前在本网站上公布。如用户继续使用本网站的服务,则视为用户已接受修改后的协议。</p>
|
||||
|
||||
<h3>(二)义务</h3>
|
||||
<p>本网站应采取合理的技术和管理措施,保障本网站的安全性和稳定性,确保用户能够正常使用本服务。</p>
|
||||
<p>本网站应对用户提供的信息进行严格保密,不得向任何第三方泄露或提供,但法律法规另有规定的除外。</p>
|
||||
<p>本网站应为用户提供优质的信息查询、验证等必要的技术支持和服务。</p>
|
||||
</section>
|
||||
|
||||
<section id="service-termination">
|
||||
<h2>五、服务的中断和终止</h2>
|
||||
<p>因系统维护、升级或不可抗力等因素导致本网站服务中断或终止的,本网站应及时通知用户,并尽可能减少对用户的影响。</p>
|
||||
<p>如用户违反本协议或法律法规的规定,本网站有权随时中断或终止向该用户提供了一定期限的补救机会后,用户仍未改正的,本网站有权终止向该用户提供了一系列通知后,用户仍未响应的,本网站有权终止向该用户个性服务,并保留追究其法律责任的权利。</p>
|
||||
<p>本协议终止后,本网站有权删除用户在本网站上的所有信息和数据,但法律法规另有规定的除外。</p>
|
||||
</section>
|
||||
|
||||
<section id="disclaimer">
|
||||
<h2>六、免责声明</h2>
|
||||
<p>本网站尽力提供准确、完整的信息和验证服务,但本网站不对验证结果的准确性、完整性、及时性或可靠性做出任何保证或承诺。因验证结果产生的任何纠纷或损失,本网站不承担任何责任。</p>
|
||||
<p>使用本网站的服务可能存在一定的风险,用户应自行承担因使用本网站服务而产生的一切风险和后果。本网站不对用户因使用本网站服务所遭受的任何直接、间接、偶然、特殊及后续的损害负责。</p>
|
||||
</section>
|
||||
|
||||
<section id="legal">
|
||||
<h2>七、法律适用与争议解决</h2>
|
||||
<p>本协议的订立、执行和解释均适用中华人民共和国法律。</p>
|
||||
</section>
|
||||
|
||||
<section id="contact">
|
||||
<h2>八、联系我们</h2>
|
||||
<p>如果您对本协议或我们的服务有任何疑问,请通过以下方式联系我们:</p>
|
||||
<ul>
|
||||
<li>电子邮件: jasper@none.pw</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<div class="highlight">
|
||||
<p><strong>重要提示:</strong>如果继续使用我们的服务,请确认您已阅读、理解并同意遵守本用户协议。否则不要使用任何功能!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p>© 2025 圆周云境. 保留所有权利。</p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
document.querySelectorAll('nav a').forEach(anchor => {
|
||||
anchor.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const targetId = this.getAttribute('href');
|
||||
const targetElement = document.querySelector(targetId);
|
||||
|
||||
window.scrollTo({
|
||||
top: targetElement.offsetTop - 100,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
130
console/user_id_card.php
Executable file
130
console/user_id_card.php
Executable file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../auth/config.php'; // 引入数据库配置
|
||||
|
||||
if (!isset($_SESSION['user_id']) || !isset($_SESSION['username'])) {
|
||||
header("Location: ../auth/");
|
||||
exit();
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
function get_user_full_info($conn, $user_id) {
|
||||
$stmt = $conn->prepare("SELECT
|
||||
username,
|
||||
level,
|
||||
points,
|
||||
email,
|
||||
register_time,
|
||||
organization,
|
||||
development_field,
|
||||
skill_tags
|
||||
FROM users
|
||||
WHERE id = ?");
|
||||
$stmt->bind_param("i", $user_id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($row = $result->fetch_assoc()) {
|
||||
return $row;
|
||||
}
|
||||
return [
|
||||
'username' => '未授权人员',
|
||||
'level' => 1,
|
||||
'points' => 0,
|
||||
'email' => 'NULL',
|
||||
'register_time' => 'NULL',
|
||||
'organization' => 'NULL',
|
||||
'development_field' => 'NULL',
|
||||
'skill_tags' => 'NULL'
|
||||
];
|
||||
}
|
||||
|
||||
$user_info = get_user_full_info($conn, $user_id);
|
||||
$username = $user_info['username'];
|
||||
$level = $user_info['level'];
|
||||
$email = $user_info['email'];
|
||||
$levelDescriptions = [1 => '云境初探者', 2 => '云境行者', 3 => '云智大师', 4 => '云核守护者'];
|
||||
$userLevelDescription = $levelDescriptions[$level] ?? '未知等级';
|
||||
|
||||
$conn->close();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>用户身份ID卡</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
background-color: #f5f6fa;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.id-card {
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
width: 300px;
|
||||
padding: 15px;
|
||||
position: relative;
|
||||
border: 1px solid #e0e0e0;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: linear-gradient(135deg, #4e27eb, #9c3cec);
|
||||
color: white;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.card-photo {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
margin: 10px auto;
|
||||
overflow: hidden;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.card-info {
|
||||
text-align: left;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.card-info p {
|
||||
margin: 5px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px dashed #e0e0e0;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.card-info-label {
|
||||
color: #666;
|
||||
font-weight: bold;
|
||||
width: 60px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="id-card">
|
||||
<div class="card-header">用户ID卡</div>
|
||||
<div class="card-photo" style="background-image: url('https://i.bobopic.com/small/87673990.jpg');"></div>
|
||||
<div class="card-info">
|
||||
<p><span class="card-info-label">名称:</span><span class="card-info-value"><?php echo htmlspecialchars($username); ?></span></p>
|
||||
<p><span class="card-info-label">ID:</span><span class="card-info-value"><?php echo htmlspecialchars($user_id); ?></span></p>
|
||||
<p><span class="card-info-label">等级:</span><span class="card-info-value"><?php echo htmlspecialchars($userLevelDescription); ?></span></p>
|
||||
<p><span class="card-info-label">邮箱:</span><span class="card-info-value"><?php echo htmlspecialchars($email); ?></span></p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
270
index-true.html
Executable file
270
index-true.html
Executable file
@ -0,0 +1,270 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>圆周云境</title>
|
||||
<link rel="icon" href="./logo.png" type="image/png">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#4e73df',
|
||||
secondary: '#e9ecef',
|
||||
accent: '#fff3cd'
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['PingFang SC', 'Microsoft YaHei', 'sans-serif'],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
.bg-blur {
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
/* 自定义加载动画 */
|
||||
.loader-spinner {
|
||||
border: 4px solid rgba(255, 255, 255, 0.3);
|
||||
border-top: 4px solid #4e73df;
|
||||
border-radius: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
animation: spin 1.5s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.fade-out {
|
||||
animation: fadeOut 1s forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
0% { opacity: 1; }
|
||||
100% { opacity: 0; visibility: hidden; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="font-sans antialiased text-gray-900 leading-relaxed">
|
||||
<div class="fixed inset-0 bg-cover bg-center z-0" style="background-image: url('back.jpeg');"></div>
|
||||
<!--<div class="fixed inset-0 bg-black/5 z-10"></div>-->
|
||||
|
||||
<div class="relative z-20">
|
||||
<div id="loader" class="fixed inset-0 bg-white flex items-center justify-center z-50">
|
||||
<div class="loader-spinner"></div>
|
||||
</div>
|
||||
|
||||
<header class="navbar fixed top-0 left-0 w-full bg-white/90 shadow-md z-40 transition-all duration-300">
|
||||
<div class="container mx-auto px-4 py-3 flex justify-between items-center">
|
||||
<a href="#home" class="flex items-center text-lg font-bold text-primary no-underline hover:no-underline">
|
||||
<img src="./logo.png" alt="Logo" class="w-6 h-6 mr-2">
|
||||
<h2 class="text-lg font-semibold">圆周云境</h2>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="hero pt-32 pb-20 bg-white/80 backdrop-blur-sm" id="home">
|
||||
<div class="container mx-auto px-4 text-center">
|
||||
<h1 class="text-[clamp(1.5rem,3vw,2.2rem)] font-bold text-primary mb-6 leading-tight">
|
||||
CCS.MC服务器玩家通行证
|
||||
</h1>
|
||||
<div class="flex justify-center flex-wrap gap-2 mb-8">
|
||||
<span class="px-4 py-2 bg-gray-100/70 rounded-full text-xs font-medium text-gray-800">
|
||||
高配置
|
||||
</span>
|
||||
<span class="px-4 py-2 bg-gray-100/70 rounded-full text-xs font-medium text-gray-800">
|
||||
极速游玩
|
||||
</span>
|
||||
</div>
|
||||
<div class="bg-accent/70 border border-yellow-300 rounded-lg px-6 py-4 mb-10 max-w-md mx-auto">
|
||||
<p class="text-sm text-yellow-800">🌟 自2024年起运营的公益MC服务器</p>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<a href="./auth" class="px-8 py-3 bg-primary text-white rounded-lg hover:bg-primary/90 transition-all shadow-lg hover:shadow-primary/30 transform hover:-translate-y-1">
|
||||
进入控制台申请
|
||||
</a>
|
||||
<a href="#about" class="px-8 py-3 border border-gray-300 rounded-lg hover:border-primary hover:text-primary transition-all">
|
||||
关于我们
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="about py-20 bg-white/80 backdrop-blur-sm" id="about">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="bg-secondary/70 rounded-xl px-8 py-8 mb-12 text-center max-w-3xl mx-auto">
|
||||
<h2 class="text-[clamp(1.3rem,2.5vw,1.8rem)] font-bold text-primary mb-4">关于我们</h2>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div class="bg-white/90 rounded-xl p-8 shadow-lg hover:shadow-xl transition-shadow">
|
||||
<h3 class="text-base font-bold text-gray-800 mb-4 flex items-center">
|
||||
<i class="fa fa-info-circle text-primary mr-2"></i>
|
||||
关于我们
|
||||
</h3>
|
||||
<p class="text-sm text-gray-700">
|
||||
圆周云境(CycleCloudscape)诞生于2022年,原名圆周文化,后转型升级,主营MC服务器领域
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-white/90 rounded-xl p-8 shadow-lg hover:shadow-xl transition-shadow">
|
||||
<h3 class="text-base font-bold text-gray-800 mb-4 flex items-center">
|
||||
<i class="fa fa-lightbulb-o text-primary mr-2"></i>
|
||||
服务类型
|
||||
</h3>
|
||||
<p class="text-sm text-gray-700">
|
||||
我们主营MC服务器领域,为广大玩家以及服主提供身份验证系统,提供公益Hyp加速ip。
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-white/90 rounded-xl p-8 shadow-lg hover:shadow-xl transition-shadow">
|
||||
<h3 class="text-base font-bold text-gray-800 mb-4 flex items-center">
|
||||
<i class="fa fa-star text-primary mr-2"></i>
|
||||
是否收费?
|
||||
</h3>
|
||||
<p class="text-sm text-gray-700">
|
||||
我们提供完全免费的网络服务。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 友情链接 -->
|
||||
<section class="partner py-20 bg-gray-100/70 backdrop-blur-sm">
|
||||
<div class="container mx-auto px-4">
|
||||
<h2 class="text-[clamp(1.3rem,2.5vw,1.8rem)] font-bold text-primary text-center mb-12">友情链接</h2>
|
||||
<div class="bg-white/90 rounded-xl shadow-xl overflow-hidden max-w-4xl mx-auto hover:shadow-2xl transition-shadow">
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<div class="w-full md:w-1/3 p-6 md:p-0">
|
||||
<img src="./rainyun.jpg" alt="雨云" class="w-full h-full object-cover">
|
||||
</div>
|
||||
<div class="w-full md:w-2/3 p-8">
|
||||
<h3 class="text-lg font-bold text-gray-800 mb-4">雨云 - 新一代云服务提供商!</h3>
|
||||
<p class="text-sm text-gray-700 mb-6">
|
||||
预装各类服务端,开箱即用,一分钟即可开好MC服!独家动态计费,享受高性能同时节省闲置费用,不用担心玩家上学或睡觉!点击链接注册可享5折优惠券!
|
||||
</p>
|
||||
<a href="https://www.rainyun.com/dyzzyduq_" class="inline-flex items-center px-6 py-3 bg-primary text-white rounded-lg hover:bg-primary/90 transition-all shadow-md hover:shadow-lg transform hover:-translate-y-1">
|
||||
访问雨云官网 <i class="fa fa-arrow-right ml-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 硬件设施 -->
|
||||
<section class="hardware py-20 bg-gray-100/70 backdrop-blur-sm" id="hardware">
|
||||
<div class="container mx-auto px-4 text-center">
|
||||
<h2 class="text-[clamp(1.3rem,2.5vw,1.8rem)] font-bold text-primary mb-6">专业级硬件设施</h2>
|
||||
<p class="text-sm text-gray-700 mb-12 max-w-2xl mx-auto">高品质基础架构,为您的游玩保驾护航</p>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div class="bg-white/90 rounded-xl p-8 shadow-lg hover:shadow-xl transition-all transform hover:-translate-y-2">
|
||||
<div class="text-3xl text-primary mb-6 p-4 bg-primary/10 rounded-full inline-block">
|
||||
<i class="fa fa-microchip"></i>
|
||||
</div>
|
||||
<h3 class="text-base font-bold text-gray-800 mb-4">高性能CPU</h3>
|
||||
<p class="text-sm text-gray-700">
|
||||
高频高性能处理器,保证玩家游玩顺畅
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-white/90 rounded-xl p-8 shadow-lg hover:shadow-xl transition-all transform hover:-translate-y-2">
|
||||
<div class="text-3xl text-primary mb-6 p-4 bg-primary/10 rounded-full inline-block">
|
||||
<i class="fa fa-bolt"></i>
|
||||
</div>
|
||||
<h3 class="text-base font-bold text-gray-800 mb-4">极速网络</h3>
|
||||
<p class="text-sm text-gray-700">
|
||||
提供极速的游玩线路(BGP),保证玩家体验
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-white/90 rounded-xl p-8 shadow-lg hover:shadow-xl transition-all transform hover:-translate-y-2">
|
||||
<div class="text-3xl text-primary mb-6 p-4 bg-primary/10 rounded-full inline-block">
|
||||
<i class="fa fa-server"></i>
|
||||
</div>
|
||||
<h3 class="text-base font-bold text-gray-800 mb-4">存档永久保留</h3>
|
||||
<p class="text-sm text-gray-700">
|
||||
提供存档永久保留
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="footer py-12 bg-white border-t backdrop-blur-sm text-center">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="flex flex-col md:flex-row justify-between items-center">
|
||||
<div class="mb-4 md:mb-0">
|
||||
<div class="flex items-center space-x-2">
|
||||
<i class="fa fa-server"></i>
|
||||
<span class="font-bold">CCS圆周云境</span>
|
||||
</div>
|
||||
<p class="text-gray-400 text-sm mt-1">背景图片来源<a href="https://bluearchive-cn.com/">《蔚蓝档案》</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6 pt-6 border-t border-gray-700 text-center text-gray-400 text-sm">
|
||||
© 2025 圆周云境 | 版本 2.4.1(Alpha)
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- 返回顶部按钮 -->
|
||||
<a href="#home" id="backToTop" class="fixed bottom-8 right-8 bg-primary text-white w-12 h-12 rounded-full flex items-center justify-center opacity-0 invisible transition-all duration-300 shadow-lg hover:bg-primary/90 transform hover:scale-110">
|
||||
<i class="fa fa-arrow-up"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.addEventListener('load', function() {
|
||||
setTimeout(function() {
|
||||
document.getElementById('loader').classList.add('fade-out');
|
||||
}, 1000);
|
||||
|
||||
const backToTopButton = document.getElementById('backToTop');
|
||||
window.addEventListener('scroll', function() {
|
||||
if (window.pageYOffset > 300) {
|
||||
backToTopButton.classList.remove('opacity-0', 'invisible');
|
||||
backToTopButton.classList.add('opacity-100', 'visible');
|
||||
} else {
|
||||
backToTopButton.classList.add('opacity-0', 'invisible');
|
||||
backToTopButton.classList.remove('opacity-100', 'visible');
|
||||
}
|
||||
const navbar = document.querySelector('.navbar');
|
||||
if (window.pageYOffset > 100) {
|
||||
navbar.classList.add('py-2');
|
||||
navbar.classList.remove('py-3');
|
||||
} else {
|
||||
navbar.classList.add('py-3');
|
||||
navbar.classList.remove('py-2');
|
||||
}
|
||||
});
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const targetId = this.getAttribute('href');
|
||||
if (targetId === '#') return;
|
||||
|
||||
const targetElement = document.querySelector(targetId);
|
||||
if (targetElement) {
|
||||
window.scrollTo({
|
||||
top: targetElement.offsetTop - 80,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
411
index.html
Executable file
411
index.html
Executable file
@ -0,0 +1,411 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>维护通知</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.loli.net/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
background: linear-gradient(135deg, #1a202c 0%, #2d3748 100%);
|
||||
color: #fff;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
.card {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||||
overflow: hidden;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
.service-icon {
|
||||
position: relative;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 2rem;
|
||||
box-shadow: 0 0 30px rgba(255, 59, 48, 0.5);
|
||||
}
|
||||
.service-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(255, 59, 48, 0.8);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(0.95);
|
||||
opacity: 0.8;
|
||||
}
|
||||
70% {
|
||||
transform: scale(1.1);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.95);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.slide-up {
|
||||
animation: slideUp 0.8s ease forwards;
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
@keyframes slideUp {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
.particle {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(135deg, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0.1) 100%);
|
||||
}
|
||||
.shake {
|
||||
animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both;
|
||||
}
|
||||
@keyframes shake {
|
||||
10%, 90% {transform: translateX(-1px);}
|
||||
20%, 80% {transform: translateX(2px);}
|
||||
30%, 50%, 70% {transform: translateX(-4px);}
|
||||
40%, 60% {transform: translateX(4px);}
|
||||
}
|
||||
.animate-pulse {
|
||||
animation: pulse-fade 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
@keyframes pulse-fade {
|
||||
0%, 100% {opacity: 1;}
|
||||
50% {opacity: .5;}
|
||||
}
|
||||
.badge {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 1.5rem;
|
||||
background: rgba(255, 59, 48, 0.2);
|
||||
border: 1px solid rgba(255, 59, 48, 0.5);
|
||||
color: #ff3b30;
|
||||
}
|
||||
.timeline {
|
||||
position: relative;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
.timeline::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 6px;
|
||||
height: calc(100% - 12px);
|
||||
width: 2px;
|
||||
background: rgba(156, 163, 175, 0.5);
|
||||
}
|
||||
.timeline-item {
|
||||
position: relative;
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
.timeline-item:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.timeline-dot {
|
||||
position: absolute;
|
||||
left: -2rem;
|
||||
top: 0.25rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: 50%;
|
||||
background: #4f46e5;
|
||||
border: 2px solid #fff;
|
||||
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.3);
|
||||
}
|
||||
.service-status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 9999px;
|
||||
background: rgba(255, 59, 48, 0.2);
|
||||
border: 1px solid rgba(255, 59, 48, 0.5);
|
||||
color: #ff3b30;
|
||||
font-weight: 500;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.status-pulse {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: #ff3b30;
|
||||
border-radius: 50%;
|
||||
margin-right: 0.5rem;
|
||||
position: relative;
|
||||
}
|
||||
.status-pulse::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(255, 59, 48, 0.5);
|
||||
animation: status-ping 1.5s cubic-bezier(0, 0, 0.2, 1) infinite;
|
||||
}
|
||||
@keyframes status-ping {
|
||||
75%, 100% {
|
||||
transform: scale(2);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.service-feature {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.feature-icon {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border-radius: 50%;
|
||||
background: rgba(79, 70, 229, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 1rem;
|
||||
flex-shrink: 0;
|
||||
color: #818cf8;
|
||||
}
|
||||
|
||||
/* 音乐控制样式 */
|
||||
.music-control {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
z-index: 1000;
|
||||
background: rgba(255,255,255,0.1);
|
||||
backdrop-filter: blur(5px);
|
||||
border: 1px solid rgba(255,255,255,0.3);
|
||||
border-radius: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.music-control:hover {
|
||||
background: rgba(255,255,255,0.2);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.music-control.playing {
|
||||
animation: rotate 2s linear infinite;
|
||||
}
|
||||
.music-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
fill: #fff;
|
||||
}
|
||||
@keyframes rotate {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<audio id="bgMusic" src="./Music.mp3" autoplay loop volume="0.3" preload="auto"></audio>
|
||||
<div class="music-control" id="musicControl" onclick="toggleMusic()">
|
||||
<svg class="music-icon" viewBox="0 0 24 24">
|
||||
<path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="card p-6 md:p-8 slide-up" style="animation-delay: 0.1s;">
|
||||
<div class="text-center">
|
||||
<h1 class="text-3xl md:text-4xl font-bold mb-2 bg-gradient-to-r from-red-400 to-pink-600 bg-clip-text text-transparent">服务正在维护</h1>
|
||||
<p class="text-gray-300 mb-6">请耐心等待恢复</p>
|
||||
|
||||
<div class="service-status-badge">
|
||||
<span class="status-pulse"></span>
|
||||
服务维护中
|
||||
</div>
|
||||
|
||||
<div class="service-icon">
|
||||
<i class="fas fa-power-off text-5xl text-red-500"></i>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-800 bg-opacity-50 rounded-lg p-5 mb-6 max-w-2xl mx-auto">
|
||||
<h2 class="text-xl font-bold mb-3 text-yellow-300">
|
||||
<i class="fas fa-exclamation-triangle mr-2"></i>通知
|
||||
</h2>
|
||||
<p class="text-gray-300 mb-4">
|
||||
我们非常遗憾地通知您,我们已于 <span class="font-semibold text-red-400">2025年06月14日</span> 停止服务。
|
||||
</p>
|
||||
<p class="text-gray-300">
|
||||
请注意,所有账户数据将继续储存在我们的数据库中,<span class="font-semibold text-yellow-300">不会丢失数据</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="timeline mx-auto text-left max-w-lg mb-6">
|
||||
<h3 class="font-bold text-xl mb-4 text-center text-purple-300">服务维护时间线</h3>
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-dot"></div>
|
||||
<div class="font-medium text-purple-300">2025-05-30</div>
|
||||
<div class="text-gray-300">提交备案申请完毕</div>
|
||||
</div>
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-dot"></div>
|
||||
<div class="font-medium text-purple-300">2025-06-14</div>
|
||||
<div class="text-gray-300">服务停止,运行备案流程</div>
|
||||
</div>
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-dot"></div>
|
||||
<div class="font-medium text-purple-300">2025-06-XX</div>
|
||||
<div class="text-gray-300">等待备案下发</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card p-6 md:p-8 mt-6 slide-up" style="animation-delay: 0.3s;">
|
||||
<h2 class="text-xl font-bold mb-4 flex items-center">
|
||||
<i class="fas fa-question-circle mr-2 text-yellow-400"></i>
|
||||
常见问题
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
<div class="p-4 bg-gray-800 bg-opacity-50 rounded-lg">
|
||||
<h3 class="font-medium text-yellow-300 mb-2">为什么维护?</h3>
|
||||
<p class="text-gray-300">国内云产品使用带域名的 HTTP/HTTPS 服务或使用80,443端口时,需确保域名完成备案才能正常使用,备案期间完全无法使用服务,<span class="font-semibold text-red-400">此外,服务器项目源文件因失误遭到损坏,正在迁移备份!</span></p>
|
||||
</div>
|
||||
<div class="p-4 bg-gray-800 bg-opacity-50 rounded-lg">
|
||||
<h3 class="font-medium text-yellow-300 mb-2">为什么之前能访问?</h3>
|
||||
<p class="text-gray-300">之前本工作室采用香港计算节点运行网站服务,现迁移至境内服务器,并注册新域名ccsnetwork.cn用于备案。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="mt-8 text-center text-gray-400 text-sm slide-up" style="animation-delay: 0.6s;">
|
||||
<!--<div class="flex justify-center space-x-4 mb-4">
|
||||
<a href="#" class="hover:text-white transition-colors" title="微信">
|
||||
<i class="fab fa-weixin text-xl"></i>
|
||||
</a>
|
||||
<a href="#" class="hover:text-white transition-colors" title="QQ">
|
||||
<i class="fab fa-qq text-xl"></i>
|
||||
</a>
|
||||
</div>-->
|
||||
<p>© 2025 CCSIT圆周云境技术部门
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 背景音乐控制
|
||||
const audio = document.getElementById('bgMusic');
|
||||
const musicControl = document.getElementById('musicControl');
|
||||
let isPlaying = true;
|
||||
|
||||
function toggleMusic() {
|
||||
if (isPlaying) {
|
||||
audio.pause();
|
||||
musicControl.classList.remove('playing');
|
||||
} else {
|
||||
audio.play().catch(err => {
|
||||
console.log('音乐播放失败,可能需要用户交互后播放:', err);
|
||||
});
|
||||
musicControl.classList.add('playing');
|
||||
}
|
||||
isPlaying = !isPlaying;
|
||||
}
|
||||
|
||||
// 粒子效果
|
||||
function createParticles() {
|
||||
const container = document.body;
|
||||
const particleCount = 50;
|
||||
|
||||
for (let i = 0; i < particleCount; i++) {
|
||||
const size = Math.random() * 5 + 1;
|
||||
const particle = document.createElement('div');
|
||||
|
||||
particle.classList.add('particle');
|
||||
particle.style.width = `${size}px`;
|
||||
particle.style.height = `${size}px`;
|
||||
particle.style.opacity = Math.random() * 0.5 + 0.1;
|
||||
|
||||
// 随机位置
|
||||
particle.style.left = `${Math.random() * 100}vw`;
|
||||
particle.style.top = `${Math.random() * 100}vh`;
|
||||
|
||||
// 添加动画
|
||||
particle.style.animation = `moveParticle ${Math.random() * 60 + 30}s linear infinite`;
|
||||
|
||||
// 随机漂浮动画
|
||||
const keyframes = `
|
||||
@keyframes moveParticle {
|
||||
0% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
25% {
|
||||
transform: translate(${Math.random() * 100 - 50}px, ${Math.random() * 100 - 50}px);
|
||||
}
|
||||
50% {
|
||||
transform: translate(${Math.random() * 100 - 50}px, ${Math.random() * 100 - 50}px);
|
||||
}
|
||||
75% {
|
||||
transform: translate(${Math.random() * 100 - 50}px, ${Math.random() * 100 - 50}px);
|
||||
}
|
||||
100% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const styleSheet = document.createElement('style');
|
||||
styleSheet.textContent = keyframes;
|
||||
document.head.appendChild(styleSheet);
|
||||
|
||||
container.appendChild(particle);
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后执行
|
||||
window.addEventListener('load', () => {
|
||||
createParticles();
|
||||
document.addEventListener('click', () => {
|
||||
if (!isPlaying) {
|
||||
audio.play().then(() => {
|
||||
musicControl.classList.add('playing');
|
||||
isPlaying = true;
|
||||
});
|
||||
}
|
||||
}, { once: true });
|
||||
|
||||
// 添加滑动动画
|
||||
const elements = document.querySelectorAll('.slide-up');
|
||||
elements.forEach(element => {
|
||||
element.style.animationPlayState = 'running';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
BIN
logo.png
Executable file
BIN
logo.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
BIN
rainyun.jpg
Executable file
BIN
rainyun.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 2.1 MiB |
Loading…
x
Reference in New Issue
Block a user