2024-10-26 10:38:22 +08:00
|
|
|
|
<?php
|
|
|
|
|
ob_start();
|
|
|
|
|
include './cfg.php';
|
|
|
|
|
ini_set('memory_limit', '256M');
|
2024-12-12 13:24:47 +08:00
|
|
|
|
|
2024-12-11 10:59:02 +08:00
|
|
|
|
$subscription_file = '/etc/neko/subscription.txt';
|
2024-10-26 10:38:22 +08:00
|
|
|
|
$download_path = '/etc/neko/config/';
|
2024-12-11 14:17:01 +08:00
|
|
|
|
$sh_script_path = '/etc/neko/core/update_config.sh';
|
2024-10-26 10:38:22 +08:00
|
|
|
|
$log_file = '/var/log/neko_update.log';
|
|
|
|
|
|
2024-12-12 13:24:47 +08:00
|
|
|
|
$current_subscription_url = '';
|
|
|
|
|
if (isset($_POST['subscription_url'])) {
|
|
|
|
|
$current_subscription_url = $_POST['subscription_url'];
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 10:38:22 +08:00
|
|
|
|
function logMessage($message) {
|
|
|
|
|
global $log_file;
|
|
|
|
|
$timestamp = date('Y-m-d H:i:s');
|
|
|
|
|
file_put_contents($log_file, "[$timestamp] $message\n", FILE_APPEND);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-12 13:24:47 +08:00
|
|
|
|
function buildFinalUrl($subscription_url, $config_url, $include, $exclude, $backend_url) {
|
|
|
|
|
$encoded_subscription_url = urlencode($subscription_url);
|
|
|
|
|
$encoded_config_url = urlencode($config_url);
|
|
|
|
|
$encoded_include = urlencode($include);
|
|
|
|
|
$encoded_exclude = urlencode($exclude);
|
|
|
|
|
|
|
|
|
|
$final_url = "{$backend_url}target=clash&url={$encoded_subscription_url}&insert=false&config={$encoded_config_url}";
|
|
|
|
|
|
|
|
|
|
if (!empty($include)) {
|
|
|
|
|
$final_url .= "&include={$encoded_include}";
|
|
|
|
|
}
|
|
|
|
|
if (!empty($exclude)) {
|
|
|
|
|
$final_url .= "&exclude={$encoded_exclude}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$final_url .= "&emoji=true&list=false&xudp=false&udp=false&tfo=false&expand=true&scv=false&fdn=false&new_name=true";
|
|
|
|
|
|
|
|
|
|
return $final_url;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 10:38:22 +08:00
|
|
|
|
function saveSubscriptionUrlToFile($url, $file) {
|
|
|
|
|
$success = file_put_contents($file, $url) !== false;
|
|
|
|
|
logMessage($success ? "订阅链接已保存到 $file" : "保存订阅链接失败到 $file");
|
|
|
|
|
return $success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function transformContent($content) {
|
|
|
|
|
$new_config_start = "redir-port: 7892
|
|
|
|
|
port: 7890
|
|
|
|
|
socks-port: 7891
|
|
|
|
|
mixed-port: 7893
|
|
|
|
|
mode: rule
|
|
|
|
|
log-level: info
|
|
|
|
|
allow-lan: true
|
|
|
|
|
unified-delay: true
|
|
|
|
|
external-controller: 0.0.0.0:9090
|
|
|
|
|
secret: Akun
|
|
|
|
|
bind-address: 0.0.0.0
|
|
|
|
|
external-ui: ui
|
|
|
|
|
tproxy-port: 7895
|
2024-12-11 10:59:02 +08:00
|
|
|
|
tcp-concurrent: true
|
2024-10-26 10:38:22 +08:00
|
|
|
|
enable-process: true
|
|
|
|
|
find-process-mode: always
|
|
|
|
|
ipv6: true
|
|
|
|
|
experimental:
|
|
|
|
|
ignore-resolve-fail: true
|
|
|
|
|
sniff-tls-sni: true
|
|
|
|
|
tracing: true
|
|
|
|
|
hosts:
|
|
|
|
|
\"localhost\": 127.0.0.1
|
|
|
|
|
profile:
|
|
|
|
|
store-selected: true
|
|
|
|
|
store-fake-ip: true
|
|
|
|
|
sniffer:
|
|
|
|
|
enable: true
|
|
|
|
|
sniff:
|
|
|
|
|
http: { ports: [1-442, 444-8442, 8444-65535], override-destination: true }
|
|
|
|
|
tls: { ports: [1-79, 81-8079, 8081-65535], override-destination: true }
|
|
|
|
|
force-domain:
|
|
|
|
|
- \"+.v2ex.com\"
|
|
|
|
|
- www.google.com
|
|
|
|
|
- google.com
|
|
|
|
|
skip-domain:
|
|
|
|
|
- Mijia Cloud
|
|
|
|
|
- dlg.io.mi.com
|
|
|
|
|
sniffing:
|
|
|
|
|
- tls
|
|
|
|
|
- http
|
|
|
|
|
port-whitelist:
|
|
|
|
|
- \"80\"
|
|
|
|
|
- \"443\"
|
|
|
|
|
tun:
|
|
|
|
|
enable: true
|
|
|
|
|
prefer-h3: true
|
|
|
|
|
listen: 0.0.0.0:53
|
|
|
|
|
stack: gvisor
|
|
|
|
|
dns-hijack:
|
|
|
|
|
- \"any:53\"
|
|
|
|
|
- \"tcp://any:53\"
|
|
|
|
|
auto-redir: true
|
|
|
|
|
auto-route: true
|
|
|
|
|
auto-detect-interface: true
|
|
|
|
|
dns:
|
|
|
|
|
enable: true
|
|
|
|
|
ipv6: true
|
|
|
|
|
default-nameserver:
|
|
|
|
|
- '1.1.1.1'
|
|
|
|
|
- '8.8.8.8'
|
|
|
|
|
enhanced-mode: fake-ip
|
|
|
|
|
fake-ip-range: 198.18.0.1/16
|
|
|
|
|
fake-ip-filter:
|
|
|
|
|
- 'stun.*.*'
|
|
|
|
|
- 'stun.*.*.*'
|
|
|
|
|
- '+.stun.*.*'
|
|
|
|
|
- '+.stun.*.*.*'
|
|
|
|
|
- '+.stun.*.*.*.*'
|
|
|
|
|
- '+.stun.*.*.*.*.*'
|
|
|
|
|
- '*.lan'
|
|
|
|
|
- '+.msftncsi.com'
|
|
|
|
|
- msftconnecttest.com
|
|
|
|
|
- 'time?.*.com'
|
|
|
|
|
- 'time.*.com'
|
|
|
|
|
- 'time.*.gov'
|
|
|
|
|
- 'time.*.apple.com'
|
|
|
|
|
- time-ios.apple.com
|
|
|
|
|
- 'time1.*.com'
|
|
|
|
|
- 'time2.*.com'
|
|
|
|
|
- 'time3.*.com'
|
|
|
|
|
- 'time4.*.com'
|
|
|
|
|
- 'time5.*.com'
|
|
|
|
|
- 'time6.*.com'
|
|
|
|
|
- 'time7.*.com'
|
|
|
|
|
- 'ntp?.*.com'
|
|
|
|
|
- 'ntp.*.com'
|
|
|
|
|
- 'ntp1.*.com'
|
|
|
|
|
- 'ntp2.*.com'
|
|
|
|
|
- 'ntp3.*.com'
|
|
|
|
|
- 'ntp4.*.com'
|
|
|
|
|
- 'ntp5.*.com'
|
|
|
|
|
- 'ntp6.*.com'
|
|
|
|
|
- 'ntp7.*.com'
|
|
|
|
|
- '+.pool.ntp.org'
|
|
|
|
|
- '+.ipv6.microsoft.com'
|
|
|
|
|
- speedtest.cros.wr.pvp.net
|
|
|
|
|
- network-test.debian.org
|
|
|
|
|
- detectportal.firefox.com
|
|
|
|
|
- cable.auth.com
|
|
|
|
|
- miwifi.com
|
|
|
|
|
- routerlogin.com
|
|
|
|
|
- routerlogin.net
|
|
|
|
|
- tendawifi.com
|
|
|
|
|
- tendawifi.net
|
|
|
|
|
- tplinklogin.net
|
|
|
|
|
- tplinkwifi.net
|
|
|
|
|
- '*.xiami.com'
|
|
|
|
|
- tplinkrepeater.net
|
|
|
|
|
- router.asus.com
|
|
|
|
|
- '*.*.*.srv.nintendo.net'
|
|
|
|
|
- '*.*.stun.playstation.net'
|
|
|
|
|
- '*.openwrt.pool.ntp.org'
|
|
|
|
|
- resolver1.opendns.com
|
|
|
|
|
- 'GC._msDCS.*.*'
|
|
|
|
|
- 'DC._msDCS.*.*'
|
|
|
|
|
- 'PDC._msDCS.*.*'
|
|
|
|
|
use-hosts: true
|
|
|
|
|
nameserver:
|
|
|
|
|
- '8.8.4.4'
|
|
|
|
|
- '1.0.0.1'
|
|
|
|
|
- \"https://1.0.0.1/dns-query\"
|
|
|
|
|
- \"https://8.8.4.4/dns-query\"
|
|
|
|
|
";
|
|
|
|
|
|
|
|
|
|
$parts = explode('proxies:', $content, 2);
|
|
|
|
|
if (count($parts) == 2) {
|
|
|
|
|
return $new_config_start . "\nproxies:" . $parts[1];
|
|
|
|
|
} else {
|
|
|
|
|
return $content;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function saveSubscriptionContentToYaml($url, $filename) {
|
|
|
|
|
global $download_path;
|
|
|
|
|
|
2024-12-11 14:17:01 +08:00
|
|
|
|
if (pathinfo($filename, PATHINFO_EXTENSION) !== 'yaml') {
|
|
|
|
|
$filename .= '.yaml';
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 10:38:22 +08:00
|
|
|
|
if (strpbrk($filename, "!@#$%^&*()+=[]\\\';,/{}|\":<>?") !== false) {
|
|
|
|
|
$message = "文件名包含非法字符,请使用字母、数字、点、下划线或横杠。";
|
|
|
|
|
logMessage($message);
|
|
|
|
|
return $message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_dir($download_path)) {
|
|
|
|
|
if (!mkdir($download_path, 0755, true)) {
|
|
|
|
|
$message = "无法创建目录:$download_path";
|
|
|
|
|
logMessage($message);
|
|
|
|
|
return $message;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-12 13:24:47 +08:00
|
|
|
|
$output_file = escapeshellarg($download_path . $filename);
|
|
|
|
|
$command = "wget -q --no-check-certificate -O $output_file " . escapeshellarg($url);
|
|
|
|
|
exec($command, $output, $return_var);
|
|
|
|
|
if ($return_var !== 0) {
|
|
|
|
|
$message = "wget 错误,无法获取订阅内容。请检查链接是否正确。";
|
|
|
|
|
logMessage($message);
|
|
|
|
|
}
|
2024-10-26 10:38:22 +08:00
|
|
|
|
$ch = curl_init();
|
|
|
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
|
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
|
|
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|
|
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
|
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
|
|
|
|
$subscription_data = curl_exec($ch);
|
|
|
|
|
|
|
|
|
|
if (curl_errno($ch)) {
|
|
|
|
|
$error_msg = curl_error($ch);
|
|
|
|
|
curl_close($ch);
|
|
|
|
|
$message = "cURL 错误: $error_msg";
|
|
|
|
|
logMessage($message);
|
|
|
|
|
return $message;
|
|
|
|
|
}
|
|
|
|
|
curl_close($ch);
|
|
|
|
|
|
2024-12-12 13:24:47 +08:00
|
|
|
|
if (empty($subscription_data)) {
|
2024-10-26 10:38:22 +08:00
|
|
|
|
$message = "无法获取订阅内容。请检查链接是否正确。";
|
|
|
|
|
logMessage($message);
|
|
|
|
|
return $message;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-12 13:24:47 +08:00
|
|
|
|
$decoded_data = (base64_decode($subscription_data, true) !== false) ? base64_decode($subscription_data) : $subscription_data;
|
2024-10-26 10:38:22 +08:00
|
|
|
|
$transformed_data = transformContent($decoded_data);
|
|
|
|
|
|
|
|
|
|
$file_path = $download_path . $filename;
|
|
|
|
|
$success = file_put_contents($file_path, $transformed_data) !== false;
|
|
|
|
|
$message = $success ? "内容已成功保存到:$file_path" : "文件保存失败。";
|
|
|
|
|
logMessage($message);
|
|
|
|
|
return $message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function generateShellScript() {
|
2024-12-11 10:59:02 +08:00
|
|
|
|
global $subscription_file, $download_path, $sh_script_path;
|
2024-10-26 10:38:22 +08:00
|
|
|
|
|
|
|
|
|
$sh_script_content = <<<EOD
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
|
|
SUBSCRIPTION_FILE='$subscription_file'
|
|
|
|
|
DOWNLOAD_PATH='$download_path'
|
|
|
|
|
DEST_PATH='/etc/neko/config/config.yaml'
|
|
|
|
|
|
|
|
|
|
if [ ! -f "\$SUBSCRIPTION_FILE" ]; then
|
|
|
|
|
echo "未找到订阅文件: \$SUBSCRIPTION_FILE"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
SUBSCRIPTION_URL=\$(cat "\$SUBSCRIPTION_FILE")
|
|
|
|
|
|
2024-12-12 13:24:47 +08:00
|
|
|
|
subscription_data=\$(wget -qO- "\$SUBSCRIPTION_URL")
|
2024-12-11 10:59:02 +08:00
|
|
|
|
if [ -z "\$subscription_data" ]; then
|
|
|
|
|
echo "无法获取订阅内容,请检查订阅链接。"
|
2024-10-26 10:38:22 +08:00
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
2024-12-11 10:59:02 +08:00
|
|
|
|
new_config_start="redir-port: 7892
|
|
|
|
|
port: 7890
|
|
|
|
|
socks-port: 7891
|
|
|
|
|
mixed-port: 7893
|
|
|
|
|
mode: rule
|
|
|
|
|
log-level: info
|
|
|
|
|
allow-lan: true
|
|
|
|
|
unified-delay: true
|
|
|
|
|
external-controller: 0.0.0.0:9090
|
|
|
|
|
secret: Akun
|
|
|
|
|
bind-address: 0.0.0.0
|
|
|
|
|
external-ui: ui
|
|
|
|
|
tproxy-port: 7895
|
|
|
|
|
tcp-concurrent: true
|
|
|
|
|
enable-process: true
|
|
|
|
|
find-process-mode: always
|
|
|
|
|
ipv6: true
|
|
|
|
|
experimental:
|
|
|
|
|
ignore-resolve-fail: true
|
|
|
|
|
sniff-tls-sni: true
|
|
|
|
|
tracing: true
|
|
|
|
|
hosts:
|
|
|
|
|
\"localhost\": 127.0.0.1
|
|
|
|
|
profile:
|
|
|
|
|
store-selected: true
|
|
|
|
|
store-fake-ip: true
|
|
|
|
|
sniffer:
|
|
|
|
|
enable: true
|
|
|
|
|
sniff:
|
|
|
|
|
http: { ports: [1-442, 444-8442, 8444-65535], override-destination: true }
|
|
|
|
|
tls: { ports: [1-79, 81-8079, 8081-65535], override-destination: true }
|
|
|
|
|
force-domain:
|
|
|
|
|
- \"+.v2ex.com\"
|
|
|
|
|
- www.google.com
|
|
|
|
|
- google.com
|
|
|
|
|
skip-domain:
|
|
|
|
|
- Mijia Cloud
|
|
|
|
|
- dlg.io.mi.com
|
|
|
|
|
sniffing:
|
|
|
|
|
- tls
|
|
|
|
|
- http
|
|
|
|
|
port-whitelist:
|
|
|
|
|
- \"80\"
|
|
|
|
|
- \"443\"
|
|
|
|
|
tun:
|
|
|
|
|
enable: true
|
|
|
|
|
prefer-h3: true
|
|
|
|
|
listen: 0.0.0.0:53
|
|
|
|
|
stack: gvisor
|
|
|
|
|
dns-hijack:
|
|
|
|
|
- \"any:53\"
|
|
|
|
|
- \"tcp://any:53\"
|
|
|
|
|
auto-redir: true
|
|
|
|
|
auto-route: true
|
|
|
|
|
auto-detect-interface: true
|
|
|
|
|
dns:
|
|
|
|
|
enable: true
|
|
|
|
|
ipv6: true
|
|
|
|
|
default-nameserver:
|
|
|
|
|
- '1.1.1.1'
|
|
|
|
|
- '8.8.8.8'
|
|
|
|
|
enhanced-mode: fake-ip
|
|
|
|
|
fake-ip-range: 198.18.0.1/16
|
|
|
|
|
fake-ip-filter:
|
|
|
|
|
- 'stun.*.*'
|
|
|
|
|
- 'stun.*.*.*'
|
|
|
|
|
- '+.stun.*.*'
|
|
|
|
|
- '+.stun.*.*.*'
|
|
|
|
|
- '+.stun.*.*.*.*'
|
|
|
|
|
- '+.stun.*.*.*.*.*'
|
|
|
|
|
- '*.lan'
|
|
|
|
|
- '+.msftncsi.com'
|
|
|
|
|
- msftconnecttest.com
|
|
|
|
|
- 'time?.*.com'
|
|
|
|
|
- 'time.*.com'
|
|
|
|
|
- 'time.*.gov'
|
|
|
|
|
- 'time.*.apple.com'
|
|
|
|
|
- time-ios.apple.com
|
|
|
|
|
- 'time1.*.com'
|
|
|
|
|
- 'time2.*.com'
|
|
|
|
|
- 'time3.*.com'
|
|
|
|
|
- 'time4.*.com'
|
|
|
|
|
- 'time5.*.com'
|
|
|
|
|
- 'time6.*.com'
|
|
|
|
|
- 'time7.*.com'
|
|
|
|
|
- 'ntp?.*.com'
|
|
|
|
|
- 'ntp.*.com'
|
|
|
|
|
- 'ntp1.*.com'
|
|
|
|
|
- 'ntp2.*.com'
|
|
|
|
|
- 'ntp3.*.com'
|
|
|
|
|
- 'ntp4.*.com'
|
|
|
|
|
- 'ntp5.*.com'
|
|
|
|
|
- 'ntp6.*.com'
|
|
|
|
|
- 'ntp7.*.com'
|
|
|
|
|
- '+.pool.ntp.org'
|
|
|
|
|
- '+.ipv6.microsoft.com'
|
|
|
|
|
- speedtest.cros.wr.pvp.net
|
|
|
|
|
- network-test.debian.org
|
|
|
|
|
- detectportal.firefox.com
|
|
|
|
|
- cable.auth.com
|
|
|
|
|
- miwifi.com
|
|
|
|
|
- routerlogin.com
|
|
|
|
|
- routerlogin.net
|
|
|
|
|
- tendawifi.com
|
|
|
|
|
- tendawifi.net
|
|
|
|
|
- tplinklogin.net
|
|
|
|
|
- tplinkwifi.net
|
|
|
|
|
- '*.xiami.com'
|
|
|
|
|
- tplinkrepeater.net
|
|
|
|
|
- router.asus.com
|
|
|
|
|
- '*.*.*.srv.nintendo.net'
|
|
|
|
|
- '*.*.stun.playstation.net'
|
|
|
|
|
- '*.openwrt.pool.ntp.org'
|
|
|
|
|
- resolver1.opendns.com
|
|
|
|
|
- 'GC._msDCS.*.*'
|
|
|
|
|
- 'DC._msDCS.*.*'
|
|
|
|
|
- 'PDC._msDCS.*.*'
|
|
|
|
|
use-hosts: true
|
|
|
|
|
nameserver:
|
|
|
|
|
- '8.8.4.4'
|
|
|
|
|
- '1.0.0.1'
|
|
|
|
|
- \"https://1.0.0.1/dns-query\"
|
|
|
|
|
- \"https://8.8.4.4/dns-query\"
|
|
|
|
|
"
|
|
|
|
|
|
|
|
|
|
echo -e "\$new_config_start\$subscription_data" > "\$DOWNLOAD_PATH/config.yaml"
|
|
|
|
|
|
|
|
|
|
mv "\$DOWNLOAD_PATH/config.yaml" "\$DEST_PATH"
|
2024-10-26 10:38:22 +08:00
|
|
|
|
|
2024-12-11 10:59:02 +08:00
|
|
|
|
if [ $? -eq 0 ]; then
|
2024-10-26 10:38:22 +08:00
|
|
|
|
echo "配置文件已成功更新并移动到 \$DEST_PATH"
|
|
|
|
|
else
|
2024-12-11 10:59:02 +08:00
|
|
|
|
echo "配置文件移动失败"
|
2024-10-26 10:38:22 +08:00
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
EOD;
|
|
|
|
|
|
|
|
|
|
$success = file_put_contents($sh_script_path, $sh_script_content) !== false;
|
|
|
|
|
logMessage($success ? "Shell 脚本已成功创建并赋予执行权限。" : "无法创建 Shell 脚本文件。");
|
|
|
|
|
if ($success) {
|
|
|
|
|
shell_exec("chmod +x $sh_script_path");
|
|
|
|
|
}
|
|
|
|
|
return $success ? "Shell 脚本已成功创建并赋予执行权限。" : "无法创建 Shell 脚本文件。";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setupCronJob($cron_time) {
|
|
|
|
|
global $sh_script_path;
|
|
|
|
|
$cron_entry = "$cron_time $sh_script_path\n";
|
2024-12-11 14:17:01 +08:00
|
|
|
|
$current_cron = shell_exec('crontab -l 2>/dev/null');
|
|
|
|
|
|
|
|
|
|
if (empty($current_cron)) {
|
|
|
|
|
$updated_cron = $cron_entry;
|
2024-10-26 10:38:22 +08:00
|
|
|
|
} else {
|
2024-12-12 13:24:47 +08:00
|
|
|
|
$updated_cron = preg_replace('/.*' . preg_quote($sh_script_path, '/') . '/m', $cron_entry, $current_cron);
|
2024-12-11 14:17:01 +08:00
|
|
|
|
if ($updated_cron == $current_cron) {
|
|
|
|
|
$updated_cron .= $cron_entry;
|
|
|
|
|
}
|
2024-10-26 10:38:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$success = file_put_contents('/tmp/cron.txt', $updated_cron) !== false;
|
|
|
|
|
if ($success) {
|
|
|
|
|
shell_exec('crontab /tmp/cron.txt');
|
|
|
|
|
logMessage("Cron 作业已成功设置为 $cron_time 运行。");
|
|
|
|
|
return "Cron 作业已成功设置为 $cron_time 运行。";
|
|
|
|
|
} else {
|
|
|
|
|
logMessage("无法写入临时 Cron 文件。");
|
|
|
|
|
return "无法写入临时 Cron 文件。";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$result = '';
|
|
|
|
|
$cron_result = '';
|
|
|
|
|
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
2024-12-12 13:24:47 +08:00
|
|
|
|
$templates = [
|
|
|
|
|
'1' => 'https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online_Full_NoAuto.ini?',
|
|
|
|
|
'2' => 'https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online_MultiCountry.ini?',
|
|
|
|
|
'3' => 'https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online_Full.ini?',
|
|
|
|
|
'4' => 'https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online_Full_Google.ini?',
|
|
|
|
|
'5' => 'https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online_Full_MultiMode.ini?',
|
|
|
|
|
'6' => 'https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online_Full_Netflix.ini?',
|
|
|
|
|
'7' => 'https://gist.githubusercontent.com/tindy2013/1fa08640a9088ac8652dbd40c5d2715b/raw/default_with_clash_adg.yml?',
|
|
|
|
|
'8' => 'https://raw.githubusercontent.com/WC-Dream/ACL4SSR/WD/Clash/config/ACL4SSR_Online_Full_Dream.ini?',
|
|
|
|
|
'9' => 'https://raw.githubusercontent.com/WC-Dream/ACL4SSR/WD/Clash/config/ACL4SSR_Mini_Dream.ini?',
|
|
|
|
|
'10' => 'https://raw.githubusercontent.com/justdoiting/ClashRule/main/GeneralClashRule.ini?',
|
|
|
|
|
'11' => 'https://raw.githubusercontent.com/lhl77/sub-ini/main/tsutsu-full.ini?',
|
|
|
|
|
'12' => 'https://raw.githubusercontent.com/Mazeorz/airports/master/Clash/Examine_Full.ini?',
|
|
|
|
|
'13' => 'https://gist.githubusercontent.com/tindy2013/1fa08640a9088ac8652dbd40c5d2715b/raw/lhie1_dler.ini?',
|
|
|
|
|
'14' => 'https://gist.githubusercontent.com/tindy2013/1fa08640a9088ac8652dbd40c5d2715b/raw/connershua_backtocn.in?',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$filename = isset($_POST['filename']) && $_POST['filename'] !== '' ? $_POST['filename'] : 'config.yaml';
|
|
|
|
|
$subscription_url = isset($_POST['subscription_url']) ? $_POST['subscription_url'] : '';
|
|
|
|
|
$backend_url = $_POST['backend_url'] ?? 'https://url.v1.mk/sub?';
|
|
|
|
|
$template_key = $_POST['template'] ?? '';
|
|
|
|
|
$include = $_POST['include'] ?? '';
|
|
|
|
|
$exclude = $_POST['exclude'] ?? '';
|
|
|
|
|
$template = $templates[$template_key] ?? '';
|
|
|
|
|
$final_url = buildFinalUrl($subscription_url, $template, $include, $exclude, $backend_url);
|
|
|
|
|
|
|
|
|
|
if (saveSubscriptionUrlToFile($final_url, $subscription_file)) {
|
|
|
|
|
$result = saveSubscriptionContentToYaml($final_url, $filename);
|
|
|
|
|
$result .= generateShellScript() . "<br>";
|
|
|
|
|
|
|
|
|
|
if (isset($_POST['cron_time'])) {
|
|
|
|
|
$cron_time = $_POST['cron_time'];
|
|
|
|
|
$cron_result .= setupCronJob($cron_time) . "<br>";
|
2024-10-26 10:38:22 +08:00
|
|
|
|
}
|
2024-12-12 13:24:47 +08:00
|
|
|
|
} else {
|
|
|
|
|
echo "保存订阅链接到文件失败。";
|
2024-10-26 10:38:22 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getSubscriptionUrlFromFile($file) {
|
|
|
|
|
if (file_exists($file)) {
|
|
|
|
|
return file_get_contents($file);
|
|
|
|
|
}
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
?>
|
2024-12-12 13:24:47 +08:00
|
|
|
|
|
2024-10-26 10:38:22 +08:00
|
|
|
|
<!doctype html>
|
|
|
|
|
<html lang="en" data-bs-theme="<?php echo substr($neko_theme, 0, -4) ?>">
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="utf-8">
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
|
|
<title>Personal - Neko</title>
|
|
|
|
|
<link rel="icon" href="./assets/img/nekobox.png">
|
|
|
|
|
<link href="./assets/css/bootstrap.min.css" rel="stylesheet">
|
|
|
|
|
<link href="./assets/css/custom.css" rel="stylesheet">
|
|
|
|
|
<link href="./assets/theme/<?php echo $neko_theme ?>" rel="stylesheet">
|
2024-12-12 13:24:47 +08:00
|
|
|
|
<script type="text/javascript" src="./assets/js/bootstrap.min.js"></script>
|
2024-10-26 10:38:22 +08:00
|
|
|
|
<script type="text/javascript" src="./assets/js/feather.min.js"></script>
|
2024-12-12 13:24:47 +08:00
|
|
|
|
<script type="text/javascript" src="./assets/bootstrap/bootstrap.bundle.min.js"></script>
|
2024-10-26 10:38:22 +08:00
|
|
|
|
<script type="text/javascript" src="./assets/js/jquery-2.1.3.min.js"></script>
|
|
|
|
|
<script type="text/javascript" src="./assets/js/neko.js"></script>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<style>
|
|
|
|
|
@media (max-width: 767px) {
|
|
|
|
|
.row a {
|
|
|
|
|
font-size: 9px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.table-responsive {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<div class="container-sm container-bg callout border border-3 rounded-4 col-11">
|
|
|
|
|
<div class="row">
|
|
|
|
|
<a href="./index.php" class="col btn btn-lg">🏠 首页</a>
|
2024-12-11 10:59:02 +08:00
|
|
|
|
<a href="./mihomo_manager.php" class="col btn btn-lg">📂 文件管理</a>
|
|
|
|
|
<a href="./mihomo.php" class="col btn btn-lg">🗂️ Mihomo</a>
|
|
|
|
|
<a href="./singbox.php" class="col btn btn-lg">💹 Sing-box</a>
|
2024-12-12 13:24:47 +08:00
|
|
|
|
<h1 class="text-center p-2" style="margin-top: 2rem; margin-bottom: 1rem;">Mihomo 订阅转换模板</h1>
|
2024-10-26 10:38:22 +08:00
|
|
|
|
|
|
|
|
|
<div class="col-12">
|
|
|
|
|
<div class="form-section">
|
|
|
|
|
<form method="post">
|
|
|
|
|
<div class="mb-3">
|
|
|
|
|
<label for="subscription_url" class="form-label">输入订阅链接:</label>
|
|
|
|
|
<input type="text" class="form-control" id="subscription_url" name="subscription_url"
|
|
|
|
|
value="<?php echo htmlspecialchars($current_subscription_url); ?>" required>
|
|
|
|
|
</div>
|
2024-12-12 13:24:47 +08:00
|
|
|
|
|
2024-10-26 10:38:22 +08:00
|
|
|
|
<div class="mb-3">
|
2024-12-11 14:17:01 +08:00
|
|
|
|
<label for="filename" class="form-label">自定义文件名 (默认: config.yaml):</label>
|
2024-10-26 10:38:22 +08:00
|
|
|
|
<input type="text" class="form-control" id="filename" name="filename"
|
|
|
|
|
value="<?php echo htmlspecialchars(isset($_POST['filename']) ? $_POST['filename'] : ''); ?>"
|
|
|
|
|
placeholder="config.yaml">
|
|
|
|
|
</div>
|
2024-12-12 13:24:47 +08:00
|
|
|
|
|
|
|
|
|
<div class="mb-3">
|
|
|
|
|
<label for="backend_url" class="form-label">选择后端地址:</label>
|
|
|
|
|
<select class="form-select" id="backend_url" name="backend_url" required>
|
|
|
|
|
<option value="https://url.v1.mk/sub?" <?php echo ($_POST['backend_url'] ?? '') === 'https://url.v1.mk/sub?' ? 'selected' : ''; ?>>
|
|
|
|
|
肥羊增强型后端【vless reality+hy1+hy2】
|
|
|
|
|
</option>
|
|
|
|
|
<option value="https://sub.d1.mk/sub?" <?php echo ($_POST['backend_url'] ?? '') === 'https://sub.d1.mk/sub?' ? 'selected' : ''; ?>>
|
|
|
|
|
肥羊备用后端【vless reality+hy1+hy2】
|
|
|
|
|
</option>
|
|
|
|
|
<option value="https://sub.xeton.dev/sub?" <?php echo ($_POST['backend_url'] ?? '') === 'https://sub.xeton.dev/sub?' ? 'selected' : ''; ?>>
|
|
|
|
|
subconverter作者提供
|
|
|
|
|
</option>
|
|
|
|
|
<option value="https://api.dler.io/sub?" <?php echo ($_POST['backend_url'] ?? '') === 'https://api.dler.io/sub?' ? 'selected' : ''; ?>>
|
|
|
|
|
api.dler.io
|
|
|
|
|
</option>
|
|
|
|
|
<option value="https://v.id9.cc/sub?" <?php echo ($_POST['backend_url'] ?? '') === 'https://v.id9.cc/sub?' ? 'selected' : ''; ?>>
|
|
|
|
|
v.id9.cc(品云提供)
|
|
|
|
|
</option>
|
|
|
|
|
<option value="https://sub.id9.cc/sub?" <?php echo ($_POST['backend_url'] ?? '') === 'https://sub.id9.cc/sub?' ? 'selected' : ''; ?>>
|
|
|
|
|
sub.id9.cc
|
|
|
|
|
</option>
|
|
|
|
|
<option value="https://api.wcc.best/sub?" <?php echo ($_POST['backend_url'] ?? '') === 'https://api.wcc.best/sub?' ? 'selected' : ''; ?>>
|
|
|
|
|
api.wcc.best
|
|
|
|
|
</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="mb-3">
|
|
|
|
|
<label for="template" class="form-label">选择订阅转换模板:</label>
|
|
|
|
|
<select class="form-select" id="template" name="template" required>
|
|
|
|
|
<option value="1" <?php echo ($_POST['template'] ?? '') === '1' ? 'selected' : ''; ?>>默认</option>
|
|
|
|
|
<option value="2" <?php echo ($_POST['template'] ?? '') === '2' ? 'selected' : ''; ?>>ACL_多国家版</option>
|
|
|
|
|
<option value="3" <?php echo ($_POST['template'] ?? '') === '3' ? 'selected' : ''; ?>>ACL_全分组版</option>
|
|
|
|
|
<option value="4" <?php echo ($_POST['template'] ?? '') === '4' ? 'selected' : ''; ?>>ACL_全分组谷歌版</option>
|
|
|
|
|
<option value="5" <?php echo ($_POST['template'] ?? '') === '5' ? 'selected' : ''; ?>>ACL_全分组多模式版</option>
|
|
|
|
|
<option value="6" <?php echo ($_POST['template'] ?? '') === '6' ? 'selected' : ''; ?>>ACL_全分组奈飞版</option>
|
|
|
|
|
<option value="7" <?php echo ($_POST['template'] ?? '') === '7' ? 'selected' : ''; ?>>附带用于 Clash 的 AdGuard DNS</option>
|
|
|
|
|
<option value="8" <?php echo ($_POST['template'] ?? '') === '8' ? 'selected' : ''; ?>>ACL_全分组 Dream修改版</option>
|
|
|
|
|
<option value="9" <?php echo ($_POST['template'] ?? '') === '9' ? 'selected' : ''; ?>>ACL_精简分组 Dream修改版</option>
|
|
|
|
|
<option value="10" <?php echo ($_POST['template'] ?? '') === '10' ? 'selected' : ''; ?>>emby-TikTok-流媒体分组-去广告加强版</option>
|
|
|
|
|
<option value="11" <?php echo ($_POST['template'] ?? '') === '11' ? 'selected' : ''; ?>>lhl77全分组(定期更新)</option>
|
|
|
|
|
<option value="12" <?php echo ($_POST['template'] ?? '') === '12' ? 'selected' : ''; ?>>品云专属配置(全地域分组)</option>
|
|
|
|
|
<option value="13" <?php echo ($_POST['template'] ?? '') === '13' ? 'selected' : ''; ?>>lhie1 洞主规则完整版</option>
|
|
|
|
|
<option value="14" <?php echo ($_POST['template'] ?? '') === '14' ? 'selected' : ''; ?>>神机规则 Inbound 回国专用</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="mb-3">
|
|
|
|
|
<label for="include" class="form-label">包含节点 (可选):</label>
|
|
|
|
|
<input type="text" class="form-control" id="include" name="include"
|
|
|
|
|
value="<?php echo htmlspecialchars($_POST['include'] ?? ''); ?>" placeholder="要保留的节点,支持正则 | 分割">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="mb-3">
|
|
|
|
|
<label for="exclude" class="form-label">排除节点 (可选):</label>
|
|
|
|
|
<input type="text" class="form-control" id="exclude" name="exclude"
|
|
|
|
|
value="<?php echo htmlspecialchars($_POST['exclude'] ?? ''); ?>" placeholder="要排除的节点,支持正则 | 分割">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<button type="submit" class="btn btn-primary" name="action" value="generate_subscription">生成配置文件</button>
|
2024-10-26 10:38:22 +08:00
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="form-section mt-4">
|
|
|
|
|
<form method="post">
|
|
|
|
|
<div class="mb-3">
|
|
|
|
|
<label for="cron_time" class="form-label">设置 Cron 时间 (例如: 0 3 * * *):</label>
|
|
|
|
|
<input type="text" class="form-control" id="cron_time" name="cron_time"
|
|
|
|
|
value="<?php echo htmlspecialchars(isset($_POST['cron_time']) ? $_POST['cron_time'] : '0 3 * * *'); ?>"
|
|
|
|
|
placeholder="0 3 * * *">
|
|
|
|
|
</div>
|
|
|
|
|
<button type="submit" class="btn btn-primary" name="action" value="update_cron">更新 Cron 作业</button>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="help mt-4">
|
|
|
|
|
<h2 class="text-center">帮助说明</h2>
|
|
|
|
|
<p>欢迎使用 Mihomo 订阅程序!请按照以下步骤进行操作:</p>
|
|
|
|
|
<ul class="list-group">
|
|
|
|
|
<li class="list-group-item"><strong>输入订阅链接:</strong> 在文本框中输入您的 Clash 订阅链接。</li>
|
2024-12-11 14:17:01 +08:00
|
|
|
|
<li class="list-group-item"><strong>输入保存文件名:</strong> 指定保存配置文件的文件名,默认为 "config.yaml",无需添加后缀。</li>
|
2024-12-12 13:24:47 +08:00
|
|
|
|
<li class="list-group-item">点击 "生成订阅链接" 按钮,系统将下载订阅内容,并进行转换和保存。</li>
|
|
|
|
|
<li class="list-group-item">推荐使用文件管理的Mihomo订阅</li>
|
2024-10-26 10:38:22 +08:00
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="result mt-4">
|
|
|
|
|
<?php echo nl2br(htmlspecialchars($result)); ?>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="result mt-2">
|
|
|
|
|
<?php echo nl2br(htmlspecialchars($cron_result)); ?>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-12-12 13:24:47 +08:00
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
|
|
|
const formInputs = [
|
|
|
|
|
document.getElementById('subscription_url'),
|
|
|
|
|
document.getElementById('filename'),
|
|
|
|
|
document.getElementById('backend_url'),
|
|
|
|
|
document.getElementById('template'),
|
|
|
|
|
document.getElementById('include'),
|
|
|
|
|
document.getElementById('exclude'),
|
|
|
|
|
document.getElementById('cron_time'),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
formInputs.forEach(input => {
|
|
|
|
|
if (input) {
|
|
|
|
|
input.value = localStorage.getItem(input.id) || input.value;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function saveSelections() {
|
|
|
|
|
formInputs.forEach(input => {
|
|
|
|
|
if (input) {
|
|
|
|
|
localStorage.setItem(input.id, input.value);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.querySelectorAll('form').forEach(form => {
|
|
|
|
|
form.addEventListener('submit', saveSelections);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
formInputs.forEach(input => {
|
|
|
|
|
if (input) {
|
|
|
|
|
input.addEventListener('change', saveSelections);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
</script>
|