mainprogram #1

Merged
dyzzyduq merged 3 commits from mainprogram into main 2025-06-17 01:58:26 +00:00
45 changed files with 7219 additions and 0 deletions

1
.user.ini Normal file
View File

@ -0,0 +1 @@
open_basedir=/www/wwwroot/none.pw/:/tmp/

View File

@ -0,0 +1 @@
PyRloZHAxDYExCkaKSZojdNxaxxdnkzswI17xrhTRgg.FrPeRAfsRh1hY5jyno0CKC5CMPMAZNVuh0kErn8p0BI

BIN
Music.mp3 Executable file

Binary file not shown.

18
auth/config.php Executable file
View 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
View 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
View 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')">&times;</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')">&times;</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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View 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();
?>

View 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();
?>

View 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();
?>

View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 KiB

64
console/change_password.php Executable file
View 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
View 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
View 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>&copy; 2025 圆周云境. 保留所有权利。</p>
</footer>
</body>
</html>

67
console/delete_account.php Executable file
View 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
View 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

File diff suppressed because it is too large Load Diff

1
console/ins/.user.ini Normal file
View File

@ -0,0 +1 @@
open_basedir=/www/wwwroot/ccsbeta/console/ins/:/tmp/

3
console/ins/config.php Executable file
View File

@ -0,0 +1,3 @@
<?php
$apiKey = '5ffc6f20166340fb9de0a12a02252b98';
$apiUrl = 'http://154.222.30.227:23333/api';

271
console/ins/files.php Executable file
View 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
View 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> &gt; <a href="#" class="hover:text-primary">服务器列表</a> &gt; <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">&gt;</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
View 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">&gt;</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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

260
console/player/index.php Executable file
View 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
View 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
View 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>&copy; 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
View 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
View 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
View 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.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
logo.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
rainyun.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB