From cdcbd79d0e8ede8e2834d5625aca20d359ed8fa5 Mon Sep 17 00:00:00 2001 From: 7782552 <724728726@qq.com> Date: Wed, 4 Feb 2026 21:06:38 +0800 Subject: [PATCH] Update PaperBootstrap.java --- .../java/io/papermc/paper/PaperBootstrap.java | 449 ++---------------- 1 file changed, 52 insertions(+), 397 deletions(-) diff --git a/src/main/java/io/papermc/paper/PaperBootstrap.java b/src/main/java/io/papermc/paper/PaperBootstrap.java index c88e0f11a672..84b44fb782cf 100644 --- a/src/main/java/io/papermc/paper/PaperBootstrap.java +++ b/src/main/java/io/papermc/paper/PaperBootstrap.java @@ -1,410 +1,65 @@ package io.papermc.paper; -import org.yaml.snakeyaml.Yaml; import java.io.*; -import java.net.*; -import java.nio.file.*; -import java.time.*; import java.util.*; -import java.time.format.DateTimeFormatter; -import java.util.concurrent.*; -import java.util.regex.*; public class PaperBootstrap { - - // ========== 全局变量(类级别)========== - private static final Path UUID_FILE = Paths.get("data/uuid.txt"); - private static String uuid; - private static Process singboxProcess; - // ====================================== - public static void main(String[] args) { - try { - System.out.println("config.yml 加载中..."); - Map config = loadConfig(); - - // ---------- UUID 自动生成 & 持久化 ---------- - uuid = generateOrLoadUUID(config.get("uuid")); - System.out.println("当前使用的 UUID: " + uuid); - // -------------------------------------------- - - String tuicPort = trim((String) config.get("tuic_port")); - String hy2Port = trim((String) config.get("hy2_port")); - String realityPort = trim((String) config.get("reality_port")); - String sni = (String) config.getOrDefault("sni", "www.bing.com"); - - boolean deployVLESS = !realityPort.isEmpty(); - boolean deployTUIC = !tuicPort.isEmpty(); - boolean deployHY2 = !hy2Port.isEmpty(); - - if (!deployVLESS && !deployTUIC && !deployHY2) - throw new RuntimeException("❌ 未设置任何协议端口!"); - - Path baseDir = Paths.get("/tmp/.singbox"); - Files.createDirectories(baseDir); - Path configJson = baseDir.resolve("config.json"); - Path cert = baseDir.resolve("cert.pem"); - Path key = baseDir.resolve("private.key"); - Path bin = baseDir.resolve("sing-box"); - Path realityKeyFile = Paths.get("reality.key"); - - System.out.println("✅ config.yml 加载成功"); - - generateSelfSignedCert(cert, key); - String version = fetchLatestSingBoxVersion(); - safeDownloadSingBox(version, bin, baseDir); + String baseDir = "/home/container"; + String nodeBinDir = baseDir + "/node-v22.12.0-linux-x64/bin"; + String n8nBin = baseDir + "/node_modules/.bin/n8n"; - // === 固定 Reality 密钥 === - String privateKey = ""; - String publicKey = ""; - if (deployVLESS) { - if (Files.exists(realityKeyFile)) { - List lines = Files.readAllLines(realityKeyFile); - for (String line : lines) { - if (line.startsWith("PrivateKey:")) privateKey = line.split(":", 2)[1].trim(); - if (line.startsWith("PublicKey:")) publicKey = line.split(":", 2)[1].trim(); - } - System.out.println("🔑 已加载本地 Reality 密钥对(固定公钥)"); - } else { - Map keys = generateRealityKeypair(bin); - privateKey = keys.getOrDefault("private_key", ""); - publicKey = keys.getOrDefault("public_key", ""); - Files.writeString(realityKeyFile, - "PrivateKey: " + privateKey + "\nPublicKey: " + publicKey + "\n"); - System.out.println("✅ Reality 密钥已保存到 reality.key"); - } - } - generateSingBoxConfig(configJson, uuid, deployVLESS, deployTUIC, deployHY2, - tuicPort, hy2Port, realityPort, sni, cert, key, - privateKey, publicKey); - - // 保存 sing-box 进程 + 启动每日 00:03 重启 - singboxProcess = startSingBox(bin, configJson); - scheduleDailyRestart(bin, configJson); - - String host = detectPublicIP(); - printDeployedLinks(uuid, deployVLESS, deployTUIC, deployHY2, - tuicPort, hy2Port, realityPort, sni, host, publicKey); - - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { deleteDirectory(baseDir); } catch (IOException ignored) {} - })); - - } catch (Exception e) { - e.printStackTrace(); - } - } - - private static String generateOrLoadUUID(Object configUuid) { - // 1. 优先使用 config.yml(兼容旧配置) - String cfg = trim((String) configUuid); - if (!cfg.isEmpty()) { - saveUuidToFile(cfg); - return cfg; - } - - // 2. 读取本地持久化文件 try { - if (Files.exists(UUID_FILE)) { - String saved = Files.readString(UUID_FILE).trim(); - if (isValidUUID(saved)) { - System.out.println("已加载持久化 UUID: " + saved); - return saved; - } - } - } catch (Exception e) { - - System.err.println("读取 UUID 文件失败: " + e.getMessage()); - } - - // 3. 首次生成 - String newUuid = UUID.randomUUID().toString(); - saveUuidToFile(newUuid); - System.out.println("首次生成 UUID: " + newUuid); - return newUuid; - } - - private static void saveUuidToFile(String uuid) { - try { - Files.createDirectories(UUID_FILE.getParent()); - Files.writeString(UUID_FILE, uuid); - // 防止被其他用户读取(非 root 环境仍然安全) - UUID_FILE.toFile().setReadable(false, false); - UUID_FILE.toFile().setReadable(true, true); - } catch (Exception e) { - System.err.println("保存 UUID 失败: " + e.getMessage()); - } - } - - private static boolean isValidUUID(String u) { - return u != null && u.matches("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"); - } - - // ===== 工具函数 ===== - private static String trim(String s) { return s == null ? "" : s.trim(); } - - private static Map loadConfig() throws IOException { - Yaml yaml = new Yaml(); - try (InputStream in = Files.newInputStream(Paths.get("config.yml"))) { - Object o = yaml.load(in); - if (o instanceof Map) return (Map) o; - return new HashMap<>(); - } - } - - // ===== 证书生成 ===== - private static void generateSelfSignedCert(Path cert, Path key) throws IOException, InterruptedException { - if (Files.exists(cert) && Files.exists(key)) { - System.out.println("🔑 证书已存在,跳过生成"); - return; - } - System.out.println("🔨 正在生成 EC 自签证书..."); - new ProcessBuilder("bash", "-c", - "openssl ecparam -genkey -name prime256v1 -out " + key + " && " + - "openssl req -new -x509 -days 3650 -key " + key + " -out " + cert + " -subj '/CN=bing.com'") - .inheritIO().start().waitFor(); - System.out.println("✅ 已生成自签证书"); - } - - // ===== Reality 密钥生成 ===== - private static Map generateRealityKeypair(Path bin) throws IOException, InterruptedException { - System.out.println("🔑 正在生成 Reality 密钥对..."); - ProcessBuilder pb = new ProcessBuilder("bash", "-c", bin + " generate reality-keypair"); - pb.redirectErrorStream(true); - Process p = pb.start(); - StringBuilder sb = new StringBuilder(); - try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) { - String line; - while ((line = br.readLine()) != null) sb.append(line).append("\n"); - } - p.waitFor(); - String out = sb.toString(); - Matcher priv = Pattern.compile("PrivateKey[:\\s]*([A-Za-z0-9_\\-+/=]+)").matcher(out); - Matcher pub = Pattern.compile("PublicKey[:\\s]*([A-Za-z0-9_\\-+/=]+)").matcher(out); - if (!priv.find() || !pub.find()) throw new IOException("Reality 密钥生成失败:" + out); - Map map = new HashMap<>(); - map.put("private_key", priv.group(1)); - map.put("public_key", pub.group(1)); - System.out.println("✅ Reality 密钥生成完成"); - return map; - } - // ===== 配置生成 ===== - private static void generateSingBoxConfig(Path configFile, String uuid, boolean vless, boolean tuic, boolean hy2, - String tuicPort, String hy2Port, String realityPort, - String sni, Path cert, Path key, - String privateKey, String publicKey) throws IOException { - - List inbounds = new ArrayList<>(); - - if (tuic) { - inbounds.add(""" - { - "type": "tuic", - "listen": "::", - "listen_port": %s, - "users": [{"uuid": "%s", "password": "eishare2025"}], - "congestion_control": "bbr", - "tls": { - "enabled": true, - "alpn": ["h3"], - "certificate_path": "%s", - "key_path": "%s" - } - } - """.formatted(tuicPort, uuid, cert, key)); - } - - if (hy2) { - inbounds.add(""" - { - "type": "hysteria2", - "listen": "::", - "listen_port": %s, - "users": [{"password": "%s"}], - "masquerade": "https://bing.com", - "ignore_client_bandwidth": true, - "up_mbps": 1000, - "down_mbps": 1000, - "tls": { - "enabled": true, - "alpn": ["h3"], - "insecure": true, - "certificate_path": "%s", - "key_path": "%s" - } - } - """.formatted(hy2Port, uuid, cert, key)); - } - - if (vless) { - inbounds.add(""" - { - "type": "vless", - "listen": "::", - "listen_port": %s, - "users": [{"uuid": "%s", "flow": "xtls-rprx-vision"}], - "tls": { - "enabled": true, - "server_name": "%s", - "reality": { - "enabled": true, - "handshake": {"server": "%s", "server_port": 443}, - "private_key": "%s", - "short_id": [""] - } - } - } - """.formatted(realityPort, uuid, sni, sni, privateKey)); - } - - String json = """ - { - "log": { "level": "info" }, - "inbounds": [%s], - "outbounds": [{"type": "direct"}] - } - """.formatted(String.join(",", inbounds)); - - Files.writeString(configFile, json); - System.out.println("✅ sing-box 配置生成完成"); - } - - // ===== 版本检测 ===== - private static String fetchLatestSingBoxVersion() { - String fallback = "1.12.12"; - try { - URL url = new URL("https://api.github.com/repos/SagerNet/sing-box/releases/latest"); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setConnectTimeout(5000); - conn.setReadTimeout(5000); - conn.setRequestProperty("Accept", "application/vnd.github.v3+json"); - try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { - String json = br.lines().reduce("", (a, b) -> a + b); - int i = json.indexOf("\"tag_name\":\"v"); - if (i != -1) { - String v = json.substring(i + 13, json.indexOf("\"", i + 13)); - System.out.println("🔍 最新版本: " + v); - return v; - } - } - } catch (Exception e) { - System.out.println("⚠️ 获取版本失败,使用回退版本 " + fallback); - } - return fallback; - } - - // ===== 下载 sing-box ===== - private static void safeDownloadSingBox(String version, Path bin, Path dir) throws IOException, InterruptedException { - if (Files.exists(bin)) return; - String arch = detectArch(); - String file = "sing-box-" + version + "-linux-" + arch + ".tar.gz"; - String url = "https://github.com/SagerNet/sing-box/releases/download/v" + version + "/" + file; - - System.out.println("⬇️ 下载 sing-box: " + url); - Path tar = dir.resolve(file); - new ProcessBuilder("bash", "-c", "curl -L -o " + tar + " \"" + url + "\"").inheritIO().start().waitFor(); - new ProcessBuilder("bash", "-c", - "cd " + dir + " && tar -xzf " + file + " 2>/dev/null || true && " + - "(find . -type f -name 'sing-box' -exec mv {} ./sing-box \\; ) && chmod +x sing-box || true") - .inheritIO().start().waitFor(); - - if (!Files.exists(bin)) throw new IOException("未找到 sing-box 可执行文件!"); - System.out.println("✅ 成功解压 sing-box 可执行文件"); - } - - private static String detectArch() { - String a = System.getProperty("os.arch").toLowerCase(); - if (a.contains("aarch") || a.contains("arm")) return "arm64"; - return "amd64"; - } - - // ===== 启动 ===== - private static Process startSingBox(Path bin, Path cfg) throws IOException, InterruptedException { - System.out.println("正在启动 sing-box..."); - ProcessBuilder pb = new ProcessBuilder(bin.toString(), "run", "-c", cfg.toString()); - pb.redirectErrorStream(true); - // 不写日志 → 直接输出到控制台 - pb.redirectOutput(ProcessBuilder.Redirect.DISCARD); - Process p = pb.start(); - Thread.sleep(1500); - System.out.println("sing-box 已启动,PID: " + p.pid()); - return p; - } + System.out.println("⚠️ [Zenix-Emergency] 启动全家桶:n8n(30196) + OpenClaw(18789)..."); + + // 1. 强制清理旧进程,防止端口占用 + System.out.println("🔄 正在清理残留 Node 进程..."); + new ProcessBuilder("pkill", "-9", "node").start().waitFor(); + Thread.sleep(2000); + + // 2. 启动 n8n (自动化中心) + System.out.println("🚀 正在启动 n8n..."); + ProcessBuilder n8nPb = new ProcessBuilder(nodeBinDir + "/node", n8nBin, "start"); + Map nEnv = n8nPb.environment(); + nEnv.put("PATH", nodeBinDir + ":" + System.getenv("PATH")); + nEnv.put("N8N_PORT", "30196"); + nEnv.put("WEBHOOK_URL", "https://8.8855.cc.cd/"); + + n8nPb.directory(new File(baseDir)); + n8nPb.inheritIO(); + n8nPb.start(); + + // 3. 启动 OpenClaw (AI 脑子) + System.out.println("🧠 正在启动 OpenClaw..."); + ProcessBuilder clawPb = new ProcessBuilder( + nodeBinDir + "/node", "dist/index.js", "gateway", + "--port", "18789", + "--token", "mytoken123", + "--force" + ); + clawPb.directory(new File(baseDir + "/openclaw")); + Map cEnv = clawPb.environment(); + cEnv.put("PATH", nodeBinDir + ":" + System.getenv("PATH")); + + // --- 🚨 关键配置补全区 --- + cEnv.put("OPENCLAW_TELEGRAM_BOT_TOKEN", "8538523017:AAEHAyOSnY0n7dFN8YRWePk8pFzU0rQhmlM"); + cEnv.put("OPENCLAW_AI_PROVIDER", "google"); // 必须指定提供商 + cEnv.put("OPENCLAW_AI_API_KEY", "AIzaSyBzv_a-Q9u2TF1FVh58DT0yOJQPEMfJtqQ"); // 👈 爹!这里换成你真正的 Gemini Key! + // ----------------------- + + clawPb.inheritIO(); + clawPb.start(); + + System.out.println("✅ 启动序列完成!"); + System.out.println("1️⃣ n8n 网页: https://8.8855.cc.cd"); + System.out.println("2️⃣ OpenClaw 接口: 127.0.0.1:18789"); + + // 保持主线程不退出 + while(true) { Thread.sleep(60000); } - // ===== 输出节点 ===== - private static String detectPublicIP() { - try (BufferedReader br = new BufferedReader(new InputStreamReader(new URL("https://api.ipify.org").openStream()))) { - return br.readLine(); - } catch (Exception e) { - return "your-server-ip"; + } catch (Exception e) { + System.err.println("❌ 启动失败!错误详情:"); + e.printStackTrace(); } } - - private static void printDeployedLinks(String uuid, boolean vless, boolean tuic, boolean hy2, - String tuicPort, String hy2Port, String realityPort, - String sni, String host, String publicKey) { - System.out.println("\n=== ✅ 已部署节点链接 ==="); - if (vless) - System.out.printf("VLESS Reality:\nvless://%s@%s:%s?encryption=none&flow=xtls-rprx-vision&security=reality&sni=%s&fp=chrome&pbk=%s#Reality\n", - uuid, host, realityPort, sni, publicKey); - if (tuic) - System.out.printf("\nTUIC:\ntuic://%s:eishare2025@%s:%s?sni=%s&alpn=h3&congestion_control=bbr&allowInsecure=1#TUIC\n", - uuid, host, tuicPort, sni); - if (hy2) - System.out.printf("\nHysteria2:\nhysteria2://%s@%s:%s?sni=%s&insecure=1#Hysteria2\n", - uuid, host, hy2Port, sni); - } - - // ===== 每日北京时间 00:03 重启 sing-box(无日志、控制台实时输出)===== - private static void scheduleDailyRestart(Path bin, Path cfg) { - ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); - - Runnable restartTask = () -> { - System.out.println("\n[定时重启Sing-box] 北京时间 00:03,准备重启 sing-box..."); - - // 1. 优雅停止旧进程 - if (singboxProcess != null && singboxProcess.isAlive()) { - System.out.println("正在停止旧进程 (PID: " + singboxProcess.pid() + ")..."); - singboxProcess.destroy(); // 发送 SIGTERM - try { - if (!singboxProcess.waitFor(10, TimeUnit.SECONDS)) { - System.out.println("进程未响应,强制终止..."); - singboxProcess.destroyForcibly(); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - // 2. 启动新进程 - try { - ProcessBuilder pb = new ProcessBuilder(bin.toString(), "run", "-c", cfg.toString()); - pb.redirectErrorStream(true); - pb.redirectOutput(ProcessBuilder.Redirect.DISCARD); - pb.redirectError(ProcessBuilder.Redirect.DISCARD); - singboxProcess = pb.start(); - System.out.println("sing-box 重启成功,新 PID: " + singboxProcess.pid()); - } catch (Exception e) { - System.err.println("重启失败: " + e.getMessage()); - e.printStackTrace(); - } - }; - - ZoneId zone = ZoneId.of("Asia/Shanghai"); - LocalDateTime now = LocalDateTime.now(zone); - LocalDateTime next = now.withHour(0).withMinute(3).withSecond(0).withNano(0); - if (!next.isAfter(now)) next = next.plusDays(1); - - long initialDelay = Duration.between(now, next).getSeconds(); - - scheduler.scheduleAtFixedRate(restartTask, initialDelay, 86_400, TimeUnit.SECONDS); - - System.out.printf("[定时重启Sing-box] 已计划每日 00:03 重启(首次执行:%s)%n", - next.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); - } - - private static void deleteDirectory(Path dir) throws IOException { - if (!Files.exists(dir)) return; - Files.walk(dir).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); - } }