文件上传成功:' . htmlspecialchars(basename($file['name'])) . '';
} else {
echo '
文件上传失败!
';
}
} else {
echo '上传错误:' . $file['error'] . '
';
}
}
if (isset($_FILES['configFileInput'])) {
$file = $_FILES['configFileInput'];
$uploadFilePath = $configDir . basename($file['name']);
if ($file['error'] === UPLOAD_ERR_OK) {
if (move_uploaded_file($file['tmp_name'], $uploadFilePath)) {
echo '配置文件上传成功:' . htmlspecialchars(basename($file['name'])) . '
';
} else {
echo '配置文件上传失败!
';
}
} else {
echo '上传错误:' . $file['error'] . '
';
}
}
if (isset($_POST['deleteFile'])) {
$fileToDelete = $uploadDir . basename($_POST['deleteFile']);
if (file_exists($fileToDelete) && unlink($fileToDelete)) {
echo '
文件删除成功:' . htmlspecialchars(basename($_POST['deleteFile'])) . '
';
} else {
echo '文件删除失败!
';
}
}
if (isset($_POST['deleteConfigFile'])) {
$fileToDelete = $configDir . basename($_POST['deleteConfigFile']);
if (file_exists($fileToDelete) && unlink($fileToDelete)) {
echo '
配置文件删除成功:' . htmlspecialchars(basename($_POST['deleteConfigFile'])) . '
';
} else {
echo '配置文件删除失败!
';
}
}
if (isset($_POST['oldFileName'], $_POST['newFileName'], $_POST['fileType'])) {
$oldFileName = basename($_POST['oldFileName']);
$newFileName = basename($_POST['newFileName']);
$fileType = $_POST['fileType'];
if ($fileType === 'proxy') {
$oldFilePath = $uploadDir. $oldFileName;
$newFilePath = $uploadDir. $newFileName;
} elseif ($fileType === 'config') {
$oldFilePath = $configDir . $oldFileName;
$newFilePath = $configDir . $newFileName;
} else {
echo '无效的文件类型';
exit;
}
if (file_exists($oldFilePath) && !file_exists($newFilePath)) {
if (rename($oldFilePath, $newFilePath)) {
echo '
文件重命名成功:' . htmlspecialchars($oldFileName) . ' -> ' . htmlspecialchars($newFileName) . '
';
} else {
echo '文件重命名失败!
';
}
} else {
echo '文件重命名失败,文件不存在或新文件名已存在。
';
}
}
if (isset($_POST['saveContent'], $_POST['fileName'], $_POST['fileType'])) {
$fileToSave = ($_POST['fileType'] === 'proxy') ? $uploadDir . basename($_POST['fileName']) : $configDir . basename($_POST['fileName']);
$contentToSave = $_POST['saveContent'];
file_put_contents($fileToSave, $contentToSave);
echo '文件内容已更新:' . htmlspecialchars(basename($fileToSave)) . '
';
}
}
function formatFileModificationTime($filePath) {
if (file_exists($filePath)) {
$fileModTime = filemtime($filePath);
return date('Y-m-d H:i:s', $fileModTime);
} else {
return '文件不存在';
}
}
$proxyFiles = scandir($uploadDir);
$configFiles = scandir($configDir);
if ($proxyFiles !== false) {
$proxyFiles = array_diff($proxyFiles, array('.', '..'));
$proxyFiles = array_filter($proxyFiles, function($file) {
return pathinfo($file, PATHINFO_EXTENSION) !== 'txt';
});
} else {
$proxyFiles = [];
}
if ($configFiles !== false) {
$configFiles = array_diff($configFiles, array('.', '..'));
} else {
$configFiles = [];
}
function formatSize($size) {
$units = array('B', 'KB', 'MB', 'GB', 'TB');
$unit = 0;
while ($size >= 1024 && $unit < count($units) - 1) {
$size /= 1024;
$unit++;
}
return round($size, 2) . ' ' . $units[$unit];
}
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['editFile'], $_GET['fileType'])) {
$filePath = ($_GET['fileType'] === 'proxy') ? $uploadDir. basename($_GET['editFile']) : $configDir . basename($_GET['editFile']);
if (file_exists($filePath)) {
header('Content-Type: text/plain');
echo file_get_contents($filePath);
exit;
} else {
echo '文件不存在';
exit;
}
}
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['downloadFile'], $_GET['fileType'])) {
$fileType = $_GET['fileType'];
$fileName = basename($_GET['downloadFile']);
$filePath = ($fileType === 'proxy') ? $uploadDir . $fileName : $configDir . $fileName;
if (file_exists($filePath)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
exit;
} else {
echo '文件不存在。';
}
}
?>
'',
'file_name' => "subscription_" . ($i + 1) . ".yaml",
];
}
}
if (isset($_POST['update'])) {
$index = intval($_POST['index']);
$url = trim($_POST['subscription_url'] ?? '');
$customFileName = trim($_POST['custom_file_name'] ?? "subscription_" . ($index + 1) . ".yaml");
$subscriptions[$index]['url'] = $url;
$subscriptions[$index]['file_name'] = $customFileName;
if (!empty($url)) {
$tempPath = $subscriptionPath . $customFileName . ".temp";
$finalPath = $subscriptionPath . $customFileName;
$command = "curl -s -L -o {$tempPath} {$url}";
exec($command . ' 2>&1', $output, $return_var);
if ($return_var !== 0) {
$command = "wget -q --show-progress -O {$tempPath} {$url}";
exec($command . ' 2>&1', $output, $return_var);
}
if ($return_var === 0) {
$_SESSION['update_logs'] = [];
storeUpdateLog("✅ 订阅 " . htmlspecialchars($url) . " 已下载并保存到临时文件: " . htmlspecialchars($tempPath));
$fileContent = file_get_contents($tempPath);
if (base64_encode(base64_decode($fileContent, true)) === $fileContent) {
$decodedContent = base64_decode($fileContent);
if ($decodedContent !== false && strlen($decodedContent) > 0) {
file_put_contents($finalPath, "# Clash Meta Config\n\n" . $decodedContent);
storeUpdateLog("📂 Base64 解码成功,配置已保存到: " . htmlspecialchars($finalPath));
unlink($tempPath);
$notificationMessage = '更新成功';
$updateCompleted = true;
} else {
storeUpdateLog("⚠️ Base64 解码失败,请检查订阅链接内容!");
unlink($tempPath);
$notificationMessage = '更新失败';
}
}
elseif (substr($fileContent, 0, 2) === "\x1f\x8b") {
$decompressedContent = gzdecode($fileContent);
if ($decompressedContent !== false) {
file_put_contents($finalPath, "# Clash Meta Config\n\n" . $decompressedContent);
storeUpdateLog("📂 Gzip 解压成功,配置已保存到: " . htmlspecialchars($finalPath));
unlink($tempPath);
$notificationMessage = '更新成功';
$updateCompleted = true;
} else {
storeUpdateLog("⚠️ Gzip 解压失败,请检查订阅链接格式!");
unlink($tempPath);
$notificationMessage = '更新失败';
}
}
else {
rename($tempPath, $finalPath);
storeUpdateLog("✅ 订阅内容已成功下载,无需解码");
$notificationMessage = '更新成功';
$updateCompleted = true;
}
} else {
storeUpdateLog("❌ 订阅更新失败!错误信息: " . implode("\n", $output));
unlink($tempPath);
$notificationMessage = '更新失败';
}
} else {
storeUpdateLog("⚠️ 第" . ($index + 1) . "个订阅链接为空!");
$notificationMessage = '更新失败';
}
file_put_contents($subscriptionFile, json_encode($subscriptions));
}
?>
> "\$LOG_FILE"
}
log "开始处理订阅更新任务..."
if [ ! -f "\$JSON_FILE" ]; then
log "❌ 错误: JSON 文件不存在: \$JSON_FILE"
exit 1
fi
jq -c '.[]' "\$JSON_FILE" | while read -r ITEM; do
URL=\$(echo "\$ITEM" | jq -r '.url')
FILE_NAME=\$(echo "\$ITEM" | jq -r '.file_name')
if [ -z "\$URL" ] || [ "\$URL" == "null" ]; then
log "⚠️ 跳过空的订阅链接,文件名: \$FILE_NAME"
continue
fi
if [ -z "\$FILE_NAME" ] || [ "\$FILE_NAME" == "null" ]; then
log "❌ 错误: 文件名为空,跳过此链接: \$URL"
continue
fi
SAVE_PATH="\$SAVE_DIR/\$FILE_NAME"
TEMP_PATH="\$SAVE_PATH.temp"
log "🔄 正在下载: \$URL 到临时文件: \$TEMP_PATH"
curl -s -L -o "\$TEMP_PATH" "\$URL"
if [ \$? -ne 0 ]; then
wget -q -O "\$TEMP_PATH" "\$URL"
fi
if [ \$? -eq 0 ]; then
log "✅ 文件下载成功: \$TEMP_PATH"
if base64 -d "\$TEMP_PATH" > /dev/null 2>&1; then
base64 -d "\$TEMP_PATH" > "\$SAVE_PATH"
if [ \$? -eq 0 ]; then
log "📂 Base64 解码成功,配置已保存: \$SAVE_PATH"
rm -f "\$TEMP_PATH"
else
log "⚠️ Base64 解码失败: \$SAVE_PATH"
rm -f "\$TEMP_PATH"
fi
elif file "\$TEMP_PATH" | grep -q "gzip compressed"; then
gunzip -c "\$TEMP_PATH" > "\$SAVE_PATH"
if [ \$? -eq 0 ]; then
log "📂 Gzip 解压成功,配置已保存: \$SAVE_PATH"
rm -f "\$TEMP_PATH"
else
log "⚠️ Gzip 解压失败: \$SAVE_PATH"
rm -f "\$TEMP_PATH"
fi
else
mv "\$TEMP_PATH" "\$SAVE_PATH"
log "✅ 订阅内容已成功下载,无需解码"
fi
else
log "❌ 订阅更新失败: \$URL"
rm -f "\$TEMP_PATH"
fi
done
log "🚀 所有订阅链接更新完成!"
EOL;
if (file_put_contents($shellScriptPath, $shellScriptContent) !== false) {
chmod($shellScriptPath, 0755);
echo "Shell 脚本已创建成功!路径: $shellScriptPath
";
} else {
echo "无法创建 Shell 脚本,请检查权限。
";
}
}
}
?>
Cron 表达式不能为空。";
exit;
}
$cronJob = "$cronExpression /etc/neko/core/update_mihomo.sh";
exec("crontab -l | grep -v '/etc/neko/core/update_mihomo.sh' | crontab -", $output, $returnVarRemove);
if ($returnVarRemove === 0) {
file_put_contents($CRON_LOG_FILE, date('[ H:i:s ] ') . "成功移除旧的 Cron 任务。\n", FILE_APPEND);
} else {
file_put_contents($CRON_LOG_FILE, date('[ H:i:s ] ') . "移除旧的 Cron 任务失败。\n", FILE_APPEND);
}
exec("(crontab -l; echo '$cronJob') | crontab -", $output, $returnVarAdd);
if ($returnVarAdd === 0) {
file_put_contents($CRON_LOG_FILE, date('[ H:i:s ] ') . "成功添加新的 Cron 任务: $cronJob\n", FILE_APPEND);
echo "Cron 任务已成功添加或更新!
";
} else {
file_put_contents($CRON_LOG_FILE, date('[ H:i:s ] ') . "添加新的 Cron 任务失败。\n", FILE_APPEND);
echo "无法添加或更新 Cron 任务,请检查服务器权限。
";
}
}
}
?>
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb',
'geosite' => 'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat',
'cache' => 'https://github.com/Thaolga/neko/raw/main/cache.db'
];
$download_directories = [
'geoip' => '/etc/neko/',
'geosite' => '/etc/neko/',
'cache' => '/www/nekobox/'
];
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['file'])) {
$file = $_GET['file'];
if (isset($file_urls[$file])) {
$file_url = $file_urls[$file];
$destination_directory = $download_directories[$file];
$destination_path = $destination_directory . basename($file_url);
if (download_file($file_url, $destination_path)) {
echo "文件成功下载到 $destination_path
";
} else {
echo "文件下载失败
";
}
} else {
echo "无效的文件请求";
}
}
function download_file($url, $destination) {
$ch = curl_init($url);
$fp = fopen($destination, 'wb');
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$result = curl_exec($ch);
curl_close($ch);
fclose($fp);
return $result !== false;
}
?>
Mihomo - NekoBox
⚠️ 使用说明:
- 通用模板(mihomo.yaml)最多支持6个订阅链接
- 请勿更改默认文件名称
- 该模板支持所有格式订阅链接,无需额外转换
文件管理
文件名 |
大小 |
最后修改时间 |
文件类型 |
执行操作 |
$file) {
$filePath = $allFilePaths[$index];
$fileType = $fileTypes[$index];
?>
|
|
|
|
|
Mihomo订阅管理
未找到订阅信息。
自动更新