' . htmlspecialchars($newFileName); } else { echo '文件重命名失败!'; } } else { echo '文件重命名失败,文件不存在或新文件名已存在。'; } } if (isset($_POST['saveContent'], $_POST['fileName'], $_POST['fileType'])) { $fileToSave = ($_POST['fileType'] === 'proxy') ? $proxyDir . 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($proxyDir); $configFiles = scandir($configDir); if ($proxyFiles !== false) { $proxyFiles = array_diff($proxyFiles, array('.', '..')); } 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') ? $proxyDir . 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; } } ?> 0) { ob_end_flush(); } function outputMessage($message) { if (!isset($_SESSION['notification_messages'])) { $_SESSION['notification_messages'] = []; } $_SESSION['notification_messages'][] = $message; } if (!isset($_SESSION['help_message'])) { $_SESSION['help_message'] = '
⚠️ 注意: 当前配置文件必须配合 Puernya 内核使用,不支持其他内核!
'; } if (!file_exists($configPath)) { mkdir($configPath, 0755, true); } if (!file_exists($configFile)) { file_put_contents($configFile, json_encode([])); } $subscriptionList = json_decode(file_get_contents($configFile), true); if (!$subscriptionList || !is_array($subscriptionList)) { $subscriptionList = []; for ($i = 1; $i <= 3; $i++) { $subscriptionList[$i - 1] = [ 'url' => '', 'file_name' => "subscription_{$i}.yaml", ]; } } if (isset($_POST['saveSubscription'])) { $index = intval($_POST['index']); if ($index >= 0 && $index < 3) { $url = $_POST['subscription_url'] ?? ''; $customFileName = $_POST['custom_file_name'] ?? "subscription_{$index}.yaml"; $subscriptionList[$index]['url'] = $url; $subscriptionList[$index]['file_name'] = $customFileName; if (!empty($url)) { $finalPath = $configPath . $customFileName; $command = sprintf( "wget -q --show-progress -O %s %s", escapeshellarg($finalPath), escapeshellarg($url) ); exec($command . ' 2>&1', $output, $return_var); if ($return_var === 0) { outputMessage("订阅链接 {$url} 更新成功!文件已保存到: {$finalPath}"); } else { outputMessage("配置更新失败!错误信息: " . implode("\n", $output)); } } else { outputMessage("第" . ($index + 1) . "个订阅链接为空!"); } file_put_contents($configFile, json_encode($subscriptionList)); } } $updateCompleted = isset($_POST['saveSubscription']); ?> '', 'file_name' => 'config.json', ], [ 'url' => '', 'file_name' => '', ], [ 'url' => '', 'file_name' => '', ] ]; if (!file_exists($subscriptionPath)) { mkdir($subscriptionPath, 0755, true); } if (!file_exists($dataFile)) { file_put_contents($dataFile, json_encode(['subscriptions' => $defaultSubscriptions], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } $subscriptionData = json_decode(file_get_contents($dataFile), true); if (!isset($subscriptionData['subscriptions']) || !is_array($subscriptionData['subscriptions'])) { $subscriptionData['subscriptions'] = $defaultSubscriptions; } if (isset($_POST['update_index'])) { $index = intval($_POST['update_index']); $subscriptionUrl = $_POST["subscription_url_$index"] ?? ''; $customFileName = ($_POST["custom_file_name_$index"] ?? '') ?: 'config.json'; if ($index < 0 || $index >= count($subscriptionData['subscriptions'])) { $message = "无效的订阅索引!"; } elseif (empty($subscriptionUrl)) { $message = "订阅 $index 的链接为空!"; } else { $subscriptionData['subscriptions'][$index]['url'] = $subscriptionUrl; $subscriptionData['subscriptions'][$index]['file_name'] = $customFileName; $finalPath = $subscriptionPath . $customFileName; $originalContent = file_exists($finalPath) ? file_get_contents($finalPath) : ''; $command = sprintf( "wget -q --header='Accept-Charset: utf-8' -O %s %s", escapeshellarg($finalPath), escapeshellarg($subscriptionUrl) ); exec($command . ' 2>&1', $output, $return_var); if ($return_var !== 0) { $message = "订阅 $index 无法下载文件。wget 错误信息: " . implode("\n", $output); } else { $fileContent = file_get_contents($finalPath); $fileContent = str_replace("\xEF\xBB\xBF", '', $fileContent); if (!isUtf8($fileContent)) { $fileContent = utf8_encode($fileContent); } $parsedData = json_decode($fileContent, true); if ($parsedData === null && json_last_error() !== JSON_ERROR_NONE) { file_put_contents($finalPath, $originalContent); $message = "订阅 $index 解析 JSON 数据失败!错误信息: " . json_last_error_msg(); } else { if (isset($parsedData['inbounds'])) { $newInbounds = []; foreach ($parsedData['inbounds'] as $inbound) { if (isset($inbound['type']) && $inbound['type'] === 'mixed' && $inbound['tag'] === 'mixed-in') { $newInbounds[] = $inbound; } elseif (isset($inbound['type']) && $inbound['type'] === 'tun') { continue; } } $newInbounds[] = [ "tag" => "tun", "type" => "tun", "inet4_address" => "172.19.0.0/30", "inet6_address" => "fdfe:dcba:9876::0/126", "stack" => "system", "auto_route" => true, "strict_route" => true, "sniff" => true, "platform" => [ "http_proxy" => [ "enabled" => true, "server" => "0.0.0.0", "server_port" => 7890 ] ] ]; $newInbounds[] = [ "tag" => "mixed", "type" => "mixed", "listen" => "0.0.0.0", "listen_port" => 7890, "sniff" => true ]; $parsedData['inbounds'] = $newInbounds; } if (isset($parsedData['experimental']['clash_api'])) { $parsedData['experimental']['clash_api'] = [ "external_ui" => "/etc/neko/ui/", "external_controller" => "0.0.0.0:9090", "secret" => "Akun" ]; } $fileContent = json_encode($parsedData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); if (file_put_contents($finalPath, $fileContent) === false) { $message = "订阅 $index 无法保存文件到: $finalPath"; } else { $message = "订阅 $index 更新成功!文件已保存到: {$finalPath},并成功解析和替换 JSON 数据。"; } } } file_put_contents($dataFile, json_encode($subscriptionData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); } } function isUtf8($string) { $encoded = utf8_encode($string); return $encoded === $string; } ?> Sing-box - Neko
🏠 首页 📂 Mihomo 🗂️ Sing-box 💹 订阅转换 📦 文件助手

Sing-box 文件管理

代理文件管理 ➤ p核专用
文件名 大小 修改时间 执行操作
配置文件管理
文件名 大小 修改时间 执行操作

Sing-box 订阅

订阅链接
>

订阅管理 ➤ p核专用

1. 对于首次使用 Sing-box 的用户,必须将核心更新至版本 v1.10.0 或更高版本。确保将出站和入站/转发防火墙规则都设置为“接受”并启用它们。
2. 注意: 通用模板(puernya.json)最多支持3 个订阅链接。请勿更改默认文件名称,并确保使用 puernya 内核。
3. 支持通用格式订阅,无需转换。
4. 保存与更新: 填写完毕后,请点击"更新配置"按钮进行保存。
5. 说明: 上述 Sing-box 订阅适用于原版 Sing-box 内核,仅支持后缀为 Sing-box 格式的机场订阅链接。对于通用订阅,请使用转换模板以确保兼容性。
订阅链接