diff --git a/x27cn/README.md b/x27cn/README.md index bdd2c6a..df1ec70 100644 --- a/x27cn/README.md +++ b/x27cn/README.md @@ -167,9 +167,197 @@ X27CN v2 使用以下加密步骤: 3. **位旋转** - 循环左移 5 位 4. **状态混合** - 使用累积状态值混淆 +## 代码压缩混淆(v1.2.0 新增) + +除了加密型混淆,X27CN 还提供专业的代码压缩和标识符混淆功能。 + +### Python API + +```python +import x27cn + +# 压缩 CSS +minified_css = x27cn.minify_css('body { color: red; }') +# 输出: 'body{color:red}' + +# 压缩 JavaScript(带变量名混淆) +minified_js = x27cn.minify_js('function hello() { var name = "world"; return name; }') +# 输出: 'function hello(){var _a="world";return _a}' + +# 压缩 HTML(自动处理内联 CSS/JS) +minified_html = x27cn.minify_html(' ') +# 输出: '' + +# 使用 Node.js 工具(效果更好,需安装 terser/clean-css) +minified = x27cn.minify_js_node(js_code) + +# 压缩文件 +x27cn.minify_file('app.js') # 生成 app.min.js +x27cn.minify_file('style.css', 'dist/style.css') +``` + +### 高级混淆 + +```python +import x27cn + +# 标识符混淆(变量名替换为 _$0, _$1, ...) +obfuscated = x27cn.obfuscate_identifiers(js_code) + +# 添加死代码(增加逆向难度) +obfuscated = x27cn.add_dead_code(js_code, complexity=3) +``` + +### 命令行 + +```bash +# 压缩 JavaScript +x27cn minify app.js + +# 压缩 CSS +x27cn minify style.css dist/style.min.css + +# 不混淆变量名 +x27cn minify app.js --no-mangle + +# 不使用 Node.js 工具(纯 Python) +x27cn minify app.js --no-node + +# 添加死代码 +x27cn minify app.js --dead-code=3 + +# 额外标识符混淆 +x27cn minify app.js --identifiers +``` + +### Node.js 工具支持 + +如果安装了以下 npm 包,X27CN 会自动使用它们以获得更好的压缩效果: + +```bash +npm install -g terser clean-css-cli html-minifier-terser +``` + +如果未安装,会自动降级到纯 Python 实现。 + +### 压缩 vs 加密混淆对比 + +| 特性 | `minify` | `obfuscate` | +|------|----------|-------------| +| 可读性 | 低 | 极低 | +| 代码可执行 | 直接执行 | 需解密 | +| 密钥需求 | 不需要 | 需要 | +| 文件大小 | 显著减小 | 略增大 | +| 安全性 | 中等 | 较高 | +| 性能影响 | 无 | 有解密开销 | + +**推荐使用场景:** +- `minify`: 生产环境部署、减小文件体积、基础代码保护 +- `obfuscate`: 需要更强保护的敏感代码、API密钥保护 + +## 密码安全(v1.3.0 新增) + +X27CN 现在提供安全的密码处理功能,适合用户认证场景。 + +### 密码哈希(存储密码) + +```python +import x27cn + +# 哈希密码(用于存储) +hashed = x27cn.hash_password('mypassword123') +# 输出: '$x27cn$100000$base64salt$base64hash' + +# 验证密码 +if x27cn.verify_password('mypassword123', hashed): + print('登录成功') +else: + print('密码错误') +``` + +### 密码强度检测 + +```python +result = x27cn.check_password_strength('abc123') +print(result['level']) # 'weak' +print(result['score']) # 15 +print(result['suggestions']) # ['添加大写字母', '添加特殊字符'] +``` + +### 生成安全密码 + +```python +# 生成 16 位随机密码 +pwd = x27cn.generate_password(16) + +# 自定义选项 +pwd = x27cn.generate_password( + length=20, + include_upper=True, + include_lower=True, + include_digits=True, + include_special=True, + exclude_ambiguous=True # 排除 0O1lI 等易混淆字符 +) +``` + +### 基于密码的加密 + +```python +# 使用密码加密数据(比 key 更安全) +encrypted = x27cn.encrypt_with_password('敏感数据', 'mypassword') +# 输出: ...... (标准 格式) + +# 解密 +decrypted = x27cn.decrypt_with_password(encrypted, 'mypassword') +``` + +格式说明: +- `` - 魔数标识(表示密码加密) +- 后续 16 个 `` - 随机盐值 +- 剩余部分 - 加密数据 + +### 快速哈希 + +```python +# MD5(不推荐用于密码,仅用于校验) +x27cn.md5('hello') # '5d41402abc4b2a76b9719d911017c592' + +# SHA256 +x27cn.sha256('hello') # '2cf24dba5fb0a30e...' + +# SHA512 +x27cn.sha512('hello') +``` + +### 命令行 + +```bash +# 哈希密码 +x27cn password hash "mypassword123" + +# 验证密码 +x27cn password verify "mypassword123" "$x27cn$100000$..." + +# 生成密码 +x27cn password generate --length=20 --count=5 + +# 检查密码强度 +x27cn password check "abc123" + +# 使用密码加密文件 +x27cn encrypt secret.txt --password="mypassword" + +# 使用密码解密 +x27cn decrypt secret.txt.enc --password="mypassword" +``` + ## 安全说明 -X27CN 设计用于**代码混淆**,不是密码学安全的加密算法。 +X27CN 提供两种安全级别: + +### 1. 代码混淆(encrypt/obfuscate) +设计用于**代码混淆**,不是密码学安全的加密算法。 适用场景: - 前端代码混淆保护 @@ -177,10 +365,39 @@ X27CN 设计用于**代码混淆**,不是密码学安全的加密算法。 - 配置文件保护 - 防止代码被轻易复制 -不适用场景: -- 密码存储(请使用 bcrypt/argon2) -- 敏感数据加密(请使用 AES-256) +### 2. 密码安全(hash_password/encrypt_with_password) +使用行业标准的 **PBKDF2-SHA256** 算法。 + +适用场景: +- 用户密码存储 ✓ +- 敏感数据加密 ✓ +- 配置文件加密 ✓ + +安全特性: +- PBKDF2-SHA256 密钥派生(100000 次迭代) +- 随机盐值防止彩虹表攻击 +- 恒定时间比较防止时序攻击 + +### 不适用场景 - 通信加密(请使用 TLS) +- 金融级加密(请使用 AES-256-GCM) + +## 完整 API 参考 + +| 函数 | 说明 | +|------|------| +| `encrypt(text, key)` | X27CN 加密 | +| `decrypt(text, key)` | X27CN 解密 | +| `obfuscate_file(path)` | 文件混淆加密 | +| `minify(content)` | 代码压缩 | +| `minify_js(js)` | JS 压缩 + 变量混淆 | +| `hash_password(pwd)` | 密码哈希 | +| `verify_password(pwd, hash)` | 验证密码 | +| `generate_password(len)` | 生成随机密码 | +| `check_password_strength(pwd)` | 检测密码强度 | +| `encrypt_with_password(data, pwd)` | 密码加密数据 | +| `decrypt_with_password(data, pwd)` | 密码解密数据 | +| `md5(text)` / `sha256(text)` | 快速哈希 | ## License diff --git a/x27cn/debug_mangle.py b/x27cn/debug_mangle.py new file mode 100644 index 0000000..9c3593c --- /dev/null +++ b/x27cn/debug_mangle.py @@ -0,0 +1,7 @@ +from x27cn.minify import _mangle_variables + +js = 'var name="x";var greeting="y"+name;' +print("输入:", js) +result = _mangle_variables(js) +print("输出:", result) + diff --git a/x27cn/pyproject.toml b/x27cn/pyproject.toml index 2215957..6a6528c 100644 --- a/x27cn/pyproject.toml +++ b/x27cn/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "x27cn" -version = "1.1.0" +version = "1.3.0" description = "X27CN 代码混淆加密库 - Code obfuscation and encryption library" readme = "README.md" license = "MIT" diff --git a/x27cn/x27cn/__init__.py b/x27cn/x27cn/__init__.py index d57e9c4..f168f41 100644 --- a/x27cn/x27cn/__init__.py +++ b/x27cn/x27cn/__init__.py @@ -40,7 +40,35 @@ from .obfuscate import ( obfuscate_inline_css, ) -__version__ = '1.1.0' +from .minify import ( + minify, + minify_file, + minify_css, + minify_js, + minify_html, + minify_css_node, + minify_js_node, + minify_html_node, + obfuscate_identifiers, + add_dead_code, + flatten_control_flow, + flatten_control_flow_safe, +) + +from .password import ( + hash_password, + verify_password, + check_password_strength, + generate_password, + encrypt_with_password, + decrypt_with_password, + quick_hash, + md5, + sha256, + sha512, +) + +__version__ = '1.3.0' __author__ = 'CFspider' __all__ = [ # 核心加密 @@ -52,12 +80,36 @@ __all__ = [ 'decrypt_base64', 'generate_key', 'DEFAULT_KEY', - # 文件混淆 + # 文件混淆(加密型) 'obfuscate_html', 'obfuscate_js', 'obfuscate_css', 'obfuscate_file', 'obfuscate_inline_js', 'obfuscate_inline_css', + # 代码压缩混淆 + 'minify', + 'minify_file', + 'minify_css', + 'minify_js', + 'minify_html', + 'minify_css_node', + 'minify_js_node', + 'minify_html_node', + 'obfuscate_identifiers', + 'add_dead_code', + 'flatten_control_flow', + 'flatten_control_flow_safe', + # 密码安全 + 'hash_password', + 'verify_password', + 'check_password_strength', + 'generate_password', + 'encrypt_with_password', + 'decrypt_with_password', + 'quick_hash', + 'md5', + 'sha256', + 'sha512', ] diff --git a/x27cn/x27cn/cli.py b/x27cn/x27cn/cli.py index 0767262..91c0be3 100644 --- a/x27cn/x27cn/cli.py +++ b/x27cn/x27cn/cli.py @@ -5,12 +5,23 @@ X27CN 命令行工具 x27cn encrypt [output] [--key=密钥] x27cn decrypt [output] [--key=密钥] x27cn obfuscate [output] [--key=密钥] + x27cn minify [output] [--no-mangle] [--no-node] + x27cn flatten [output] [--intensity=2] [--safe] + x27cn password hash + x27cn password verify + x27cn password generate [--length=16] + x27cn password check """ import argparse import sys from .core import encrypt, decrypt, DEFAULT_KEY from .obfuscate import obfuscate_file +from .minify import minify_file, obfuscate_identifiers, add_dead_code, flatten_control_flow, flatten_control_flow_safe +from .password import ( + hash_password, verify_password, generate_password, + check_password_strength, encrypt_with_password, decrypt_with_password +) def main(): @@ -18,7 +29,7 @@ def main(): prog='x27cn', description='X27CN 代码混淆加密工具' ) - parser.add_argument('--version', action='version', version='x27cn 1.0.0') + parser.add_argument('--version', action='version', version='x27cn 1.3.0') subparsers = parser.add_subparsers(dest='command', help='命令') @@ -28,6 +39,7 @@ def main(): enc_parser.add_argument('output', nargs='?', help='输出文件(可选)') enc_parser.add_argument('--key', '-k', default=DEFAULT_KEY, help='加密密钥') enc_parser.add_argument('--text', '-t', action='store_true', help='将 input 作为文本而非文件') + enc_parser.add_argument('--password', '-p', help='使用密码加密(更安全)') # decrypt 命令 dec_parser = subparsers.add_parser('decrypt', help='解密文本或文件') @@ -35,6 +47,7 @@ def main(): dec_parser.add_argument('output', nargs='?', help='输出文件(可选)') dec_parser.add_argument('--key', '-k', default=DEFAULT_KEY, help='解密密钥') dec_parser.add_argument('--text', '-t', action='store_true', help='将 input 作为文本而非文件') + dec_parser.add_argument('--password', '-p', help='使用密码解密') # obfuscate 命令 obf_parser = subparsers.add_parser('obfuscate', help='混淆加密文件(生成自解密代码)') @@ -42,6 +55,47 @@ def main(): obf_parser.add_argument('output', nargs='?', help='输出文件(可选)') obf_parser.add_argument('--key', '-k', default=DEFAULT_KEY, help='加密密钥') + # minify 命令 + min_parser = subparsers.add_parser('minify', help='压缩混淆文件(不加密)') + min_parser.add_argument('input', help='输入文件 (.html/.js/.css)') + min_parser.add_argument('output', nargs='?', help='输出文件(可选)') + min_parser.add_argument('--no-mangle', action='store_true', help='不混淆变量名') + min_parser.add_argument('--no-node', action='store_true', help='不使用 Node.js 工具') + min_parser.add_argument('--dead-code', type=int, default=0, help='添加死代码复杂度 (1-5)') + min_parser.add_argument('--identifiers', action='store_true', help='额外混淆标识符') + + # flatten 命令 + flat_parser = subparsers.add_parser('flatten', help='控制流扁平化混淆(仅JS)') + flat_parser.add_argument('input', help='输入 JavaScript 文件') + flat_parser.add_argument('output', nargs='?', help='输出文件(可选)') + flat_parser.add_argument('--intensity', '-i', type=int, default=2, choices=[1, 2, 3], + help='扁平化强度 (1=轻, 2=中, 3=强)') + flat_parser.add_argument('--safe', '-s', action='store_true', help='使用安全模式(更保守)') + + # password 命令 + pwd_parser = subparsers.add_parser('password', help='密码工具') + pwd_subparsers = pwd_parser.add_subparsers(dest='pwd_command', help='密码子命令') + + # password hash + pwd_hash = pwd_subparsers.add_parser('hash', help='哈希密码') + pwd_hash.add_argument('password', help='要哈希的密码') + pwd_hash.add_argument('--iterations', '-i', type=int, default=100000, help='迭代次数') + + # password verify + pwd_verify = pwd_subparsers.add_parser('verify', help='验证密码') + pwd_verify.add_argument('password', help='明文密码') + pwd_verify.add_argument('hash', help='哈希值') + + # password generate + pwd_gen = pwd_subparsers.add_parser('generate', help='生成随机密码') + pwd_gen.add_argument('--length', '-l', type=int, default=16, help='密码长度') + pwd_gen.add_argument('--no-special', action='store_true', help='不包含特殊字符') + pwd_gen.add_argument('--count', '-c', type=int, default=1, help='生成数量') + + # password check + pwd_check = pwd_subparsers.add_parser('check', help='检查密码强度') + pwd_check.add_argument('password', help='要检查的密码') + args = parser.parse_args() if not args.command: @@ -51,12 +105,18 @@ def main(): try: if args.command == 'encrypt': if args.text: - result = encrypt(args.input, args.key) + if args.password: + result = encrypt_with_password(args.input, args.password) + else: + result = encrypt(args.input, args.key) print(result) else: with open(args.input, 'r', encoding='utf-8') as f: content = f.read() - result = encrypt(content, args.key) + if args.password: + result = encrypt_with_password(content, args.password) + else: + result = encrypt(content, args.key) if args.output: with open(args.output, 'w', encoding='utf-8') as f: f.write(result) @@ -66,12 +126,18 @@ def main(): elif args.command == 'decrypt': if args.text: - result = decrypt(args.input, args.key) + if args.password: + result = decrypt_with_password(args.input, args.password) + else: + result = decrypt(args.input, args.key) print(result) else: with open(args.input, 'r', encoding='utf-8') as f: content = f.read() - result = decrypt(content, args.key) + if args.password: + result = decrypt_with_password(content, args.password) + else: + result = decrypt(content, args.key) if args.output: with open(args.output, 'w', encoding='utf-8') as f: f.write(result) @@ -82,6 +148,87 @@ def main(): elif args.command == 'obfuscate': output = obfuscate_file(args.input, args.output, args.key) print(f'混淆完成: {output}') + + elif args.command == 'minify': + output = minify_file( + args.input, + args.output, + use_node=not args.no_node, + mangle=not args.no_mangle + ) + # 后处理 + if args.dead_code > 0 or args.identifiers: + with open(output, 'r', encoding='utf-8') as f: + content = f.read() + if args.identifiers and output.endswith('.js'): + content = obfuscate_identifiers(content) + if args.dead_code > 0 and output.endswith('.js'): + content = add_dead_code(content, args.dead_code) + with open(output, 'w', encoding='utf-8') as f: + f.write(content) + print(f'压缩完成: {output}') + + elif args.command == 'flatten': + with open(args.input, 'r', encoding='utf-8') as f: + content = f.read() + + if args.safe: + result = flatten_control_flow_safe(content) + else: + result = flatten_control_flow(content, intensity=args.intensity) + + import os + if args.output: + output_path = args.output + else: + base, ext = os.path.splitext(args.input) + output_path = f"{base}.flat{ext}" + + with open(output_path, 'w', encoding='utf-8') as f: + f.write(result) + print(f'控制流扁平化完成: {output_path}') + + elif args.command == 'password': + if args.pwd_command == 'hash': + hashed = hash_password(args.password, iterations=args.iterations) + print(hashed) + + elif args.pwd_command == 'verify': + if verify_password(args.password, args.hash): + print('✓ 密码正确') + else: + print('✗ 密码错误') + sys.exit(1) + + elif args.pwd_command == 'generate': + for _ in range(args.count): + pwd = generate_password( + length=args.length, + include_special=not args.no_special + ) + print(pwd) + + elif args.pwd_command == 'check': + result = check_password_strength(args.password) + level_colors = { + 'weak': '🔴', + 'fair': '🟠', + 'good': '🟡', + 'strong': '🟢', + 'excellent': '💚' + } + print(f"{level_colors.get(result['level'], '')} 强度: {result['level'].upper()} ({result['score']}/100)") + print(f" 长度: {result['length']} 字符") + print(f" 小写: {'✓' if result['has_lower'] else '✗'}") + print(f" 大写: {'✓' if result['has_upper'] else '✗'}") + print(f" 数字: {'✓' if result['has_digit'] else '✗'}") + print(f" 特殊字符: {'✓' if result['has_special'] else '✗'}") + if result['suggestions']: + print("\n建议:") + for s in result['suggestions']: + print(f" - {s}") + else: + pwd_parser.print_help() except FileNotFoundError: print(f'错误: 文件不存在 - {args.input}', file=sys.stderr) diff --git a/x27cn/x27cn/minify.py b/x27cn/x27cn/minify.py new file mode 100644 index 0000000..07983e4 --- /dev/null +++ b/x27cn/x27cn/minify.py @@ -0,0 +1,804 @@ +""" +X27CN 代码压缩混淆模块 + +提供 HTML/CSS/JS 的专业压缩和混淆功能: +1. 纯Python实现的压缩(无需额外依赖) +2. Node.js工具调用(如果可用,效果更好) +""" + +import re +import os +import json +import subprocess +import shutil +from typing import Optional, Dict, Any + +# ============== 纯 Python 压缩实现 ============== + +def minify_css(css: str) -> str: + """ + 压缩 CSS 代码(纯Python实现) + + Args: + css: CSS 源代码 + + Returns: + 压缩后的 CSS + + Example: + >>> minify_css('body { color: red; }') + 'body{color:red}' + """ + # 移除注释 + css = re.sub(r'/\*[\s\S]*?\*/', '', css) + + # 移除多余空白 + css = re.sub(r'\s+', ' ', css) + + # 移除选择器和属性周围的空白 + css = re.sub(r'\s*([{};:,>+~])\s*', r'\1', css) + + # 移除最后的分号 + css = re.sub(r';}', '}', css) + + # 移除0值的单位 + css = re.sub(r'(\s|:)0(px|em|rem|%|pt|cm|mm|in|pc|ex|ch|vw|vh|vmin|vmax)', r'\g<1>0', css) + + # 简化颜色 + css = re.sub(r'#([0-9a-fA-F])\1([0-9a-fA-F])\2([0-9a-fA-F])\3', r'#\1\2\3', css) + + # 移除开头结尾空白 + css = css.strip() + + return css + + +def minify_js(js: str, mangle: bool = True) -> str: + """ + 压缩 JavaScript 代码(纯Python实现) + + Args: + js: JavaScript 源代码 + mangle: 是否混淆变量名 + + Returns: + 压缩后的 JavaScript + + Example: + >>> minify_js('function test() { return 1; }') + 'function test(){return 1}' + """ + # 先混淆变量名(在原始代码上,保持完整语法) + if mangle: + js = _mangle_variables(js) + + # 保护字符串和正则表达式 + strings = [] + def save_string(match): + idx = len(strings) + strings.append(match.group(0)) + return f'__STR_{idx}__' + + # 保护单引号、双引号和反引号字符串 + js = re.sub(r"'(?:[^'\\]|\\.)*'", save_string, js) + js = re.sub(r'"(?:[^"\\]|\\.)*"', save_string, js) + js = re.sub(r'`(?:[^`\\]|\\.)*`', save_string, js) + + # 移除单行注释 + js = re.sub(r'//[^\n]*', '', js) + + # 移除多行注释 + js = re.sub(r'/\*[\s\S]*?\*/', '', js) + + # 压缩空白 + js = re.sub(r'\s+', ' ', js) + + # 移除操作符周围的空白 + js = re.sub(r'\s*([{}\[\]();,<>=+\-*/%&|!?:])\s*', r'\1', js) + + # 关键字后保留空格 + for kw in ['return', 'throw', 'new', 'delete', 'typeof', 'void', 'in', 'instanceof', 'else', 'case', 'var', 'let', 'const', 'function', 'class', 'extends', 'async', 'await', 'yield', 'import', 'export', 'from', 'as', 'of']: + js = re.sub(rf'({kw})([a-zA-Z_$])', rf'\1 \2', js) + + # 恢复字符串 + for i, s in enumerate(strings): + js = js.replace(f'__STR_{i}__', s) + + # 移除开头结尾空白 + js = js.strip() + + return js + + +def _mangle_variables(js: str) -> str: + """混淆局部变量名""" + # 保护字符串 + strings = [] + def save_string(match): + idx = len(strings) + strings.append(match.group(0)) + return f'__STR_{idx}__' + + js = re.sub(r"'(?:[^'\\]|\\.)*'", save_string, js) + js = re.sub(r'"(?:[^"\\]|\\.)*"', save_string, js) + js = re.sub(r'`(?:[^`\\]|\\.)*`', save_string, js) + + # 找到函数作用域内的变量声明 + var_pattern = re.compile(r'\b(var|let|const)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)') + + # 生成短变量名 + def gen_name(index): + chars = 'abcdefghijklmnopqrstuvwxyz' + if index < 26: + return '_' + chars[index] + return '_' + chars[index // 26 - 1] + chars[index % 26] + + # 收集变量 + var_map = {} + index = 0 + for match in var_pattern.finditer(js): + name = match.group(2) + if name not in var_map and name not in ['undefined', 'null', 'true', 'false', 'NaN', 'Infinity']: + var_map[name] = gen_name(index) + index += 1 + + # 替换变量(从长到短排序避免部分替换) + for old_name in sorted(var_map.keys(), key=len, reverse=True): + new_name = var_map[old_name] + # 只替换完整单词 + js = re.sub(rf'\b{re.escape(old_name)}\b', new_name, js) + + # 恢复字符串 + for i, s in enumerate(strings): + js = js.replace(f'__STR_{i}__', s) + + return js + + +def minify_html(html: str, minify_inline: bool = True) -> str: + """ + 压缩 HTML 代码 + + Args: + html: HTML 源代码 + minify_inline: 是否压缩内联的CSS/JS + + Returns: + 压缩后的 HTML + """ + # 保护
'
+        html = re.sub(r']*)>([\s\S]*?)', process_script, html, flags=re.IGNORECASE)
+        
+        # 压缩内联 CSS
+        def process_style(match):
+            attrs = match.group(1)
+            content = match.group(2)
+            minified = minify_css(content)
+            return f'{minified}'
+        html = re.sub(r']*)>([\s\S]*?)', process_style, html, flags=re.IGNORECASE)
+    else:
+        # 保护 script 和 style
+        html = re.sub(r']*>[\s\S]*?', save_block('script'), html, flags=re.IGNORECASE)
+        html = re.sub(r']*>[\s\S]*?', save_block('style'), html, flags=re.IGNORECASE)
+    
+    # 移除 HTML 注释
+    html = re.sub(r'', '', html)
+    
+    # 压缩空白(但保留单个空格)
+    html = re.sub(r'\s+', ' ', html)
+    
+    # 移除标签周围的空白
+    html = re.sub(r'>\s+<', '><', html)
+    
+    # 移除属性值周围的引号(可选值)
+    # html = re.sub(r'=\s*"([^"\s]+)"', r'=\1', html)
+    
+    # 移除冗余属性
+    html = re.sub(r'\s+type\s*=\s*["\']?text/javascript["\']?', '', html, flags=re.IGNORECASE)
+    html = re.sub(r'\s+type\s*=\s*["\']?text/css["\']?', '', html, flags=re.IGNORECASE)
+    
+    # 恢复保护的内容
+    for i, block in enumerate(preserved):
+        html = html.replace(f'__BLOCK_{i}__', block)
+    
+    return html.strip()
+
+
+# ============== Node.js 工具调用 ==============
+
+def _check_node() -> bool:
+    """检查 Node.js 是否可用"""
+    return shutil.which('node') is not None
+
+
+def _check_npm_package(package: str) -> bool:
+    """检查 npm 包是否安装"""
+    try:
+        result = subprocess.run(
+            ['npm', 'list', package],
+            capture_output=True,
+            text=True,
+            timeout=10
+        )
+        return result.returncode == 0
+    except:
+        return False
+
+
+def minify_css_node(css: str) -> str:
+    """
+    使用 clean-css (Node.js) 压缩 CSS
+    
+    如果 Node.js 不可用,自动降级到纯 Python 实现。
+    """
+    if not _check_node():
+        return minify_css(css)
+    
+    try:
+        # 写入临时文件
+        import tempfile
+        with tempfile.NamedTemporaryFile(mode='w', suffix='.css', delete=False, encoding='utf-8') as f:
+            f.write(css)
+            temp_path = f.name
+        
+        # 调用 cleancss
+        result = subprocess.run(
+            ['npx', 'cleancss', temp_path],
+            capture_output=True,
+            text=True,
+            timeout=30
+        )
+        
+        os.unlink(temp_path)
+        
+        if result.returncode == 0 and result.stdout:
+            return result.stdout
+        else:
+            return minify_css(css)
+    except Exception:
+        return minify_css(css)
+
+
+def minify_js_node(js: str, mangle: bool = True) -> str:
+    """
+    使用 Terser (Node.js) 压缩混淆 JavaScript
+    
+    如果 Node.js 不可用,自动降级到纯 Python 实现。
+    """
+    if not _check_node():
+        return minify_js(js, mangle)
+    
+    try:
+        import tempfile
+        with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False, encoding='utf-8') as f:
+            f.write(js)
+            temp_path = f.name
+        
+        cmd = ['npx', 'terser', temp_path, '--compress', '--format', 'comments=false']
+        if mangle:
+            cmd.append('--mangle')
+        
+        result = subprocess.run(
+            cmd,
+            capture_output=True,
+            text=True,
+            timeout=30
+        )
+        
+        os.unlink(temp_path)
+        
+        if result.returncode == 0 and result.stdout:
+            return result.stdout
+        else:
+            return minify_js(js, mangle)
+    except Exception:
+        return minify_js(js, mangle)
+
+
+def minify_html_node(html: str) -> str:
+    """
+    使用 html-minifier-terser (Node.js) 压缩 HTML
+    
+    如果 Node.js 不可用,自动降级到纯 Python 实现。
+    """
+    if not _check_node():
+        return minify_html(html)
+    
+    try:
+        import tempfile
+        with tempfile.NamedTemporaryFile(mode='w', suffix='.html', delete=False, encoding='utf-8') as f:
+            f.write(html)
+            temp_path = f.name
+        
+        result = subprocess.run(
+            ['npx', 'html-minifier-terser',
+             '--collapse-whitespace',
+             '--remove-comments',
+             '--minify-css', 'true',
+             '--minify-js', 'true',
+             temp_path],
+            capture_output=True,
+            text=True,
+            timeout=30
+        )
+        
+        os.unlink(temp_path)
+        
+        if result.returncode == 0 and result.stdout:
+            return result.stdout
+        else:
+            return minify_html(html)
+    except Exception:
+        return minify_html(html)
+
+
+# ============== 高级混淆功能 ==============
+
+def obfuscate_identifiers(js: str) -> str:
+    """
+    混淆 JavaScript 标识符(变量名、函数名)
+    
+    Args:
+        js: JavaScript 源代码
+        
+    Returns:
+        混淆后的代码
+    """
+    # 保护字符串和正则
+    protected = []
+    def save_protected(match):
+        idx = len(protected)
+        protected.append(match.group(0))
+        return f'__PROT_{idx}__'
+    
+    # 保护字符串
+    js = re.sub(r"'(?:[^'\\]|\\.)*'", save_protected, js)
+    js = re.sub(r'"(?:[^"\\]|\\.)*"', save_protected, js)
+    js = re.sub(r'`(?:[^`\\]|\\.)*`', save_protected, js)
+    
+    # 保护正则
+    js = re.sub(r'/(?![/*])(?:[^/\\]|\\.)+/[gimsuy]*', save_protected, js)
+    
+    # 收集声明的变量和函数
+    declarations = set()
+    
+    # var/let/const 声明
+    for match in re.finditer(r'\b(?:var|let|const)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)', js):
+        declarations.add(match.group(1))
+    
+    # function 声明
+    for match in re.finditer(r'\bfunction\s+([a-zA-Z_$][a-zA-Z0-9_$]*)', js):
+        declarations.add(match.group(1))
+    
+    # 生成混淆名
+    def gen_name(index):
+        # 使用 _$0, _$1, ... 格式
+        return f'_${index:x}'
+    
+    # 过滤保留字和内置对象
+    reserved = {
+        'undefined', 'null', 'true', 'false', 'NaN', 'Infinity',
+        'Object', 'Array', 'String', 'Number', 'Boolean', 'Function',
+        'Symbol', 'BigInt', 'Math', 'Date', 'RegExp', 'Error',
+        'JSON', 'console', 'window', 'document', 'navigator',
+        'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
+        'Promise', 'async', 'await', 'class', 'extends', 'super',
+        'this', 'new', 'delete', 'typeof', 'instanceof', 'in',
+        'if', 'else', 'for', 'while', 'do', 'switch', 'case', 'break',
+        'continue', 'return', 'throw', 'try', 'catch', 'finally',
+        'import', 'export', 'default', 'from', 'as', 'of',
+        'arguments', 'eval', 'with', 'debugger',
+        'crypto', 'fetch', 'Response', 'Request', 'Headers', 'URL',
+        'TextEncoder', 'TextDecoder', 'Uint8Array', 'ArrayBuffer',
+        'atob', 'btoa', 'encodeURIComponent', 'decodeURIComponent',
+    }
+    
+    # 创建映射
+    name_map = {}
+    idx = 0
+    for name in sorted(declarations, key=len, reverse=True):
+        if name not in reserved and len(name) > 1:
+            name_map[name] = gen_name(idx)
+            idx += 1
+    
+    # 替换
+    for old_name, new_name in name_map.items():
+        js = re.sub(rf'\b{re.escape(old_name)}\b', new_name, js)
+    
+    # 恢复保护的内容
+    for i, p in enumerate(protected):
+        js = js.replace(f'__PROT_{i}__', p)
+    
+    return js
+
+
+def add_dead_code(js: str, complexity: int = 2) -> str:
+    """
+    添加无效代码增加逆向难度
+    
+    Args:
+        js: JavaScript 源代码
+        complexity: 复杂度 (1-5)
+        
+    Returns:
+        添加死代码后的代码
+    """
+    dead_code_templates = [
+        'var _$d0=function(){return Math.random()>2};',
+        'var _$d1=(function(){var _=[];for(var i=0;i<0;i++)_.push(i);return _})();',
+        'if(typeof _$d2==="undefined")var _$d2=null;',
+        'try{if(false)throw new Error()}catch(_$e){}',
+        'var _$d3=Date.now()%1===2?1:0;',
+    ]
+    
+    import random
+    dead_codes = random.sample(dead_code_templates, min(complexity, len(dead_code_templates)))
+    
+    # 在代码开头添加
+    return ''.join(dead_codes) + js
+
+
+# ============== 统一接口 ==============
+
+def minify(
+    content: str,
+    content_type: str = 'auto',
+    use_node: bool = True,
+    mangle: bool = True
+) -> str:
+    """
+    统一的压缩混淆接口
+    
+    Args:
+        content: 源代码
+        content_type: 类型 ('html', 'css', 'js', 'auto')
+        use_node: 是否尝试使用 Node.js 工具
+        mangle: 是否混淆变量名(仅 JS)
+        
+    Returns:
+        压缩后的代码
+        
+    Example:
+        >>> minify('body { color: red; }', 'css')
+        'body{color:red}'
+        >>> minify('...', 'html')
+        '...'
+    """
+    # 自动检测类型
+    if content_type == 'auto':
+        content_lower = content.strip().lower()
+        if content_lower.startswith(' str:
+    """
+    压缩混淆文件
+    
+    Args:
+        input_path: 输入文件路径
+        output_path: 输出文件路径(默认添加 .min 后缀)
+        use_node: 是否使用 Node.js 工具
+        mangle: 是否混淆变量名
+        
+    Returns:
+        输出文件路径
+        
+    Example:
+        >>> minify_file('app.js')
+        'app.min.js'
+        >>> minify_file('style.css', 'dist/style.css')
+        'dist/style.css'
+    """
+    # 读取文件
+    with open(input_path, 'r', encoding='utf-8') as f:
+        content = f.read()
+    
+    # 检测类型
+    ext = os.path.splitext(input_path)[1].lower()
+    if ext in ['.html', '.htm']:
+        content_type = 'html'
+    elif ext == '.css':
+        content_type = 'css'
+    elif ext == '.js':
+        content_type = 'js'
+    else:
+        content_type = 'auto'
+    
+    # 压缩
+    minified = minify(content, content_type, use_node, mangle)
+    
+    # 输出路径
+    if output_path is None:
+        base, ext = os.path.splitext(input_path)
+        output_path = f"{base}.min{ext}"
+    
+    # 写入
+    with open(output_path, 'w', encoding='utf-8') as f:
+        f.write(minified)
+    
+    return output_path
+
+
+# ============== 控制流扁平化 ==============
+
+def flatten_control_flow(js: str, intensity: int = 2) -> str:
+    """
+    控制流扁平化 - 将顺序执行的代码块打乱为 switch-case 结构
+    
+    Args:
+        js: JavaScript 源代码
+        intensity: 扁平化强度 (1-3)
+            1: 仅扁平化顶层语句
+            2: 扁平化函数体内语句
+            3: 递归扁平化所有代码块
+        
+    Returns:
+        扁平化后的 JavaScript 代码
+        
+    Example:
+        原始代码:
+            step1(); step2(); step3();
+        
+        扁平化后:
+            var _$s=[2,1,3,0],_$i=0;
+            while(_$s[_$i]!==0){
+                switch(_$s[_$i++]){
+                    case 1:step1();break;
+                    case 2:step2();break;
+                    case 3:step3();break;
+                }
+            }
+    """
+    import random
+    
+    # 保护字符串和正则
+    protected = []
+    def save_protected(match):
+        idx = len(protected)
+        protected.append(match.group(0))
+        return f'__PROT_{idx}__'
+    
+    js = re.sub(r"'(?:[^'\\]|\\.)*'", save_protected, js)
+    js = re.sub(r'"(?:[^"\\]|\\.)*"', save_protected, js)
+    js = re.sub(r'`(?:[^`\\]|\\.)*`', save_protected, js)
+    js = re.sub(r'/(?![/*])(?:[^/\\]|\\.)+/[gimsuy]*', save_protected, js)
+    
+    def flatten_block(code: str, depth: int = 0) -> str:
+        """扁平化一个代码块"""
+        if depth >= intensity:
+            return code
+        
+        # 分割语句(简化版本,处理基本情况)
+        statements = _split_statements(code)
+        
+        if len(statements) < 3:
+            return code
+        
+        # 过滤空语句
+        statements = [s.strip() for s in statements if s.strip()]
+        
+        if len(statements) < 3:
+            return code
+        
+        # 生成随机编号
+        indices = list(range(1, len(statements) + 1))
+        random.shuffle(indices)
+        indices.append(0)  # 结束标志
+        
+        # 生成唯一变量名
+        var_s = f'_$s{depth}'
+        var_i = f'_$i{depth}'
+        
+        # 构建 switch 语句
+        cases = []
+        for i, stmt in enumerate(statements):
+            case_num = i + 1
+            # 递归扁平化函数体
+            if intensity >= 2 and 'function' in stmt:
+                stmt = _flatten_function_body(stmt, depth + 1, intensity)
+            cases.append(f'case {case_num}:{stmt};break;')
+        
+        # 组装结果
+        order_array = ','.join(str(i) for i in indices)
+        result = f'var {var_s}=[{order_array}],{var_i}=0;'
+        result += f'while({var_s}[{var_i}]!==0){{'
+        result += f'switch({var_s}[{var_i}++]){{'
+        result += ''.join(cases)
+        result += '}}'
+        
+        return result
+    
+    def _split_statements(code: str) -> list:
+        """分割语句(简化版本)"""
+        statements = []
+        current = ''
+        brace_depth = 0
+        paren_depth = 0
+        bracket_depth = 0
+        in_string = None
+        
+        i = 0
+        while i < len(code):
+            c = code[i]
+            
+            # 检查字符串
+            if c in '"\'`' and (i == 0 or code[i-1] != '\\'):
+                if in_string is None:
+                    in_string = c
+                elif in_string == c:
+                    in_string = None
+                current += c
+                i += 1
+                continue
+            
+            if in_string:
+                current += c
+                i += 1
+                continue
+            
+            # 跟踪括号
+            if c == '{':
+                brace_depth += 1
+            elif c == '}':
+                brace_depth -= 1
+            elif c == '(':
+                paren_depth += 1
+            elif c == ')':
+                paren_depth -= 1
+            elif c == '[':
+                bracket_depth += 1
+            elif c == ']':
+                bracket_depth -= 1
+            
+            # 分割点
+            if c == ';' and brace_depth == 0 and paren_depth == 0 and bracket_depth == 0:
+                if current.strip():
+                    statements.append(current.strip())
+                current = ''
+            elif c == '}' and brace_depth == 0 and paren_depth == 0 and bracket_depth == 0:
+                current += c
+                if current.strip():
+                    statements.append(current.strip())
+                current = ''
+            else:
+                current += c
+            
+            i += 1
+        
+        if current.strip():
+            statements.append(current.strip())
+        
+        return statements
+    
+    def _flatten_function_body(func_code: str, depth: int, intensity: int) -> str:
+        """扁平化函数体内容"""
+        # 找到函数体
+        match = re.search(r'(function[^{]*\{)([\s\S]*)(\})\s*$', func_code)
+        if not match:
+            return func_code
+        
+        prefix = match.group(1)
+        body = match.group(2)
+        suffix = match.group(3)
+        
+        # 扁平化函数体
+        flattened_body = flatten_block(body, depth)
+        
+        return prefix + flattened_body + suffix
+    
+    # 处理整个代码
+    result = flatten_block(js, 0)
+    
+    # 恢复保护的内容
+    for i, p in enumerate(protected):
+        result = result.replace(f'__PROT_{i}__', p)
+    
+    return result
+
+
+def flatten_control_flow_safe(js: str) -> str:
+    """
+    安全的控制流扁平化(仅处理简单函数)
+    
+    此版本更保守,只处理明确可以扁平化的代码块,
+    避免破坏复杂语法结构。
+    
+    Args:
+        js: JavaScript 源代码
+        
+    Returns:
+        扁平化后的代码
+    """
+    import random
+    
+    # 保护字符串、正则、模板字符串
+    protected = []
+    def save_protected(match):
+        idx = len(protected)
+        protected.append(match.group(0))
+        return f'__PROT_{idx}__'
+    
+    js = re.sub(r"'(?:[^'\\]|\\.)*'", save_protected, js)
+    js = re.sub(r'"(?:[^"\\]|\\.)*"', save_protected, js)
+    js = re.sub(r'`(?:[^`\\]|\\.)*`', save_protected, js)
+    
+    # 找到简单的立即执行函数 (IIFE)
+    def process_iife(match):
+        inner = match.group(1)
+        # 分割为简单语句
+        stmts = [s.strip() for s in inner.split(';') if s.strip()]
+        
+        if len(stmts) < 3:
+            return match.group(0)
+        
+        # 过滤包含控制流语句的代码
+        for stmt in stmts:
+            if any(kw in stmt for kw in ['if', 'for', 'while', 'switch', 'try', 'function']):
+                return match.group(0)
+        
+        # 生成扁平化代码
+        indices = list(range(1, len(stmts) + 1))
+        random.shuffle(indices)
+        indices.append(0)
+        
+        order = ','.join(str(i) for i in indices)
+        cases = ''.join('case {}:{};break;'.format(i+1, stmts[i]) for i in range(len(stmts)))
+        
+        return '(function(){{var _$f=[{}],_$g=0;while(_$f[_$g]!==0){{switch(_$f[_$g++]){{{}}}}}}}})()'.format(order, cases)
+    
+    # 处理 IIFE
+    js = re.sub(r'\(function\(\)\{([^{}]*)\}\)\(\)', process_iife, js)
+    
+    # 恢复保护的内容
+    for i, p in enumerate(protected):
+        js = js.replace(f'__PROT_{i}__', p)
+    
+    return js
+
diff --git a/x27cn/x27cn/password.py b/x27cn/x27cn/password.py
new file mode 100644
index 0000000..545c05a
--- /dev/null
+++ b/x27cn/x27cn/password.py
@@ -0,0 +1,411 @@
+"""
+X27CN 密码加密模块
+
+提供安全的密码哈希和验证功能:
+- PBKDF2-SHA256 密码哈希(适合密码存储)
+- 密码强度检测
+- 基于密码的加密/解密
+"""
+
+import os
+import re
+import hashlib
+import hmac
+import base64
+import secrets
+from typing import Tuple, Optional
+
+
+# ============== 密码哈希 ==============
+
+def hash_password(
+    password: str,
+    salt: Optional[bytes] = None,
+    iterations: int = 100000
+) -> str:
+    """
+    使用 PBKDF2-SHA256 哈希密码
+    
+    Args:
+        password: 明文密码
+        salt: 盐值(可选,默认自动生成16字节)
+        iterations: 迭代次数(默认100000,越高越安全但越慢)
+        
+    Returns:
+        格式化的哈希字符串: $x27cn$iterations$salt$hash
+        
+    Example:
+        >>> hashed = hash_password('mypassword123')
+        >>> print(hashed)
+        '$x27cn$100000$abc123...$def456...'
+    """
+    if salt is None:
+        salt = os.urandom(16)
+    
+    # PBKDF2-SHA256 派生
+    dk = hashlib.pbkdf2_hmac(
+        'sha256',
+        password.encode('utf-8'),
+        salt,
+        iterations,
+        dklen=32
+    )
+    
+    # 编码为 base64
+    salt_b64 = base64.b64encode(salt).decode('ascii')
+    hash_b64 = base64.b64encode(dk).decode('ascii')
+    
+    return f'$x27cn${iterations}${salt_b64}${hash_b64}'
+
+
+def verify_password(password: str, hashed: str) -> bool:
+    """
+    验证密码是否匹配哈希
+    
+    Args:
+        password: 待验证的明文密码
+        hashed: hash_password 返回的哈希字符串
+        
+    Returns:
+        密码是否正确
+        
+    Example:
+        >>> hashed = hash_password('mypassword123')
+        >>> verify_password('mypassword123', hashed)
+        True
+        >>> verify_password('wrongpassword', hashed)
+        False
+    """
+    try:
+        parts = hashed.split('$')
+        if len(parts) != 5 or parts[1] != 'x27cn':
+            return False
+        
+        iterations = int(parts[2])
+        salt = base64.b64decode(parts[3])
+        expected_hash = base64.b64decode(parts[4])
+        
+        # 重新计算
+        dk = hashlib.pbkdf2_hmac(
+            'sha256',
+            password.encode('utf-8'),
+            salt,
+            iterations,
+            dklen=32
+        )
+        
+        # 使用恒定时间比较防止时序攻击
+        return hmac.compare_digest(dk, expected_hash)
+    except Exception:
+        return False
+
+
+# ============== 密码强度 ==============
+
+def check_password_strength(password: str) -> dict:
+    """
+    检测密码强度
+    
+    Args:
+        password: 待检测的密码
+        
+    Returns:
+        包含强度信息的字典:
+        - score: 分数 (0-100)
+        - level: 等级 ('weak', 'fair', 'good', 'strong', 'excellent')
+        - suggestions: 改进建议列表
+        
+    Example:
+        >>> result = check_password_strength('abc123')
+        >>> print(result['level'])
+        'weak'
+    """
+    score = 0
+    suggestions = []
+    
+    length = len(password)
+    
+    # 长度评分
+    if length >= 16:
+        score += 30
+    elif length >= 12:
+        score += 25
+    elif length >= 8:
+        score += 15
+    elif length >= 6:
+        score += 10
+    else:
+        suggestions.append('密码长度至少需要8个字符')
+    
+    # 复杂性评分
+    has_lower = bool(re.search(r'[a-z]', password))
+    has_upper = bool(re.search(r'[A-Z]', password))
+    has_digit = bool(re.search(r'[0-9]', password))
+    has_special = bool(re.search(r'[!@#$%^&*(),.?":{}|<>_\-+=\[\]\\\/`~]', password))
+    
+    complexity = sum([has_lower, has_upper, has_digit, has_special])
+    score += complexity * 15
+    
+    if not has_lower:
+        suggestions.append('添加小写字母')
+    if not has_upper:
+        suggestions.append('添加大写字母')
+    if not has_digit:
+        suggestions.append('添加数字')
+    if not has_special:
+        suggestions.append('添加特殊字符 (!@#$%^&* 等)')
+    
+    # 熵评分(字符多样性)
+    unique_chars = len(set(password))
+    if unique_chars >= 10:
+        score += 10
+    elif unique_chars >= 6:
+        score += 5
+    
+    # 常见模式扣分
+    common_patterns = [
+        r'123', r'abc', r'qwerty', r'password', r'admin',
+        r'111', r'000', r'aaa', r'(.)\1{2,}'  # 连续重复字符
+    ]
+    for pattern in common_patterns:
+        if re.search(pattern, password.lower()):
+            score -= 10
+            if pattern == r'(.)\1{2,}':
+                suggestions.append('避免连续重复的字符')
+            else:
+                suggestions.append('避免使用常见模式')
+            break
+    
+    # 限制分数范围
+    score = max(0, min(100, score))
+    
+    # 确定等级
+    if score >= 80:
+        level = 'excellent'
+    elif score >= 60:
+        level = 'strong'
+    elif score >= 40:
+        level = 'good'
+    elif score >= 20:
+        level = 'fair'
+    else:
+        level = 'weak'
+    
+    return {
+        'score': score,
+        'level': level,
+        'suggestions': suggestions,
+        'length': length,
+        'has_lower': has_lower,
+        'has_upper': has_upper,
+        'has_digit': has_digit,
+        'has_special': has_special,
+    }
+
+
+# ============== 密码生成 ==============
+
+def generate_password(
+    length: int = 16,
+    include_upper: bool = True,
+    include_lower: bool = True,
+    include_digits: bool = True,
+    include_special: bool = True,
+    exclude_ambiguous: bool = True
+) -> str:
+    """
+    生成安全的随机密码
+    
+    Args:
+        length: 密码长度(默认16)
+        include_upper: 包含大写字母
+        include_lower: 包含小写字母
+        include_digits: 包含数字
+        include_special: 包含特殊字符
+        exclude_ambiguous: 排除易混淆字符 (0O1lI)
+        
+    Returns:
+        随机密码
+        
+    Example:
+        >>> pwd = generate_password(20)
+        >>> print(len(pwd))
+        20
+    """
+    chars = ''
+    required = []
+    
+    lower_chars = 'abcdefghijkmnopqrstuvwxyz' if exclude_ambiguous else 'abcdefghijklmnopqrstuvwxyz'
+    upper_chars = 'ABCDEFGHJKMNPQRSTUVWXYZ' if exclude_ambiguous else 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+    digit_chars = '23456789' if exclude_ambiguous else '0123456789'
+    special_chars = '!@#$%^&*-_=+?'
+    
+    if include_lower:
+        chars += lower_chars
+        required.append(secrets.choice(lower_chars))
+    if include_upper:
+        chars += upper_chars
+        required.append(secrets.choice(upper_chars))
+    if include_digits:
+        chars += digit_chars
+        required.append(secrets.choice(digit_chars))
+    if include_special:
+        chars += special_chars
+        required.append(secrets.choice(special_chars))
+    
+    if not chars:
+        raise ValueError('至少需要选择一种字符类型')
+    
+    # 生成剩余字符
+    remaining = length - len(required)
+    password_chars = required + [secrets.choice(chars) for _ in range(remaining)]
+    
+    # 打乱顺序
+    secrets.SystemRandom().shuffle(password_chars)
+    
+    return ''.join(password_chars)
+
+
+# ============== 基于密码的加密 ==============
+
+def encrypt_with_password(plaintext: str, password: str) -> str:
+    """
+    使用密码加密数据
+    
+    使用 PBKDF2 派生加密密钥,然后用 X27CN 算法加密。
+    输出格式为  标准格式,盐值编码在开头。
+    
+    Args:
+        plaintext: 明文数据
+        password: 加密密码
+        
+    Returns:
+        加密后的密文( 格式,包含盐值)
+        
+    Example:
+        >>> encrypted = encrypt_with_password('secret data', 'mypassword')
+        >>> # 输出: 
+        >>> decrypted = decrypt_with_password(encrypted, 'mypassword')
+        >>> print(decrypted)
+        'secret data'
+    """
+    from .core import encrypt
+    
+    # 生成随机盐(16字节)
+    salt = os.urandom(16)
+    
+    # 派生密钥
+    key = hashlib.pbkdf2_hmac(
+        'sha256',
+        password.encode('utf-8'),
+        salt,
+        50000,
+        dklen=32
+    )
+    key_str = base64.b64encode(key).decode('ascii')[:32]
+    
+    # 加密数据
+    encrypted = encrypt(plaintext, key_str)
+    
+    # 将盐值编码为  格式
+    salt_hex = ''.join(f'<{b:02x}>' for b in salt)
+    
+    # 添加魔数标识  表示密码加密
+    return f'{salt_hex}{encrypted}'
+
+
+def decrypt_with_password(ciphertext: str, password: str) -> str:
+    """
+    使用密码解密数据
+    
+    Args:
+        ciphertext: encrypt_with_password 返回的密文( 格式)
+        password: 解密密码
+        
+    Returns:
+        解密后的明文
+        
+    Raises:
+        ValueError: 密码错误或数据损坏
+        
+    Example:
+        >>> encrypted = encrypt_with_password('hello', 'pass123')
+        >>> decrypt_with_password(encrypted, 'pass123')
+        'hello'
+    """
+    from .core import decrypt
+    
+    try:
+        # 检查魔数
+        if not ciphertext.startswith(''):
+            raise ValueError('无效的密文格式(缺少密码加密标识)')
+        
+        # 移除魔数
+        data = ciphertext[6:]  # 去掉 
+        
+        # 提取盐值(16字节 = 16个  块 = 64字符)
+        salt_part = data[:64]  # 16 * 4 = 64
+        encrypted_data = data[64:]
+        
+        # 解析盐值
+        salt_bytes = []
+        import re
+        salt_matches = re.findall(r'<([0-9a-fA-F]{2})>', salt_part)
+        if len(salt_matches) != 16:
+            raise ValueError('无效的盐值格式')
+        salt = bytes(int(h, 16) for h in salt_matches)
+        
+        # 派生密钥
+        key = hashlib.pbkdf2_hmac(
+            'sha256',
+            password.encode('utf-8'),
+            salt,
+            50000,
+            dklen=32
+        )
+        key_str = base64.b64encode(key).decode('ascii')[:32]
+        
+        # 解密
+        return decrypt(encrypted_data, key_str)
+    except ValueError:
+        raise
+    except Exception as e:
+        raise ValueError(f'解密失败(密码可能错误): {e}')
+
+
+# ============== 快速哈希 ==============
+
+def quick_hash(data: str, algorithm: str = 'sha256') -> str:
+    """
+    快速计算字符串哈希值
+    
+    Args:
+        data: 要哈希的数据
+        algorithm: 算法 ('md5', 'sha1', 'sha256', 'sha512')
+        
+    Returns:
+        十六进制哈希值
+        
+    Example:
+        >>> quick_hash('hello')
+        '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
+    """
+    h = hashlib.new(algorithm)
+    h.update(data.encode('utf-8'))
+    return h.hexdigest()
+
+
+def md5(data: str) -> str:
+    """计算 MD5 哈希"""
+    return quick_hash(data, 'md5')
+
+
+def sha256(data: str) -> str:
+    """计算 SHA256 哈希"""
+    return quick_hash(data, 'sha256')
+
+
+def sha512(data: str) -> str:
+    """计算 SHA512 哈希"""
+    return quick_hash(data, 'sha512')
+
diff --git a/破皮版workers.js b/破皮版workers.js
index 1eb075a..e276b96 100644
--- a/破皮版workers.js
+++ b/破皮版workers.js
@@ -1 +1 @@
-import{connect as e}from"cloudflare:sockets";var _$d=function(_e,_k){if(!_e)return"";_k=_k||"x27cn2026";var _kb=[];for(var i=0;i<_k.length;i++)_kb.push(_k.charCodeAt(i));var _ek=new Array(256),_sb=new Array(256),_isb=new Array(256);for(var i=0;i<256;i++){_ek[i]=(_kb[i%_kb.length]^((7*i+13)&255))&255;_sb[i]=(167*i+89)&255}for(var i=0;i<256;i++)_isb[_sb[i]]=i;var _h=_e.replace(/<([0-9a-fA-F]{1,4})>/g,"$1");if(_h.length%2!==0)return"";var _eb=[];for(var i=0;i<_h.length;i+=2)_eb.push(parseInt(_h.substr(i,2),16));var _st=0;for(var i=0;i<_kb.length;i++)_st^=_kb[i];var _r=[];for(var i=0;i<_eb.length;i++){var v=_eb[i],_ns=(_st+v+_ek[(i+128)%256])&255;v=(((v-3*i-_st)%256)+256)%256;v=((v>>5)|(v<<3))&255;v=_isb[v];v=v^_ek[i%256];_r.push(v);_st=_ns}try{return decodeURIComponent(_r.map(x=>'%'+x.toString(16).padStart(2,'0')).join(''))}catch(e){return String.fromCharCode(..._r)}};var _$e=function(_t,_k){if(!_t)return"";_k=_k||"x27cn2026";var _kb=(new TextEncoder).encode(_k),_ek=new Uint8Array(256),_sb=new Uint8Array(256);for(var i=0;i<256;i++){_ek[i]=(_kb[i%_kb.length]^((7*i+13)&255))&255;_sb[i]=(167*i+89)&255}var _d=(new TextEncoder).encode(_t),_r=new Uint8Array(_d.length),_st=0;for(var b of _kb)_st^=b;for(var i=0;i<_d.length;i++){var v=_d[i];v=v^_ek[i%256];v=_sb[v];v=((v<<5)|(v>>3))&255;v=(v+3*i+_st)&255;_st=(_st+v+_ek[(i+128)%256])&255;_r[i]=v}var _h=Array.from(_r).map(b=>b.toString(16).padStart(2,"0")).join(""),_o="";for(var i=0;i<_h.length;i+=4)_o+="<"+_h.substr(i,4)+">";return _o};var _$gk=function(_h){var _c="ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789",_k="";for(var i=0;i<8;i++){var _v=parseInt(_h.substr(i*2,2),16);_k+=_c[_v%_c.length]}return _k};var _$n='Welcome to nginx!

Welcome to nginx!

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.

';async function _$m(t){var e=new TextEncoder,n=await crypto.subtle.digest("MD5",e.encode(t)),r=Array.from(new Uint8Array(n)).map(e=>e.toString(16).padStart(2,"0")).join(""),s=await crypto.subtle.digest("MD5",e.encode(r.slice(7,27)));return Array.from(new Uint8Array(s)).map(e=>e.toString(16).padStart(2,"0")).join("").toLowerCase()}async function _$a(t){var e=t.replace(/[\t"'\r\n]+/g,",").replace(/,+/g,",");return","==e.charAt(0)&&(e=e.slice(1)),","==e.charAt(e.length-1)&&(e=e.slice(0,-1)),e.split(",")}let _$c,_$pi="",_$s5=null,_$gp=!1,_$sa="",_$pa={},_$cp,_$ca,_$ci=0,_$ef=!0,_$doh="https://doh.cmliussss.net/CMLiussss",_$wl=["*tapecontent.net","*cloudatacdn.com","*loadshare.org","*cdn-centaurus.com","scholar.google.com"];const _$pg="https://edt-pages.github.io";export default{async fetch(t,n,r){const s=new URL(t.url),o=t.headers.get("User-Agent")||"null",a=t.headers.get("Upgrade"),i=n.ADMIN||n.admin||n.PASSWORD||n.password||n.pswd||n.TOKEN||n.KEY||n.UUID||n.uuid||_$d("<2b63><9361><4453>"),c=n.KEY||_$d("<2b63><93dc><434b><5a78><0ae8><7db7>"),l=await _$m(i+c),u=/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/,d=n.UUID||n.uuid,p=d&&u.test(d)?d.toLowerCase():[l.slice(0,8),l.slice(8,12),"4"+l.slice(13,16),"8"+l.slice(17,20),l.slice(20)].join("-"),f=n.HOST?(await _$a(n.HOST)).map(e=>e.toLowerCase().replace(/^https?:\/\//,"").split("/")[0].split(":")[0]):[s.hostname],h=f[0];if(n.PROXYIP){const e=await _$a(n.PROXYIP);_$pi=e[Math.floor(Math.random()*e.length)],_$ef=!1}else _$pi=(t.cf.colo+_$d("<4ced><4e64><6a91><8212><9f1b><7714>")).toLowerCase();const g=t.headers.get("X-Real-IP")||t.headers.get("CF-Connecting-IP")||t.headers.get("X-Forwarded-For")||"unknown";if(n.GO2SOCKS5&&(_$wl=await _$a(n.GO2SOCKS5)),_$doh=n.ECH_DOH||n.DOH||_$doh,!a||"websocket"!==a){if("http:"===s.protocol)return Response.redirect(s.href.replace("http://"+s.hostname,"https://"+s.hostname),301);const e=s.pathname.slice(1).toLowerCase(),r=n.TWO_PROXY||n.two_proxy||"",_k=n.ACCESSKEY||n.ACCESS_KEY||n.AKEY||_$gk(l);if(""===e||"/"===e)return new Response(_$n,{status:200,headers:{"Content-Type":"text/html","Server":"nginx/1.18.0"}});if(_$d("<10ef><0af9>")===e){const _u="https://"+s.hostname+"/x2727admin/"+_k,_m=_$d("<3d1a><3222><9f43><4965><7d>")+": "+_k+String.fromCharCode(10,10)+_$d("<9ff4><245c><2362><5a91><0a1e><6354><2867>")+":"+String.fromCharCode(10)+_u+String.fromCharCode(10,10)+_$d("<68d9><4757><2e71><4f03><6e1b><64e7><6b2b><9541><05b0><718c>")+":"+String.fromCharCode(10)+"- "+_$d("<1ed8><7b54><2948><9dac><1a1e><5211><97f1><61d8>")+String.fromCharCode(10)+"- "+_$d("<9d9b><5498><77af><634b><10a8><51c7><7148><4b74><68e6><2007><8825>")+String.fromCharCode(10)+"- "+_$d("<3cc2><4547><491f><0512><8e7c><24b3><1e8f>")+String.fromCharCode(10)+"- "+_$d("<05d8><344e><4b22><6443><06de><4c8e>"),t=_$e(_m);return new Response(t,{headers:{"Content-Type":"text/plain","Access-Control-Allow-Origin":"*","X-Enc":"x27cn","X-Hint":"Decrypt with key: x27cn2026"}})}const _op=s.pathname.slice(1);if(e.startsWith(_$d("<10ef><0af9>")+"/")){const o=_op.substring(11);if(o!==_k){const e=_$d("<9fe9><9c87><1631><26e6><8a41><7e41><4b45><6a18><0071><0969><1ce5><8e32><4699><1f68><3ed9><9050><1446><7c32><61af><40d7><88>");return new Response(_$e(e),{status:403,headers:{"Content-Type":"text/plain","Access-Control-Allow-Origin":"*","X-Enc":"x27cn"}})}const a=t.cf?.colo||"UNKNOWN",i="/"+p+(r?"?two_proxy="+encodeURIComponent(r):""),c="vless://"+p+"@"+s.hostname+":443?security=tls&type=ws&host="+s.hostname+"&sni="+s.hostname+"&path="+encodeURIComponent(i)+"&encryption=none#Node-"+a,l={status:"online",version:"1.8.7",colo:a,host:s.hostname,uuid:p,vless:c,two_proxy:r||null,proxyip:_$pi},u=_$e(JSON.stringify(l),o);return new Response(u,{headers:{"Content-Type":"text/plain","Access-Control-Allow-Origin":"*","X-Enc":"x27cn","X-Key":o}})}return new Response(JSON.stringify({error:"Unknown endpoint",available:["/x2727admin"]}),{status:404,headers:{"Content-Type":"application/json"}})}return new Response(null,{status:101})}}; \ No newline at end of file +import{connect}from"cloudflare:sockets";function x27cnEncrypt(e,t="x27cn2026"){if(!e)return"";const n=(new TextEncoder).encode(t),s=new Uint8Array(256),r=new Uint8Array(256);for(let e=0;e<256;e++)s[e]=255&(n[e%n.length]^7*e+13&255),r[e]=167*e+89&255;const o=(new TextEncoder).encode(e),a=new Uint8Array(o.length);let i=0;for(const e of n)i^=e;for(let e=0;e>3),t=t+3*e+i&255,i=i+t+s[(e+128)%256]&255,a[e]=t}const c=Array.from(a).map(e=>e.toString(16).padStart(2,"0")).join("");let l="";for(let e=0;e";return l}function generateAccessKey(e){const t="ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789";let n="";for(let s=0;s<8;s++){n+=t[parseInt(e.substr(2*s,2),16)%55]}return n}const NGINX_PAGE='\n\n\nWelcome to nginx!\n\n\n\n

Welcome to nginx!

\n

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

\n

For online documentation and support please refer to nginx.org.
\nCommercial support is available at nginx.com.

\n

Thank you for using nginx.

\n\n';let config_JSON,cacheproxyIP,cachedProxyArray,proxyIP="",enableSocks5Proxy=null,enableGlobalSocks5=!1,mySocks5Account="",parsedSocks5Address={},cachedProxyIndex=0,enableProxyFallback=!0,ECH_DOH="https://doh.cmliussss.net/CMLiussss",socks5Whitelist=["*tapecontent.net","*cloudatacdn.com","*loadshare.org","*cdn-centaurus.com","scholar.google.com"];const pagesStaticUrl="https://edt-pages.github.io";export default{async fetch(e,t,n){const s=new URL(e.url),r=e.headers.get("User-Agent")||"null",o=e.headers.get("Upgrade"),a=t.ADMIN||t.admin||t.PASSWORD||t.password||t.pswd||t.TOKEN||t.KEY||t.UUID||t.uuid||"cfspider-public",i=t.KEY||"cfspider-default-key",c=await MD5MD5(a+i),l=/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/,d=t.UUID||t.uuid,u=d&&l.test(d)?d.toLowerCase():[c.slice(0,8),c.slice(8,12),"4"+c.slice(13,16),"8"+c.slice(17,20),c.slice(20)].join("-"),p=t.CUSTOM_HOST||t.HOST||e.headers.get("X-Forwarded-Host")||e.headers.get("Host")||s.hostname,f=(t.HOST?(await parseToArray(t.HOST)).map(e=>e.toLowerCase().replace(/^https?:\/\//,"").split("/")[0].split(":")[0]):[s.hostname])[0];if(t.PROXYIP){const e=await parseToArray(t.PROXYIP);proxyIP=e[Math.floor(Math.random()*e.length)],enableProxyFallback=!1}else proxyIP=(e.cf.colo+".PrOxYIp.CmLiUsSsS.nEt").toLowerCase();const h=e.headers.get("X-Real-IP")||e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")||e.headers.get("True-Client-IP")||e.headers.get("Fly-Client-IP")||e.headers.get("X-Appengine-Remote-Addr")||e.headers.get("X-Forwarded-For")||e.headers.get("X-Real-IP")||e.headers.get("X-Cluster-Client-IP")||e.cf?.clientTcpRtt||"unknownIP";if(t.GO2SOCKS5&&(socks5Whitelist=await parseToArray(t.GO2SOCKS5)),ECH_DOH=t.ECH_DOH||t.DOH||ECH_DOH,o&&"websocket"===o){if(a){await reversedelegateparamsget(e);const n=new URL(e.url).pathname;let s="";if(n.includes("two_proxy=")){const e=n.match(/two_proxy=([^&]+)/);e&&(s=decodeURIComponent(decodeURIComponent(e[1])))}const r=s||t.TWO_PROXY||t.two_proxy||"";return await handleWebSocket(e,u,r)}}else{if("http:"===s.protocol)return Response.redirect(s.href.replace(`http://${s.hostname}`,`https://${s.hostname}`),301);const o=s.pathname.slice(1).toLowerCase(),g=("false"!==t.NEW_IP&&t.NEW_IP,!d||!l.test(d)),m=t.TWO_PROXY||t.two_proxy||"",y=t.ACCESSKEY||t.ACCESS_KEY||t.AKEY||generateAccessKey(c);if(""===o||"/"===o)return new Response(NGINX_PAGE,{status:200,headers:{"Content-Type":"text/html",Server:"nginx/1.18.0"}});if("x2727admin"===o){const e=x27cnEncrypt(`Your access key: ${y}\n\nFull URL:\n${`https://${p}/x2727admin/${y}`}\n\nEnvironment variables (optional):\n- ACCESSKEY: Custom access key\n- UUID: UUID parameter for cfspider\n- KEY: Custom encryption key\n- PROXYIP: Custom proxy IP`);return new Response(e,{headers:{"Content-Type":"text/plain","Access-Control-Allow-Origin":"*","X-Enc":"x27cn","X-Hint":"Decrypt with key: x27cn2026"}})}if(o.startsWith("x2727admin/")){const t=s.pathname.slice(1).substring(11);if(t!==y)return new Response(x27cnEncrypt("Invalid key. Please visit /x2727admin to get the correct key."),{status:403,headers:{"Content-Type":"text/plain","Access-Control-Allow-Origin":"*","X-Enc":"x27cn"}});const n=e.cf?.colo||"UNKNOWN",r="/"+u+(m?"?two_proxy="+encodeURIComponent(m):""),o={status:"online",version:"1.8.7",colo:n,host:p,uuid:u,vless:`vless://${u}@${p}:443?security=tls&type=ws&host=${p}&sni=${p}&path=${encodeURIComponent(r)}&encryption=none#Node-${n}`,two_proxy:m||null,proxyip:proxyIP},a=x27cnEncrypt(JSON.stringify(o),t);return new Response(a,{headers:{"Content-Type":"text/plain","Access-Control-Allow-Origin":"*","X-Enc":"x27cn","X-Key":t}})}if("status"===o){const t=e.cf?.colo||"UNKNOWN",n="/"+u+(m?"?two_proxy="+encodeURIComponent(m):""),r="vless://"+u+"@"+s.hostname+":443?security=tls&type=ws&host="+s.hostname+"&sni="+s.hostname+"&path="+encodeURIComponent(n)+"&encryption=none#CFspider-"+t;return new Response(JSON.stringify({status:"online",version:"1.8.7",colo:t,host:s.hostname,uuid:u,vless:r,two_proxy:m||null},null,2),{headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}if("api/proxyip"===o){const n=e.cf?.colo||"UNKNOWN",s=(n+".proxyip.cmliussss.net").toLowerCase(),r=t.PROXYIP||"";return new Response(JSON.stringify({colo:n,default:s,hk:"proxyip.cfspider.com",env:r||null,current:r||s,options:{default:{name:"Netherlands",address:s},hk:{name:"Hong Kong",address:"proxyip.cfspider.com"}}}),{headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}if("api/uuid"===o||"api/config"===o){const e="false"!==t.NEW_IP&&"0"!==t.NEW_IP,n=t.TWO_PROXY||t.two_proxy||"",r={host:s.hostname,new_ip:e,version:"1.8.7",is_default_uuid:g,two_proxy_enabled:!!n};if(g?(r.uuid=u,r.vless_path=n?"/"+u+"?two_proxy="+encodeURIComponent(n):"/"+u):n&&(r.two_proxy=n),n){const e=n.split(":");r.two_proxy_host=e[0]||"",r.two_proxy_port=e[1]||""}return new Response(JSON.stringify(r),{headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}if("proxy"===o||o.startsWith("proxy?")){const n=s.searchParams.get("url"),r=s.searchParams.get("method")||"GET",o=s.searchParams.get("two_proxy");if(!n)return new Response(JSON.stringify({error:"Missing url parameter"}),{status:400,headers:{"Content-Type":"application/json"}});try{const s={};for(const[t,n]of e.headers)if(t.toLowerCase().startsWith("x-cfspider-header-")){s[t.substring(18)]=n}let a;const i=o||t.TWO_PROXY||t.two_proxy||"";if(i){const t=i.split(":"),o=t[0],a=parseInt(t[1])||3128,c=t[2]||"",l=t[3]||"",d=new URL(n),u=d.hostname,p=(d.port||d.protocol,"https:"===d.protocol),{connect:f}=await import("cloudflare:sockets");if(p)return new Response(JSON.stringify({error:"HTTPS + two_proxy notSupportedvia /proxy API。pleaseUse Python cfspider.get() with two_proxy params。",hint:"cfspider.get(url, cf_proxies=..., uuid=..., two_proxy=...)",reason:"Workers /proxy API onlysupport HTTP twoProxy"}),{status:501,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}});{const t=f({hostname:o,port:a}),i=t.writable.getWriter(),d=t.readable.getReader();let p=`${r} ${n} HTTP/1.1\r\nHost: ${u}\r\n`;if(c&&l){p+=`Proxy-Authorization: Basic ${btoa(`${c}:${l}`)}\r\n`}for(const[e,t]of Object.entries(s))p+=`${e}: ${t}\r\n`;p+="Connection: close\r\n\r\n",await i.write((new TextEncoder).encode(p));let h=new Uint8Array(0);for(;;){const{value:e,done:t}=await d.read();if(t)break;const n=new Uint8Array(h.length+e.length);n.set(h),n.set(e,h.length),h=n}const g=(new TextDecoder).decode(h),m=g.indexOf("\r\n\r\n"),y=g.substring(0,m),w=h.slice((new TextEncoder).encode(g.substring(0,m+4)).length),b=y.split("\r\n")[0],S=parseInt(b.split(" ")[1])||200,x=new Headers;return y.split("\r\n").slice(1).forEach(e=>{const[t,...n]=e.split(":");t&&n.length&&x.set(t.trim(),n.join(":").trim())}),x.set("Access-Control-Allow-Origin","*"),x.set("X-CF-Colo",e.cf?.colo||"unknown"),x.set("X-CFspider-Version","1.8.6"),x.set("X-CFspider-TwoProxy","enabled"),new Response(w,{status:S,headers:x})}}{const t=new Request(n,{method:r,headers:s,body:"GET"!==r&&"HEAD"!==r?e.body:null});a=await fetch(t);const o=new Headers(a.headers);return o.set("Access-Control-Allow-Origin","*"),o.set("X-CF-Colo",e.cf?.colo||"unknown"),o.set("X-CFspider-Version","1.8.6"),new Response(a.body,{status:a.status,headers:o})}}catch(e){return new Response(JSON.stringify({error:e.message}),{status:500,headers:{"Content-Type":"application/json"}})}}if("api/config/new_ip"===o&&"POST"===e.method){const e="false"!==t.NEW_IP&&"0"!==t.NEW_IP;return new Response(JSON.stringify({new_ip:e,message:"pleasevia Cloudflare Dashboard or wrangler.toml set NEW_IP envVar"}),{headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}if(!a)return fetch(pagesStaticUrl+"/noADMIN").then(e=>{const t=new Headers(e.headers);return t.set("Cache-Control","no-store, no-cache, must-revalidate, proxy-revalidate"),t.set("Pragma","no-cache"),t.set("Expires","0"),new Response(e.body,{status:404,statusText:e.statusText,headers:t})});if(t.KV&&"function"==typeof t.KV.get){const o=s.pathname.slice(1).toLowerCase(),c=s.pathname.slice(1);if(c===i&&"dontmovethisdefaultsecretkey,ifNeededpleasemanuallyviaaddvariableKEYtolinemodify"!==i){const e=new URLSearchParams(s.search);return e.set("token",await MD5MD5(f+u)),new Response("redirectin...",{status:302,headers:{Location:`/sub?${e.toString()}`}})}if("login"===o){const t=e.headers.get("Cookie")||"",n=t.split(";").find(e=>e.trim().startsWith("auth="))?.split("=")[1];if(n==await MD5MD5(r+i+a))return new Response("redirectin...",{status:302,headers:{Location:"/admin"}});if("POST"===e.method){const t=await e.text();if(new URLSearchParams(t).get("password")===a){const e=new Response(JSON.stringify({success:!0}),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}});return e.headers.set("Set-Cookie",`auth=${await MD5MD5(r+i+a)}; Path=/; Max-Age=86400; HttpOnly`),e}}return fetch(pagesStaticUrl+"/login")}if("admin"===o||o.startsWith("admin/")){const l=e.headers.get("Cookie")||"",d=l.split(";").find(e=>e.trim().startsWith("auth="))?.split("=")[1];if(!d||d!==await MD5MD5(r+i+a))return new Response("redirectin...",{status:302,headers:{Location:"/login"}});if("admin/log.json"===o){const e=await t.KV.get("log.json")||"[]";return new Response(e,{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}if("admin/getCloudflareUsage"===c)try{const e=await getCloudflareUsage(s.searchParams.get("Email"),s.searchParams.get("GlobalAPIKey"),s.searchParams.get("AccountID"),s.searchParams.get("APIToken"));return new Response(JSON.stringify(e,null,2),{status:200,headers:{"Content-Type":"application/json"}})}catch(e){const t={msg:"querypleaserequestamountfailed,failedreason:"+e.message,error:e.message};return new Response(JSON.stringify(t,null,2),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}else{if("admin/getADDAPI"===c){if(s.searchParams.get("url")){const e=s.searchParams.get("url");try{new URL(e);const t=await pleaserequestpreferredAPI([e],s.searchParams.get("port")||"443"),n=t[0].length>0?t[0]:t[1];return new Response(JSON.stringify({success:!0,data:n},null,2),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}catch(e){const t={msg:"verifypreferredAPIfailed,failedreason:"+e.message,error:e.message};return new Response(JSON.stringify(t,null,2),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}}return new Response(JSON.stringify({success:!1,data:[]},null,2),{status:403,headers:{"Content-Type":"application/json;charset=utf-8"}})}if("admin/check"===o){let e;if(s.searchParams.has("socks5"))e=await SOCKS5canuseabilityverify("socks5",s.searchParams.get("socks5"));else{if(!s.searchParams.has("http"))return new Response(JSON.stringify({error:"missingproxyparams"}),{status:400,headers:{"Content-Type":"application/json;charset=utf-8"}});e=await SOCKS5canuseabilityverify("http",s.searchParams.get("http"))}return new Response(JSON.stringify(e,null,2),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}}if(config_JSON=await readconfig_JSON(t,f,u,t.PATH),"admin/init"===o)try{return config_JSON=await readconfig_JSON(t,f,u,t.PATH,!0),n.waitUntil(pleaserequestlogrecord(t,e,h,"Init_Config",config_JSON)),config_JSON.init="configalreadyheavysetTodefaultvalue",new Response(JSON.stringify(config_JSON,null,2),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}catch(e){const t={msg:"configresetfailed,failedreason:"+e.message,error:e.message};return new Response(JSON.stringify(t,null,2),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}else if("POST"===e.method)if("admin/config.json"===o)try{const s=await e.json();return s.UUID&&s.HOST?(await t.KV.put("config.json",JSON.stringify(s,null,2)),n.waitUntil(pleaserequestlogrecord(t,e,h,"Save_Config",config_JSON)),new Response(JSON.stringify({success:!0,message:"configalreadykeepstore"}),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})):new Response(JSON.stringify({error:"confignotcomplete"}),{status:400,headers:{"Content-Type":"application/json;charset=utf-8"}})}catch(e){return console.error("keepstoreconfigfailed:",e),new Response(JSON.stringify({error:"keepstoreconfigfailed: "+e.message}),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}else if("admin/cf.json"===o)try{const s=await e.json(),r={Email:null,GlobalAPIKey:null,AccountID:null,APIToken:null,UsageAPI:null};if(!s.init||!0!==s.init)if(s.Email&&s.GlobalAPIKey)r.Email=s.Email,r.GlobalAPIKey=s.GlobalAPIKey;else if(s.AccountID&&s.APIToken)r.AccountID=s.AccountID,r.APIToken=s.APIToken;else{if(!s.UsageAPI)return new Response(JSON.stringify({error:"confignotcomplete"}),{status:400,headers:{"Content-Type":"application/json;charset=utf-8"}});r.UsageAPI=s.UsageAPI}return await t.KV.put("cf.json",JSON.stringify(r,null,2)),n.waitUntil(pleaserequestlogrecord(t,e,h,"Save_Config",config_JSON)),new Response(JSON.stringify({success:!0,message:"configalreadykeepstore"}),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}catch(e){return console.error("keepstoreconfigfailed:",e),new Response(JSON.stringify({error:"keepstoreconfigfailed: "+e.message}),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}else if("admin/tg.json"===o)try{const s=await e.json();if(s.init&&!0===s.init){const e={BotToken:null,ChatID:null};await t.KV.put("tg.json",JSON.stringify(e,null,2))}else{if(!s.BotToken||!s.ChatID)return new Response(JSON.stringify({error:"confignotcomplete"}),{status:400,headers:{"Content-Type":"application/json;charset=utf-8"}});await t.KV.put("tg.json",JSON.stringify(s,null,2))}return n.waitUntil(pleaserequestlogrecord(t,e,h,"Save_Config",config_JSON)),new Response(JSON.stringify({success:!0,message:"configalreadykeepstore"}),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}catch(e){return console.error("keepstoreconfigfailed:",e),new Response(JSON.stringify({error:"keepstoreconfigfailed: "+e.message}),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}else{if("admin/ADD.txt"!==c)return new Response(JSON.stringify({error:"notSupportedPOSTpleaserequestpath"}),{status:404,headers:{"Content-Type":"application/json;charset=utf-8"}});try{const s=await e.text();return await t.KV.put("ADD.txt",s),n.waitUntil(pleaserequestlogrecord(t,e,h,"Save_Custom_IPs",config_JSON)),new Response(JSON.stringify({success:!0,message:"selffixedmeaningIPalreadykeepstore"}),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}catch(e){return console.error("keepstoreselffixedmeaningIPfailed:",e),new Response(JSON.stringify({error:"keepstoreselffixedmeaningIPfailed: "+e.message}),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}}else{if("admin/config.json"===o)return new Response(JSON.stringify(config_JSON,null,2),{status:200,headers:{"Content-Type":"application/json"}});if("admin/ADD.txt"===c){let n=await t.KV.get("ADD.txt")||"null";return"null"==n&&(n=(await generaterandomIP(e,config_JSON.preferredsubscribegenerate.localIPlib.randomnumberamount,config_JSON.preferredsubscribegenerate.localIPlib.pointfixedport))[1]),new Response(n,{status:200,headers:{"Content-Type":"text/plain;charset=utf-8",asn:e.cf.asn}})}if("admin/cf.json"===o)return new Response(JSON.stringify(e.cf,null,2),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}return n.waitUntil(pleaserequestlogrecord(t,e,h,"Admin_Login",config_JSON)),fetch(pagesStaticUrl+"/admin")}if("logout"===o||l.test(o)){const e=new Response("redirectin...",{status:302,headers:{Location:"/login"}});return e.headers.set("Set-Cookie","auth=; Path=/; Max-Age=0; HttpOnly"),e}if("sub"===o){const o=await MD5MD5(f+u);if(s.searchParams.get("token")===o){config_JSON=await readconfig_JSON(t,f,u,t.PATH),n.waitUntil(pleaserequestlogrecord(t,e,h,"Get_SUB",config_JSON));const a=r.toLowerCase(),i=4102329600,c=Date.now(),l=new Date(c);l.setHours(0,0,0,0);const d=Math.floor((c-l.getTime())/864e5*24*1099511627776/2);let p=d,g=d,m=26388279066624;config_JSON.CF.Usage.success&&(p=config_JSON.CF.Usage.pages,g=config_JSON.CF.Usage.workers,m=Number.isFinite(config_JSON.CF.Usage.max)?config_JSON.CF.Usage.max/1e3*1024:102400);const y={"content-type":"text/plain; charset=utf-8","Profile-Update-Interval":config_JSON.preferredsubscribegenerate.SUBUpdateTime,"Profile-web-page-url":s.protocol+"//"+s.host+"/admin","Subscription-Userinfo":`upload=${p}; download=${g}; total=${m}; expire=${i}`,"Cache-Control":"no-store"},w=s.searchParams.has("b64")||s.searchParams.has("base64")||e.headers.get("subconverter-request")||e.headers.get("subconverter-version")||a.includes("subconverter")||a.includes("CF-Workers-SUB".toLowerCase())?"mixed":s.searchParams.has("target")?s.searchParams.get("target"):s.searchParams.has("clash")||a.includes("clash")||a.includes("meta")||a.includes("mihomo")?"clash":s.searchParams.has("sb")||s.searchParams.has("singbox")||a.includes("singbox")||a.includes("sing-box")?"singbox":s.searchParams.has("surge")||a.includes("surge")?"surge&ver=4":s.searchParams.has("quanx")||a.includes("quantumult")?"quanx":s.searchParams.has("loon")||a.includes("loon")?"loon":"mixed";a.includes("mozilla")||(y["Content-Disposition"]=`attachment; filename*=utf-8''${encodeURIComponent(config_JSON.preferredsubscribegenerate.SUBNAME)}`);const b=s.searchParams.has("surge")||a.includes("surge")?"trojan":config_JSON.protocoltype;let S="";if("mixed"===w){const n=config_JSON.startuse0RTT?config_JSON.PATH+"?ed=2560":config_JSON.PATH,r="Shadowrocket"==config_JSON.TLSsplitslice?`&fragment=${encodeURIComponent("1,40-60,30-50,tlshello")}`:"Happ"==config_JSON.TLSsplitslice?`&fragment=${encodeURIComponent("3,1,tlshello")}`:"";let o=[],a="";if(!s.searchParams.has("sub")&&config_JSON.preferredsubscribegenerate.local){const n=config_JSON.preferredsubscribegenerate.localIPlib.randomIP?(await generaterandomIP(e,config_JSON.preferredsubscribegenerate.localIPlib.randomnumberamount,config_JSON.preferredsubscribegenerate.localIPlib.pointfixedport))[0]:await t.KV.get("ADD.txt")?await parseToArray(await t.KV.get("ADD.txt")):(await generaterandomIP(e,config_JSON.preferredsubscribegenerate.localIPlib.randomnumberamount,config_JSON.preferredsubscribegenerate.localIPlib.pointfixedport))[0],s=[],r=[],i=[];for(const e of n)if(e.toLowerCase().startsWith("https://"))s.push(e);else if(e.toLowerCase().includes("://"))if(e.includes("#")){const t=e.split("#");i.push(t[0]+"#"+encodeURIComponent(decodeURIComponent(t[1])))}else i.push(e);else r.push(e);const c=await pleaserequestpreferredAPI(s),l=[...new Set(i.concat(c[1]))];a=l.length>0?l.join("\n")+"\n":"";const d=c[0];o=[...new Set(r.concat(d))]}else{let e=s.searchParams.get("sub")||config_JSON.preferredsubscribegenerate.SUB;e=e&&!/^https?:\/\//i.test(e)?`https://${e}`:e;const t=`${e}/sub?host=example.com&uuid=00000000-0000-4000-8000-000000000000`;try{const e=await fetch(t,{headers:{"User-Agent":"v2rayN/edgetunnel (https://github.com/cmliu/edgetunnel)"}});if(!e.ok)return new Response("preferredsubscribegeneratehandlerexception:"+e.statusText,{status:e.status});const n=atob(await e.text()),s=n.includes("\r\n")?n.split("\r\n"):n.split("\n");for(const e of s)if(e.trim())if(e.includes("00000000-0000-4000-8000-000000000000")&&e.includes("example.com")){const t=e.match(/:\/\/[^@]+@([^?]+)/);if(t){let n=t[1],s="";const r=e.match(/#(.+)$/);r&&(s="#"+decodeURIComponent(r[1])),o.push(n+s)}}else a+=e+"\n"}catch(e){return new Response("preferredsubscribegeneratehandlerexception:"+e.message,{status:403})}}const i=config_JSON.ECH?`&ech=${encodeURIComponent("cloudflare-ech.com+"+ECH_DOH)}`:"";S=a+o.map(e=>{const t=e.match(/^(\[[\da-fA-F:]+\]|[\d.]+|[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*)(?::(\d+))?(?:#(.+))?$/);let s,o,a="443";return t?(s=t[1],a=t[2]||"443",o=t[3]||s,`${b}://00000000-0000-4000-8000-000000000000@${s}:${a}?security=tls&type=${config_JSON.transmittransferprotocol+i}&host=example.com&fp=${config_JSON.Fingerprint}&sni=example.com&path=${encodeURIComponent(config_JSON.randompath?randompath()+n:n)+r}&encryption=none${config_JSON.jumppasscertificateverify?"&insecure=1&allowInsecure=1":""}#${encodeURIComponent(o)}`):(console.warn(`[subscribecontent] notstandardIPformatalreadyignore: ${e}`),null)}).filter(e=>null!==e).join("\n")}else{const e=`${config_JSON.subscribeconvertconfig.SUBAPI}/sub?target=${w}&url=${encodeURIComponent(s.protocol+"//"+s.host+"/sub?target=mixed&token="+o+(s.searchParams.has("sub")&&""!=s.searchParams.get("sub")?`&sub=${s.searchParams.get("sub")}`:""))}&config=${encodeURIComponent(config_JSON.subscribeconvertconfig.SUBCONFIG)}&emoji=${config_JSON.subscribeconvertconfig.SUBEMOJI}&scv=${config_JSON.jumppasscertificateverify}`;try{const t=await fetch(e,{headers:{"User-Agent":"Subconverter for "+w+" edgetunnel(https://github.com/cmliu/edgetunnel)"}});if(!t.ok)return new Response("subscribeconvertbackendexception:"+t.statusText,{status:t.status});S=await t.text(),(s.searchParams.has("surge")||a.includes("surge"))&&(S=Surgesubscribeconfigfilehotpatch(S,s.protocol+"//"+s.host+"/sub?token="+o+"&surge",config_JSON))}catch(e){return new Response("subscribeconvertbackendexception:"+e.message,{status:403})}}return a.includes("subconverter")||(S=await batchamountreplacechangedomain(S.replace(/00000000-0000-4000-8000-000000000000/g,config_JSON.UUID),config_JSON.HOSTS)),"mixed"!==w||a.includes("mozilla")&&!s.searchParams.has("b64")&&!s.searchParams.has("base64")||(S=btoa(S)),"singbox"===w?(S=Singboxsubscribeconfigfilehotpatch(S,config_JSON.UUID,config_JSON.Fingerprint,config_JSON.ECH?await getECH(f):null),y["content-type"]="application/json; charset=utf-8"):"clash"===w&&(S=Clashsubscribeconfigfilehotpatch(S,config_JSON.UUID,config_JSON.ECH,config_JSON.HOSTS),y["content-type"]="application/x-yaml; charset=utf-8"),new Response(S,{status:200,headers:y})}}else if("locations"===o){const t=e.headers.get("Cookie")||"",n=t.split(";").find(e=>e.trim().startsWith("auth="))?.split("=")[1];if(n&&n==await MD5MD5(r+i+a))return fetch(new Request("https://speed.cloudflare.com/locations",{headers:{Referer:"https://speed.cloudflare.com/"}}))}else if("robots.txt"===o)return new Response("User-agent: *\nDisallow: /",{status:200,headers:{"Content-Type":"text/plain; charset=UTF-8"}})}}let g=t.URL||"nginx";if(g&&"nginx"!==g&&"1101"!==g){g=g.trim().replace(/\/$/,""),g.match(/^https?:\/\//i)||(g="https://"+g),g.toLowerCase().startsWith("http://")&&(g="https://"+g.substring(7));try{const e=new URL(g);g=e.protocol+"//"+e.host}catch(e){g="nginx"}}if("1101"===g)return new Response(await html1101(s.host,h),{status:200,headers:{"Content-Type":"text/html; charset=UTF-8"}});try{const t=new URL(g),n=new Headers(e.headers);n.set("Host",t.host),n.set("Referer",t.origin),n.set("Origin",t.origin),!n.has("User-Agent")&&r&&"null"!==r&&n.set("User-Agent",r);const o=await fetch(t.origin+s.pathname+s.search,{method:e.method,headers:n,body:e.body,cf:e.cf}),a=o.headers.get("content-type")||"";if(/text|javascript|json|xml/.test(a)){const e=(await o.text()).replaceAll(t.host,s.host);return new Response(e,{status:o.status,headers:{...Object.fromEntries(o.headers),"Cache-Control":"no-store"}})}return o}catch(e){}return new Response(await nginx(),{status:200,headers:{"Content-Type":"text/html; charset=UTF-8"}})}};async function handleWebSocket(e,t,n=""){const s=new WebSocketPair,[r,o]=Object.values(s);o.accept();let a={socket:null},i=!1;const c=e.headers.get("sec-websocket-protocol")||"",l=makeReadableStr(o,c);let d=null,u=null;if(n){const e=n.split(":");e.length>=2&&(u={hostname:e[0],port:parseInt(e[1],10),username:e[2]||"",password:e[3]||""},console.log(`[twoProxy] enabled: ${u.hostname}:${u.port}`))}return l.pipeTo(new WritableStream({async write(e){if(i)return await forwardataudp(e,o,null);if(a.socket){const t=a.socket.writable.getWriter();return await t.write(e),void t.releaseLock()}if(null===d){const t=new Uint8Array(e);d=t.byteLength>=58&&13===t[56]&&10===t[57]}if(a.socket){const t=a.socket.writable.getWriter();return await t.write(e),void t.releaseLock()}if(d){const{port:n,hostname:s,rawClientData:r}=parsetrojanpleaserequest(e,t);if(isSpeedTestSite(s))throw new Error("Speedtest site is blocked");await forwardataTCP(s,n,r,o,null,a,t,u)}else{const{port:n,hostname:s,rawIndex:r,version:c,isUDP:l}=parsevlesspleaserequest(e,t);if(isSpeedTestSite(s))throw new Error("Speedtest site is blocked");if(l){if(53!==n)throw new Error("UDP is not supported");i=!0}const d=new Uint8Array([c[0],0]),p=e.slice(r);if(i)return forwardataudp(p,o,d);await forwardataTCP(s,n,p,o,d,a,t,u)}}})).catch(e=>{}),new Response(null,{status:101,webSocket:r})}function parsetrojanpleaserequest(e,t){const n=sha224(t);if(e.byteLength<56)return{hasError:!0,message:"invalid data"};if(13!==new Uint8Array(e.slice(56,57))[0]||10!==new Uint8Array(e.slice(57,58))[0])return{hasError:!0,message:"invalid header format"};if((new TextDecoder).decode(e.slice(0,56))!==n)return{hasError:!0,message:"invalid password"};const s=e.slice(58);if(s.byteLength<6)return{hasError:!0,message:"invalid S5 request data"};const r=new DataView(s);if(1!==r.getUint8(0))return{hasError:!0,message:"unsupported command, only TCP is allowed"};const o=r.getUint8(1);let a=0,i=2,c="";switch(o){case 1:a=4,c=new Uint8Array(s.slice(i,i+a)).join(".");break;case 3:a=new Uint8Array(s.slice(i,i+1))[0],i+=1,c=(new TextDecoder).decode(s.slice(i,i+a));break;case 4:a=16;const e=new DataView(s.slice(i,i+a)),t=[];for(let n=0;n<8;n++)t.push(e.getUint16(2*n).toString(16));c=t.join(":");break;default:return{hasError:!0,message:`invalid addressType is ${o}`}}if(!c)return{hasError:!0,message:`address is empty, addressType is ${o}`};const l=i+a,d=s.slice(l,l+2);return{hasError:!1,addressType:o,port:new DataView(d).getUint16(0),hostname:c,rawClientData:s.slice(l+4)}}function parsevlesspleaserequest(e,t){if(e.byteLength<24)return{hasError:!0,message:"Invalid data"};const n=new Uint8Array(e.slice(0,1));if(formatIdentifier(new Uint8Array(e.slice(1,17)))!==t)return{hasError:!0,message:"Invalid uuid"};const s=new Uint8Array(e.slice(17,18))[0],r=new Uint8Array(e.slice(18+s,19+s))[0];let o=!1;if(1===r);else{if(2!==r)return{hasError:!0,message:"Invalid command"};o=!0}const a=19+s,i=new DataView(e.slice(a,a+2)).getUint16(0);let c=a+2,l=0,d=c+1,u="";const p=new Uint8Array(e.slice(c,d))[0];switch(p){case 1:l=4,u=new Uint8Array(e.slice(d,d+l)).join(".");break;case 2:l=new Uint8Array(e.slice(d,d+1))[0],d+=1,u=(new TextDecoder).decode(e.slice(d,d+l));break;case 3:l=16;const t=[],n=new DataView(e.slice(d,d+l));for(let e=0;e<8;e++)t.push(n.getUint16(2*e).toString(16));u=t.join(":");break;default:return{hasError:!0,message:`Invalid address type: ${p}`}}return u?{hasError:!1,addressType:p,port:i,hostname:u,isUDP:o,rawIndex:d+l,version:n}:{hasError:!0,message:`Invalid address: ${p}`}}async function forwardataTCP(e,t,n,s,r,o,a,i=null){async function c(e,t,n,r=null,o=!0){let a;if(r&&r.length>0)for(let e=0;esetTimeout(()=>t(new Error("connectiontimeout")),1e3))]);const e=a.writable.getWriter();return await e.write(n),e.releaseLock(),console.log(`[reversedelegateconnection] successconnectionto: ${s}:${o}`),cachedProxyIndex=t,a}catch(e){console.log(`[reversedelegateconnection] connectionfailed: ${s}:${o}, error: ${e.message}`);try{a?.close?.()}catch(e){}continue}}if(o){a=connect({hostname:e,port:t});const s=a.writable.getWriter();return await s.write(n),s.releaseLock(),a}throw closeSocketQuietly(s),new Error("[reversedelegateconnection] allhavereversedelegateconnectionfailed,andnotenableProxyFallback,connectionterminate。")}async function l(){let i;if("socks5"===enableSocks5Proxy)console.log(`[SOCKS5proxy] proxyto: ${e}:${t}`),i=await socks5Connect(e,t,n);else if("http"===enableSocks5Proxy||"https"===enableSocks5Proxy)console.log(`[HTTPproxy] proxyto: ${e}:${t}`),i=await httpConnect(e,t,n);else{console.log(`[reversedelegateconnection] proxyto: ${e}:${t}`);const s=await parseplaceaddressport(proxyIP,e,a);i=await c(atob("UFJPWFlJUC50cDEuMDkwMjI3Lnh5eg=="),1,n,s,enableProxyFallback)}o.socket=i,i.closed.catch(()=>{}).finally(()=>closeSocketQuietly(s)),connectStreams(i,s,r,null)}console.log(`[TCPturnsend] target: ${e}:${t} | proxyIP: ${proxyIP} | twoProxy: ${i?i.hostname+":"+i.port:"no"} | reversedelegatetype: ${enableSocks5Proxy||"proxyip"}`);if(i){console.log("[TCPturnsend] makeusetwoProxy");try{const a=await async function(){if(!i)throw new Error("twoProxynotconfig");console.log(`[twoProxy] via ${i.hostname}:${i.port} connectionto ${e}:${t}`);const s=connect({hostname:i.hostname,port:i.port}),r=s.writable.getWriter(),o=s.readable.getReader();try{const a=i.username&&i.password?`Proxy-Authorization: Basic ${btoa(`${i.username}:${i.password}`)}\r\n`:"",c=`CONNECT ${e}:${t} HTTP/1.1\r\nHost: ${e}:${t}\r\n${a}User-Agent: CFspider/1.8.3\r\nConnection: keep-alive\r\n\r\n`;await r.write((new TextEncoder).encode(c));let l=new Uint8Array(0),d=-1,u=0;for(;-1===d&&u<8192;){const{done:e,value:t}=await o.read();if(e)throw new Error("proxyconnectionclose");l=new Uint8Array([...l,...t]),u=l.length;for(let e=0;e=300)throw new Error(`proxyconnectionfailed: HTTP ${f}`);return console.log(`[twoProxy] tunnelestablishsuccess: ${e}:${t}`),await r.write(n),r.releaseLock(),o.releaseLock(),s}catch(e){try{r.releaseLock()}catch(e){}try{o.releaseLock()}catch(e){}try{s.close()}catch(e){}throw e}}();o.socket=a,a.closed.catch(()=>{}).finally(()=>closeSocketQuietly(s)),connectStreams(a,s,r,null)}catch(e){console.log(`[twoProxy] connectionfailed: ${e.message}, backexittodefaultmethodstyle`);try{await l()}catch(e){throw e}}}else if(enableSocks5Proxy&&(enableGlobalSocks5||(d=e,socks5Whitelist.some(e=>new RegExp(`^${e.replace(/\*/g,".*")}$`,"i").test(d))))){console.log("[TCPturnsend] startuse SOCKS5/HTTP globalproxy");try{await l()}catch(e){throw e}}else try{console.log(`[TCPturnsend] trydirectto: ${e}:${t}`);const a=await c(e,t,n);o.socket=a,connectStreams(a,s,r,l)}catch(e){await l()}var d}async function forwardataudp(e,t,n){try{const s=connect({hostname:"8.8.4.4",port:53});let r=n;const o=s.writable.getWriter();await o.write(e),o.releaseLock(),await s.readable.pipeTo(new WritableStream({async write(e){if(t.readyState===WebSocket.OPEN)if(r){const n=new Uint8Array(r.length+e.byteLength);n.set(r,0),n.set(e,r.length),t.send(n.buffer),r=null}else t.send(e)}}))}catch(e){}}function closeSocketQuietly(e){try{e.readyState!==WebSocket.OPEN&&e.readyState!==WebSocket.CLOSING||e.close()}catch(e){}}function formatIdentifier(e,t=0){const n=[...e.slice(t,t+16)].map(e=>e.toString(16).padStart(2,"0")).join("");return`${n.substring(0,8)}-${n.substring(8,12)}-${n.substring(12,16)}-${n.substring(16,20)}-${n.substring(20)}`}async function connectStreams(e,t,n,s){let r=n,o=!1;await e.readable.pipeTo(new WritableStream({async write(e,n){if(o=!0,t.readyState!==WebSocket.OPEN&&n.error("ws.readyState is not open"),r){const n=new Uint8Array(r.length+e.byteLength);n.set(r,0),n.set(e,r.length),t.send(n.buffer),r=null}else t.send(e)},abort(){}})).catch(e=>{closeSocketQuietly(t)}),!o&&s&&await s()}function makeReadableStr(e,t){let n=!1;return new ReadableStream({start(s){e.addEventListener("message",e=>{n||s.enqueue(e.data)}),e.addEventListener("close",()=>{n||(closeSocketQuietly(e),s.close())}),e.addEventListener("error",e=>s.error(e));const{earlyData:r,error:o}=base64ToArray(t);o?s.error(o):r&&s.enqueue(r)},cancel(){n=!0,closeSocketQuietly(e)}})}function isSpeedTestSite(e){const t=[atob("c3BlZWQuY2xvdWRmbGFyZS5jb20=")];if(t.includes(e))return!0;for(const n of t)if(e.endsWith("."+n)||e===n)return!0;return!1}function base64ToArray(e){if(!e)return{error:null};try{const t=atob(e.replace(/-/g,"+").replace(/_/g,"/")),n=new Uint8Array(t.length);for(let e=0;e>8,255&t]);if(await c.write(p),a=await l.read(),a.done||0!==new Uint8Array(a.value)[1])throw new Error("S5 connection failed");return await c.write(n),c.releaseLock(),l.releaseLock(),i}catch(e){try{c.releaseLock()}catch(e){}try{l.releaseLock()}catch(e){}try{i.close()}catch(e){}throw e}}async function httpConnect(e,t,n){const{username:s,password:r,hostname:o,port:a}=parsedSocks5Address,i=connect({hostname:o,port:a}),c=i.writable.getWriter(),l=i.readable.getReader();try{const o=`CONNECT ${e}:${t} HTTP/1.1\r\nHost: ${e}:${t}\r\n${s&&r?`Proxy-Authorization: Basic ${btoa(`${s}:${r}`)}\r\n`:""}User-Agent: Mozilla/5.0\r\nConnection: keep-alive\r\n\r\n`;await c.write((new TextEncoder).encode(o));let a=new Uint8Array(0),d=-1,u=0;for(;-1===d&&u<8192;){const{done:e,value:t}=await l.read();if(e)throw new Error("Connection closed before receiving HTTP response");a=new Uint8Array([...a,...t]),u=a.length;const n=a.findIndex((e,t)=>t=300)throw new Error(`Connection failed: HTTP ${p}`);return await c.write(n),c.releaseLock(),l.releaseLock(),i}catch(e){try{c.releaseLock()}catch(e){}try{l.releaseLock()}catch(e){}try{i.close()}catch(e){}throw e}}function Clashsubscribeconfigfilehotpatch(e,t=null,n=!1,s=[]){let r=e.replace(/mode:\s*Rule\b/g,"mode: rule");if(/^dns:\s*(?:\n|$)/m.test(r)||(r="dns:\n enable: true\n default-nameserver:\n - 223.5.5.5\n - 119.29.29.29\n - 114.114.114.114\n use-hosts: true\n nameserver:\n - https://sm2.doh.pub/dns-query\n - https://dns.alidns.com/dns-query\n fallback:\n - 8.8.4.4\n - 101.101.101.101\n - 208.67.220.220\n fallback-filter:\n geoip: true\n domain: [+.google.com, +.facebook.com, +.youtube.com]\n ipcidr:\n - 240.0.0.0/4\n - 0.0.0.0/32\n geoip-code: CN\n"+r),n&&s.length>0){const e=s.map(e=>` "${e}":\n - tls://8.8.8.8\n - https://doh.cmliussss.com/CMLiussss\n - ${ECH_DOH}`).join("\n");if(/^\s{2}nameserver-policy:\s*(?:\n|$)/m.test(r))r=r.replace(/^(\s{2}nameserver-policy:\s*\n)/m,`$1${e}\n`);else{const t=r.split("\n");let n=-1,s=!1;for(let e=0;e0&&i+1=0;t--)if(n[t].trim()){e=t;break}if(e>=0){const t=" ".repeat(r);n.splice(e+1,0,`${t}ech-opts:`,`${t} enable: true`)}}a.push(...n)}else a.push(e),i++}return a.join("\n")}function Singboxsubscribeconfigfilehotpatch(e,t=null,n="chrome",s=null){try{let r=JSON.parse(e);Array.isArray(r.inbounds)&&r.inbounds.forEach(e=>{if("tun"===e.type){const t=[];e.inet4_address&&t.push(e.inet4_address),e.inet6_address&&t.push(e.inet6_address),t.length>0&&(e.address=t,delete e.inet4_address,delete e.inet6_address);const n=[];Array.isArray(e.inet4_route_address)&&n.push(...e.inet4_route_address),Array.isArray(e.inet6_route_address)&&n.push(...e.inet6_route_address),n.length>0&&(e.route_address=n,delete e.inet4_route_address,delete e.inet6_route_address);const s=[];Array.isArray(e.inet4_route_exclude_address)&&s.push(...e.inet4_route_exclude_address),Array.isArray(e.inet6_route_exclude_address)&&s.push(...e.inet6_route_exclude_address),s.length>0&&(e.route_exclude_address=s,delete e.inet4_route_exclude_address,delete e.inet6_route_exclude_address)}});const o=new Map,a=(e,t=!1)=>{Array.isArray(e)&&e.forEach(e=>{if(e.geosite){const t=Array.isArray(e.geosite)?e.geosite:[e.geosite];e.rule_set=t.map(e=>{const t=`geosite-${e}`;return o.has(t)||o.set(t,{tag:t,type:"remote",format:"binary",url:`https://gh.090227.xyz/https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-${e}.srs`,download_detour:"DIRECT"}),t}),delete e.geosite}if(e.geoip){const t=Array.isArray(e.geoip)?e.geoip:[e.geoip];e.rule_set=e.rule_set||[],t.forEach(t=>{const n=`geoip-${t}`;o.has(n)||o.set(n,{tag:n,type:"remote",format:"binary",url:`https://gh.090227.xyz/https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-${t}.srs`,download_detour:"DIRECT"}),e.rule_set.push(n)}),delete e.geoip}const n=t?"server":"outbound",s=String(e[n]).toUpperCase();"REJECT"!==s&&"BLOCK"!==s||(e.action="reject",e.method="drop",delete e[n])})};r.dns&&r.dns.rules&&a(r.dns.rules,!0),r.route&&r.route.rules&&a(r.route.rules,!1),o.size>0&&(r.route||(r.route={}),r.route.rule_set=Array.from(o.values())),r.outbounds||(r.outbounds=[]),r.outbounds=r.outbounds.filter(e=>"REJECT"!==e.tag&&"block"!==e.tag);const i=new Set(r.outbounds.map(e=>e.tag));if(i.has("DIRECT")||(r.outbounds.push({type:"direct",tag:"DIRECT"}),i.add("DIRECT")),r.dns&&r.dns.servers){const e=new Set(r.dns.servers.map(e=>e.tag));r.dns.rules&&r.dns.rules.forEach(t=>{t.server&&!e.has(t.server)&&("dns_block"===t.server&&e.has("block")?t.server="block":t.server.toLowerCase().includes("block")&&!e.has(t.server)&&(r.dns.servers.push({tag:t.server,address:"rcode://success"}),e.add(t.server)))})}return r.outbounds.forEach(e=>{"selector"!==e.type&&"urltest"!==e.type||Array.isArray(e.outbounds)&&(e.outbounds=e.outbounds.filter(e=>{const t=e.toUpperCase();return i.has(e)&&"REJECT"!==t&&"BLOCK"!==t}),0===e.outbounds.length&&e.outbounds.push("DIRECT"))}),t&&r.outbounds.forEach(e=>{(e.uuid&&e.uuid===t||e.password&&e.password===t)&&(e.tls||(e.tls={enabled:!0}),n&&(e.tls.utls={enabled:!0,fingerprint:n}),s&&(e.tls.ech={enabled:!0,config:`-----BEGIN ECH CONFIGS-----\n${s}\n-----END ECH CONFIGS-----`}))}),JSON.stringify(r,null,2)}catch(t){return console.error("Singboxhotpatchexecutelinefailed:",t),JSON.stringify(JSON.parse(e),null,2)}}function Surgesubscribeconfigfilehotpatch(e,t,n){const s=e.includes("\r\n")?e.split("\r\n"):e.split("\n");let r="";const o=n.startuse0RTT?n.PATH+"?ed=2560":n.PATH;for(let e of s)if(!e.includes("= trojan,")||e.includes("ws=true")||e.includes("ws-path="))r+=e+"\n";else{const t=e.split("sni=")[1].split(",")[0],s=`sni=${t}, skip-cert-verify=${n.jumppasscertificateverify}`,a=`sni=${t}, skip-cert-verify=${n.jumppasscertificateverify}, ws=true, ws-path=${o}, ws-headers=Host:"${t}"`;r+=e.replace(new RegExp(s,"g"),a).replace("[","").replace("]","")+"\n"}return r=`#!MANAGED-CONFIG ${t} interval=${60*n.preferredsubscribegenerate.SUBUpdateTime*60} strict=false`+r.substring(r.indexOf("\n")),r}async function pleaserequestlogrecord(e,t,n,s="Get_SUB",r){try{const o=new Date,a={TYPE:s,IP:n,ASN:`AS${t.cf.asn||"0"} ${t.cf.asOrganization||"Unknown"}`,CC:`${t.cf.country||"N/A"} ${t.cf.city||"N/A"}`,URL:t.url,UA:t.headers.get("User-Agent")||"Unknown",TIME:o.getTime()};let i=[];const c=await e.KV.get("log.json");if(c)try{if(i=JSON.parse(c),Array.isArray(i))if("Get_SUB"!==s){const e=o.getTime()-18e5;if(i.some(s=>"Get_SUB"!==s.TYPE&&s.IP===n&&s.URL===t.url&&s.UA===(t.headers.get("User-Agent")||"Unknown")&&s.TIME>=e))return;for(i.push(a);JSON.stringify(i,null,2).length>4194304&&i.length>0;)i.shift()}else for(i.push(a);JSON.stringify(i,null,2).length>4194304&&i.length>0;)i.shift();else i=[a];if(r.TG.startuse)try{const t=await e.KV.get("tg.json"),n=JSON.parse(t);await sendMessage(n.BotToken,n.ChatID,a,r)}catch(e){console.error(`readtg.jsonouterror: ${e.message}`)}}catch(e){i=[a]}else i=[a];await e.KV.put("log.json",JSON.stringify(i,null,2))}catch(e){console.error(`logrecordfailed: ${e.message}`)}}async function sendMessage(e,t,n,s){if(e&&t)try{const r=new Date(n.TIME).toLocaleString("zh-CN",{timeZone:"Asia/Shanghai"}),o=new URL(n.URL),a=`#${s.preferredsubscribegenerate.SUBNAME} logthroughknow\n\n📌 type:#${n.TYPE}\n🌐 IP:${n.IP}\n📍 bitset:${n.CC}\n🏢 ASN:${n.ASN}\n🔗 domain:${o.host}\n🔍 path:${o.pathname+o.search}\n🤖 UA:${n.UA}\n📅 time:${r}\n`+(s.CF.Usage.success?`📊 pleaserequestuseamount:${s.CF.Usage.total}/100000 ${(s.CF.Usage.total/1e5*100).toFixed(2)}%\n`:""),i=`https://api.telegram.org/bot${e}/sendMessage?chat_id=${t}&parse_mode=HTML&text=${encodeURIComponent(a)}`;return fetch(i,{method:"GET",headers:{Accept:"text/html,application/xhtml+xml,application/xml;","Accept-Encoding":"gzip, deflate, br","User-Agent":n.UA||"Unknown"}})}catch(e){console.error("Error sending message:",e)}}function maskcodesensitiveinfo(e,t=3,n=2){if(!e||"string"!=typeof e)return e;if(e.length<=t+n)return e;const s=e.slice(0,t),r=e.slice(-n),o=e.length-t-n;return`${s}${"*".repeat(o)}${r}`}async function MD5MD5(e){const t=new TextEncoder,n=await crypto.subtle.digest("MD5",t.encode(e)),s=Array.from(new Uint8Array(n)).map(e=>e.toString(16).padStart(2,"0")).join(""),r=await crypto.subtle.digest("MD5",t.encode(s.slice(7,27)));return Array.from(new Uint8Array(r)).map(e=>e.toString(16).padStart(2,"0")).join("").toLowerCase()}function randompath(){const e=Math.floor(3*Math.random()+1);return`/${["about","account","acg","act","activity","ad","ads","ajax","album","albums","anime","api","app","apps","archive","archives","article","articles","ask","auth","avatar","bbs","bd","blog","blogs","book","books","bt","buy","cart","category","categories","cb","channel","channels","chat","china","city","class","classify","clip","clips","club","cn","code","collect","collection","comic","comics","community","company","config","contact","content","course","courses","cp","data","detail","details","dh","directory","discount","discuss","dl","dload","doc","docs","document","documents","doujin","download","downloads","drama","edu","en","ep","episode","episodes","event","events","f","faq","favorite","favourites","favs","feedback","file","files","film","films","forum","forums","friend","friends","game","games","gif","go","go.html","go.php","group","groups","help","home","hot","htm","html","image","images","img","index","info","intro","item","items","ja","jp","jump","jump.html","jump.php","jumping","knowledge","lang","lesson","lessons","lib","library","link","links","list","live","lives","m","mag","magnet","mall","manhua","map","member","members","message","messages","mobile","movie","movies","music","my","new","news","note","novel","novels","online","order","out","out.html","out.php","outbound","p","page","pages","pay","payment","pdf","photo","photos","pic","pics","picture","pictures","play","player","playlist","post","posts","product","products","program","programs","project","qa","question","rank","ranking","read","readme","redirect","redirect.html","redirect.php","reg","register","res","resource","retrieve","sale","search","season","seasons","section","seller","series","service","services","setting","settings","share","shop","show","shows","site","soft","sort","source","special","star","stars","static","stock","store","stream","streaming","streams","student","study","tag","tags","task","teacher","team","tech","temp","test","thread","tool","tools","topic","topics","torrent","trade","travel","tv","txt","type","u","upload","uploads","url","urls","user","users","v","version","video","videos","view","vip","vod","watch","web","wenku","wiki","work","www","zh","zh-cn","zh-tw","zip"].sort(()=>.5-Math.random()).slice(0,e).join("/")}`}function randomreplacechangethroughconfigsymbol(e){if(!e?.includes("*"))return e;return e.replace(/\*/g,()=>{let e="";for(let t=0;tMath.random()-.5);let r=0,o=null;return e.replace(/example\.com/g,()=>(r%n===0&&(o=randomreplacechangethroughconfigsymbol(s[Math.floor(r/n)%s.length])),r++,o))}async function getECH(e){try{const t=await fetch(`https://1.1.1.1/dns-query?name=${encodeURIComponent(e)}&type=65`,{headers:{accept:"application/dns-json"}}),n=await t.json();if(!n.Answer?.length)return"";for(let e of n.Answer){if(65!==e.type||!e.data)continue;const t=e.data.match(/ech=([^\s]+)/);if(t)return t[1].replace(/"/g,"");if(e.data.startsWith("\\#")){const t=e.data.split(" ").slice(2).join(""),n=new Uint8Array(t.match(/.{1,2}/g).map(e=>parseInt(e,16)));let s=2;for(;se.toLowerCase().replace(/^https?:\/\//,"").split("/")[0].split(":")[0])),config_JSON.UUID=n,config_JSON.PATH=s?s.startsWith("/")?s:"/"+s:config_JSON.reversedelegate.SOCKS5.startuse?"/"+config_JSON.reversedelegate.SOCKS5.startuse+(config_JSON.reversedelegate.SOCKS5.global?"://":"=")+config_JSON.reversedelegate.SOCKS5.accountid:"auto"===config_JSON.reversedelegate.PROXYIP?"/":`/proxyip=${config_JSON.reversedelegate.PROXYIP}`;const c="Shadowrocket"==config_JSON.TLSsplitslice?`&fragment=${encodeURIComponent("1,40-60,30-50,tlshello")}`:"Happ"==config_JSON.TLSsplitslice?`&fragment=${encodeURIComponent("3,1,tlshello")}`:"";config_JSON.Fingerprint||(config_JSON.Fingerprint="chrome"),config_JSON.ECH?config_JSON.preferredsubscribegenerate.SUBUpdateTime=1:config_JSON.ECH=!1;const l=config_JSON.ECH?`&ech=${encodeURIComponent("cloudflare-ech.com+"+ECH_DOH)}`:"";config_JSON.LINK=`${config_JSON.protocoltype}://${n}@${o}:443?security=tls&type=${config_JSON.transmittransferprotocol+l}&host=${o}&fp=${config_JSON.Fingerprint}&sni=${o}&path=${encodeURIComponent(config_JSON.startuse0RTT?config_JSON.PATH+"?ed=2560":config_JSON.PATH)+c}&encryption=none${config_JSON.jumppasscertificateverify?"&insecure=1&allowInsecure=1":""}#${encodeURIComponent(config_JSON.preferredsubscribegenerate.SUBNAME)}`,config_JSON.preferredsubscribegenerate.TOKEN=await MD5MD5(t+n);const d={BotToken:null,ChatID:null};config_JSON.TG={startuse:!!config_JSON.TG.startuse&&config_JSON.TG.startuse,...d};try{const t=await e.KV.get("tg.json");if(t){const e=JSON.parse(t);config_JSON.TG.ChatID=e.ChatID?e.ChatID:null,config_JSON.TG.BotToken=e.BotToken?maskcodesensitiveinfo(e.BotToken):null}else await e.KV.put("tg.json",JSON.stringify(d,null,2))}catch(e){console.error(`readtg.jsonouterror: ${e.message}`)}const u={Email:null,GlobalAPIKey:null,AccountID:null,APIToken:null,UsageAPI:null};config_JSON.CF={...u,Usage:{success:!1,pages:0,workers:0,total:0,max:1e5}};try{const t=await e.KV.get("cf.json");if(t){const e=JSON.parse(t);if(e.UsageAPI)try{const t=await fetch(e.UsageAPI),n=await t.json();config_JSON.CF.Usage=n}catch(e){console.error(`pleaserequest CF_JSON.UsageAPI failed: ${e.message}`)}else{config_JSON.CF.Email=e.Email?e.Email:null,config_JSON.CF.GlobalAPIKey=e.GlobalAPIKey?maskcodesensitiveinfo(e.GlobalAPIKey):null,config_JSON.CF.AccountID=e.AccountID?maskcodesensitiveinfo(e.AccountID):null,config_JSON.CF.APIToken=e.APIToken?maskcodesensitiveinfo(e.APIToken):null,config_JSON.CF.UsageAPI=null;const t=await getCloudflareUsage(e.Email,e.GlobalAPIKey,e.AccountID,e.APIToken);config_JSON.CF.Usage=t}}else await e.KV.put("cf.json",JSON.stringify(u,null,2))}catch(e){console.error(`readcf.jsonouterror: ${e.message}`)}return config_JSON.plusloadtime=(performance.now()-a).toFixed(2)+"ms",config_JSON}async function generaterandomIP(e,t=16,n=-1){const s={9808:"cmcc",4837:"cu",4134:"ct"},r=e.cf.asn,o=s[r]?`https://raw.githubusercontent.com/cmliu/cmliu/main/CF-CIDR/${s[r]}.txt`:"https://raw.githubusercontent.com/cmliu/cmliu/main/CF-CIDR.txt",a={9808:"CFmovemovepreferred",4837:"CFconnectthroughpreferred",4134:"CFtelecompreferred"}[r]||"CFofficialmethodpreferred",i=[443,2053,2083,2087,2096,8443];let c=[];try{const e=await fetch(o);c=e.ok?await parseToArray(await e.text()):["104.16.0.0/13"]}catch{c=["104.16.0.0/13"]}const l=Array.from({length:t},()=>`${(e=>{const[t,n]=e.split("/"),s=32-parseInt(n),r=((t.split(".").reduce((e,t,n)=>e|parseInt(t)<<24-8*n,0)&4294967295<>>0)>>>0)+Math.floor(Math.random()*Math.pow(2,s))>>>0;return[r>>>24&255,r>>>16&255,r>>>8&255,255&r].join(".")})(c[Math.floor(Math.random()*c.length)])}:${-1===n?i[Math.floor(Math.random()*i.length)]:n}#${a}`);return[l,l.join("\n")]}async function parseToArray(e){var t=e.replace(/[ "'\r\n]+/g,",").replace(/,+/g,",");","==t.charAt(0)&&(t=t.slice(1)),","==t.charAt(t.length-1)&&(t=t.slice(0,t.length-1));return t.split(",")}function isValidBase64(e){if("string"!=typeof e)return!1;const t=e.replace(/\s/g,"");if(0===t.length||t.length%4!=0)return!1;if(!/^[A-Za-z0-9+/]+={0,2}$/.test(t))return!1;try{return atob(t),!0}catch{return!1}}function base64Decode(e){const t=new Uint8Array(atob(e).split("").map(e=>e.charCodeAt(0)));return new TextDecoder("utf-8").decode(t)}async function pleaserequestpreferredAPI(e,t="443",n=3e3){if(!e?.length)return[[],[],[]];const s=new Set;let r="";await Promise.allSettled(e.map(async e=>{try{const o=new AbortController,a=setTimeout(()=>o.abort(),n),i=await fetch(e,{signal:o.signal});clearTimeout(a);let c="";try{const e=await i.arrayBuffer(),t=(i.headers.get("content-type")||"").toLowerCase(),n=t.match(/charset=([^\s;]+)/i)?.[1]?.toLowerCase()||"";let s=["utf-8","gb2312"];(n.includes("gb")||n.includes("gbk")||n.includes("gb2312"))&&(s=["gb2312","utf-8"]);let r=!1;for(const t of s)try{const n=new TextDecoder(t).decode(e);if(n&&n.length>0&&!n.includes("�")){c=n,r=!0;break}if(n&&n.length>0)continue}catch(e){continue}if(r||(c=await i.text()),!c||0===c.trim().length)return}catch(e){return void console.error("Failed to decode response:",e)}const l=isValidBase64(c)?base64Decode(c):c;if(l.split("#")[0].includes("://"))return void(r+=l+"\n");const d=c.trim().split("\n").map(e=>e.trim()).filter(e=>e),u=d.length>1&&d[0].includes(","),p=/^[^\[\]]*:[^\[\]]*:[^\[\]]/;if(u){const n=d[0].split(",").map(e=>e.trim()),r=d.slice(1);if(n.includes("IPplaceaddress")&&n.includes("port")&&n.includes("dataincenter")){const e=n.indexOf("IPplaceaddress"),t=n.indexOf("port"),o=n.indexOf("country")>-1?n.indexOf("country"):n.indexOf("city")>-1?n.indexOf("city"):n.indexOf("dataincenter"),a=n.indexOf("TLS");r.forEach(n=>{const r=n.split(",").map(e=>e.trim());if(-1!==a&&"true"!==r[a]?.toLowerCase())return;const i=p.test(r[e])?`[${r[e]}]`:r[e];s.add(`${i}:${r[t]}#${r[o]}`)})}else if(n.some(e=>e.includes("IP"))&&n.some(e=>e.includes("delay"))&&n.some(e=>e.includes("downloadspeed"))){const o=n.findIndex(e=>e.includes("IP")),a=n.findIndex(e=>e.includes("delay")),i=n.findIndex(e=>e.includes("downloadspeed")),c=new URL(e).searchParams.get("port")||t;r.forEach(e=>{const t=e.split(",").map(e=>e.trim()),n=p.test(t[o])?`[${t[o]}]`:t[o];s.add(`${n}:${c}#CFpreferred ${t[a]}ms ${t[i]}MB/s`)})}}else d.forEach(n=>{const r=n.indexOf("#"),[o,a]=r>-1?[n.substring(0,r),n.substring(r)]:[n,""];let i=!1;if(o.startsWith("["))i=/\]:(\d+)$/.test(o);else{const e=o.lastIndexOf(":");i=e>-1&&/^\d+$/.test(o.substring(e+1))}const c=new URL(e).searchParams.get("port")||t;s.add(i?n:`${o}:${c}${a}`)})}catch(e){}}));const o=r.trim()?[...new Set(r.split(/\r?\n/).filter(e=>""!==e.trim()))]:[];return[Array.from(s),o,[]]}async function reversedelegateparamsget(e){const t=new URL(e.url),{pathname:n,searchParams:s}=t,r=n.toLowerCase();mySocks5Account=s.get("socks5")||s.get("http")||null,enableGlobalSocks5=s.has("globalproxy")||!1;const o=r.match(/\/(proxyip[.=]|pyip=|ip=)(.+)/);if(s.has("proxyip")){const e=s.get("proxyip");return proxyIP=e.includes(",")?e.split(",")[Math.floor(Math.random()*e.split(",").length)]:e,void(enableProxyFallback=!1)}if(o){const e="proxyip."===o[1]?`proxyip.${o[2]}`:o[2];return proxyIP=e.includes(",")?e.split(",")[Math.floor(Math.random()*e.split(",").length)]:e,void(enableProxyFallback=!1)}let a;if(a=n.match(/\/(socks5?|http):\/?\/?(.+)/i)){if(enableSocks5Proxy="http"===a[1].toLowerCase()?"http":"socks5",mySocks5Account=a[2].split("#")[0],enableGlobalSocks5=!0,mySocks5Account.includes("@")){const e=mySocks5Account.lastIndexOf("@");let t=mySocks5Account.substring(0,e).replaceAll("%3D","=");/^(?:[A-Z0-9+/]{4})*(?:[A-Z0-9+/]{2}==|[A-Z0-9+/]{3}=)?$/i.test(t)&&!t.includes(":")&&(t=atob(t)),mySocks5Account=`${t}@${mySocks5Account.substring(e+1)}`}}else if(a=n.match(/\/(g?s5|socks5|g?http)=(.+)/i)){const e=a[1].toLowerCase();mySocks5Account=a[2],enableSocks5Proxy=e.includes("http")?"http":"socks5",enableGlobalSocks5=e.startsWith("g")||enableGlobalSocks5}if(mySocks5Account)try{parsedSocks5Address=await getSOCKS5accountid(mySocks5Account),enableSocks5Proxy=s.get("http")?"http":enableSocks5Proxy}catch(e){console.error("parseSOCKS5placeaddressfailed:",e.message),enableSocks5Proxy=null}else enableSocks5Proxy=null}async function getSOCKS5accountid(e){if(e.includes("@")){const t=e.lastIndexOf("@");let n=e.substring(0,t).replaceAll("%3D","=");/^(?:[A-Z0-9+/]{4})*(?:[A-Z0-9+/]{2}==|[A-Z0-9+/]{3}=)?$/i.test(n)&&!n.includes(":")&&(n=atob(n)),e=`${n}@${e.substring(t+1)}`}const t=e.lastIndexOf("@"),[n,s]=-1===t?[e,void 0]:[e.substring(t+1),e.substring(0,t)];let r,o,a,i;if(s&&([r,o]=s.split(":"),!o))throw new Error('invalid SOCKS placeaddressformat:confirmcertpartsplitmustis "username:password" formstyle');if(n.includes("]:"))[a,i]=[n.split("]:")[0]+"]",Number(n.split("]:")[1].replace(/[^\d]/g,""))];else if(n.startsWith("["))[a,i]=[n,80];else{const e=n.split(":");[a,i]=2===e.length?[e[0],Number(e[1].replace(/[^\d]/g,""))]:[n,80]}if(isNaN(i))throw new Error("invalid SOCKS placeaddressformat:portidmustisnumberchar");if(a.includes(":")&&!/^\[.*\]$/.test(a))throw new Error("invalid SOCKS placeaddressformat:IPv6 placeaddressmustusemethodbracketidbracketup,if [2001:db8::1]");return{username:r,password:o,hostname:a,port:i}}async function getCloudflareUsage(e,t,n,s){const r="https://api.cloudflare.com/client/v4",o=e=>e?.reduce((e,t)=>e+(t?.sum?.requests||0),0)||0,a={"Content-Type":"application/json"};try{if(!(n||e&&t))return{success:!1,pages:0,workers:0,total:0,max:1e5};if(!n){const s=await fetch(`${r}/accounts`,{method:"GET",headers:{...a,"X-AUTH-EMAIL":e,"X-AUTH-KEY":t}});if(!s.ok)throw new Error(`accountusergetfailed: ${s.status}`);const o=await s.json();if(!o?.result?.length)throw new Error("notfindtoaccountuser");const i=o.result.findIndex(t=>t.name?.toLowerCase().startsWith(e.toLowerCase()));n=o.result[i>=0?i:0]?.id}const i=new Date;i.setUTCHours(0,0,0,0);const c=s?{...a,Authorization:`Bearer ${s}`}:{...a,"X-AUTH-EMAIL":e,"X-AUTH-KEY":t},l=await fetch(`${r}/graphql`,{method:"POST",headers:c,body:JSON.stringify({query:"query getBillingMetrics($AccountID: String!, $filter: AccountWorkersInvocationsAdaptiveFilter_InputObject) {\n viewer { accounts(filter: {accountTag: $AccountID}) {\n pagesFunctionsInvocationsAdaptiveGroups(limit: 1000, filter: $filter) { sum { requests } }\n workersInvocationsAdaptive(limit: 10000, filter: $filter) { sum { requests } }\n } }\n }",variables:{AccountID:n,filter:{datetime_geq:i.toISOString(),datetime_leq:(new Date).toISOString()}}})});if(!l.ok)throw new Error(`queryfailed: ${l.status}`);const d=await l.json();if(d.errors?.length)throw new Error(d.errors[0].message);const u=d?.data?.viewer?.accounts?.[0];if(!u)throw new Error("notfindtoaccountuserdata");const p=o(u.pagesFunctionsInvocationsAdaptiveGroups),f=o(u.workersInvocationsAdaptive),h=p+f,g=1e5;return console.log(`statsresult - Pages: ${p}, Workers: ${f}, totalcount: ${h}, uplimit: 100000`),{success:!0,pages:p,workers:f,total:h,max:g}}catch(e){return console.error("getmakeuseamounterror:",e.message),{success:!1,pages:0,workers:0,total:0,max:1e5}}}function sha224(e){const t=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],n=(e,t)=>(e>>>t|e<<32-t)>>>0,s=8*(e=unescape(encodeURIComponent(e))).length;for(e+=String.fromCharCode(128);8*e.length%512!=448;)e+=String.fromCharCode(0);const r=[3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428],o=Math.floor(s/4294967296),a=4294967295&s;e+=String.fromCharCode(o>>>24&255,o>>>16&255,o>>>8&255,255&o,a>>>24&255,a>>>16&255,a>>>8&255,255&a);const i=[];for(let t=0;t>>3,r=n(s[e-2],17)^n(s[e-2],19)^s[e-2]>>>10;s[e]=s[e-16]+t+s[e-7]+r>>>0}let[o,a,c,l,d,u,p,f]=r;for(let e=0;e<64;e++){const r=f+(n(d,6)^n(d,11)^n(d,25))+(d&u^~d&p)+t[e]+s[e]>>>0,i=o&a^o&c^a&c;f=p,p=u,u=d,d=l+r>>>0,l=c,c=a,a=o,o=r+((n(o,2)^n(o,13)^n(o,22))+i>>>0)>>>0}for(let e=0;e<8;e++)r[e]=r[e]+(0===e?o:1===e?a:2===e?c:3===e?l:4===e?d:5===e?u:6===e?p:f)>>>0}let c="";for(let e=0;e<7;e++)for(let t=24;t>=0;t-=8)c+=(r[e]>>>t&255).toString(16).padStart(2,"0");return c}async function parseplaceaddressport(e,t="dash.cloudflare.com",n="00000000-0000-4000-8000-000000000000"){if(cacheproxyIP&&cachedProxyArray&&cacheproxyIP===e)console.log(`[reversedelegateparse] readcache totalnumber: ${cachedProxyArray.length}one\n${cachedProxyArray.map(([e,t],n)=>`${n+1}. ${e}:${t}`).join("\n")}`);else{async function s(e,t){try{const n=await fetch(`https://1.1.1.1/dns-query?name=${e}&type=${t}`,{headers:{Accept:"application/dns-json"}});if(!n.ok)return[];return(await n.json()).Answer||[]}catch(e){return console.error(`DoHqueryfailed (${t}):`,e),[]}}function r(e){let t=e,n=443;if(e.includes("]:")){const s=e.split("]:");t=s[0]+"]",n=parseInt(s[1],10)||n}else if(e.includes(":")&&!e.startsWith("[")){const s=e.lastIndexOf(":");t=e.slice(0,s),n=parseInt(e.slice(s+1),10)||n}return[t,n]}let o=[];if((e=e.toLowerCase()).includes(".william"))try{const d=(await s(e,"TXT")).filter(e=>16===e.type).map(e=>e.data);if(d.length>0){let u=d[0];u.startsWith('"')&&u.endsWith('"')&&(u=u.slice(1,-1));o=u.replace(/\\010/g,",").replace(/\n/g,",").split(",").map(e=>e.trim()).filter(Boolean).map(e=>r(e))}}catch(p){console.error("parseWilliamdomainfailed:",p)}else{let[f,h]=r(e);if(e.includes(".tp")){const m=e.match(/\.tp(\d+)/);m&&(h=parseInt(m[1],10))}const g=/^\[?([a-fA-F0-9:]+)\]?$/;if(/^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/.test(f)||g.test(f))o=[[f,h]];else{const[y,w]=await Promise.all([s(f,"A"),s(f,"AAAA")]),b=[...y.filter(e=>1===e.type).map(e=>e.data),...w.filter(e=>28===e.type).map(e=>`[${e.data}]`)];o=b.length>0?b.map(e=>[e,h]):[[f,h]]}}const a=o.sort((e,t)=>e[0].localeCompare(t[0])),i=t.includes(".")?t.split(".").slice(-2).join("."):t;let c=[...i+n].reduce((e,t)=>e+t.charCodeAt(0),0);console.log(`[reversedelegateparse] randomseed: ${c}\ntargetstationpoint: ${i}`);const l=[...a].sort(()=>(c=1103515245*c+12345&2147483647)/2147483647-.5);cachedProxyArray=l.slice(0,8),console.log(`[reversedelegateparse] parsecomplete totalnumber: ${cachedProxyArray.length}one\n${cachedProxyArray.map(([e,t],n)=>`${n+1}. ${e}:${t}`).join("\n")}`),cacheproxyIP=e}return cachedProxyArray}async function SOCKS5canuseabilityverify(e="socks5",t){const n=Date.now();try{parsedSocks5Address=await getSOCKS5accountid(t)}catch(s){return{success:!1,error:s.message,proxy:e+"://"+t,responseTime:Date.now()-n}}const{username:s,password:r,hostname:o,port:a}=parsedSocks5Address,i=s&&r?`${s}:${r}@${o}:${a}`:`${o}:${a}`;try{const t=new Uint8Array(0),s="socks5"==e?await socks5Connect("check.socks5.090227.xyz",80,t):await httpConnect("check.socks5.090227.xyz",80,t);if(!s)return{success:!1,error:"nonemethodconnectiontoproxyservicehandler",proxy:e+"://"+i,responseTime:Date.now()-n};try{const t=s.writable.getWriter(),r=new TextEncoder;await t.write(r.encode("GET /cdn-cgi/trace HTTP/1.1\r\nHost: check.socks5.090227.xyz\r\nConnection: close\r\n\r\n")),t.releaseLock();const o=s.readable.getReader(),a=new TextDecoder;let c="";try{for(;;){const{done:e,value:t}=await o.read();if(e)break;c+=a.decode(t,{stream:!0})}}finally{o.releaseLock()}return await s.close(),{success:!0,proxy:e+"://"+i,ip:c.match(/ip=(.*)/)[1],loc:c.match(/loc=(.*)/)[1],responseTime:Date.now()-n}}catch(t){try{await s.close()}catch(e){console.log("closeconnectionwhenouterror:",e)}return{success:!1,error:t.message,proxy:e+"://"+i,responseTime:Date.now()-n}}}catch(t){return{success:!1,error:t.message,proxy:e+"://"+i,responseTime:Date.now()-n}}}async function nginx(){return'\n\t\n\t\n\t\n\tWelcome to nginx!\n\t\n\n\n\t

Welcome to nginx!

\n\t

If you see this page, the nginx web server is successfully installed and\n\tworking. Further configuration is required.

\n\t\n\t

For online documentation and support please refer to\n\tnginx.org.
\n\tCommercial support is available at\n\tnginx.com.

\n\t\n\t

Thank you for using nginx.

\n\t\n\t\n\t'}async function html1101(e,t){const n=new Date,s=n.getFullYear()+"-"+String(n.getMonth()+1).padStart(2,"0")+"-"+String(n.getDate()).padStart(2,"0")+" "+String(n.getHours()).padStart(2,"0")+":"+String(n.getMinutes()).padStart(2,"0")+":"+String(n.getSeconds()).padStart(2,"0"),r=Array.from(crypto.getRandomValues(new Uint8Array(8))).map(e=>e.toString(16).padStart(2,"0")).join("");return`\n\x3c!--[if lt IE 7]> \x3c!--\x3e \x3c!--\nWorker threw exception | ${e} | Cloudflare\n\n\n\n\n\n\n\x3c!--[if lt IE 9]>body{margin:0;padding:0}\n\n\n\x3c!--[if gte IE 10]>\x3c!--\x3e\n