init: fork of kejilion/sh with certbot host-network fix
Some checks failed
Weekly Translation Workflow / translate (push) Has been cancelled
Some checks failed
Weekly Translation Workflow / translate (push) Has been cancelled
- Replace 'docker run -p 80:80' with '--network host' for certbot standalone HTTP-01 to avoid timeout in environments where Docker NAT port-publish breaks LE inbound traffic but host port 80 is reachable. - Applies to kejilion.sh (all locale variants) and auto_cert_renewal.sh. - README install command points to this Gitea mirror.
This commit is contained in:
160
.github/workflows/translate.yml
vendored
Normal file
160
.github/workflows/translate.yml
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
name: Weekly Translation Workflow
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * 0'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
translate:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.9'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install deep-translator
|
||||
|
||||
- name: Create translation directories
|
||||
run: |
|
||||
mkdir -p en tw kr jp
|
||||
|
||||
- name: Create translation script
|
||||
run: |
|
||||
cat > translate.py << 'EOF'
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from deep_translator import GoogleTranslator
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
|
||||
def is_chinese(text):
|
||||
return bool(re.search(r'[\u4e00-\u9fff]', text))
|
||||
|
||||
def translate_text(text, target_lang):
|
||||
if not text.strip() or not is_chinese(text):
|
||||
return text
|
||||
try:
|
||||
# 过滤掉一些不该翻译的特殊符号
|
||||
clean_text = text.strip()
|
||||
result = GoogleTranslator(source='zh-CN', target=target_lang).translate(clean_text)
|
||||
return result
|
||||
except Exception as e:
|
||||
print(f"\n[Error] {e}")
|
||||
return text
|
||||
|
||||
def process_content_with_vars(content, target_lang):
|
||||
"""
|
||||
核心逻辑:保护 ${var} 和 $var,翻译其中的中文部分
|
||||
"""
|
||||
# 匹配 ${var} 或 $var (字母数字下划线)
|
||||
parts = re.split(r'(\$\{\w+\}|\$\w+)', content)
|
||||
translated_parts = []
|
||||
for p in parts:
|
||||
if p.startswith('$'): # 变量部分,保持原样
|
||||
translated_parts.append(p)
|
||||
elif is_chinese(p): # 中文部分,翻译
|
||||
translated_parts.append(translate_text(p, target_lang))
|
||||
else: # 其他英文/符号,保持原样
|
||||
translated_parts.append(p)
|
||||
return "".join(translated_parts)
|
||||
|
||||
def universal_translator(line, target_lang):
|
||||
"""
|
||||
通用翻译引擎:识别行内所有引号内容并翻译
|
||||
"""
|
||||
# 1. 保护注释行
|
||||
leading_space = re.match(r'^(\s*)', line).group(1)
|
||||
stripped = line.strip()
|
||||
if stripped.startswith('#'):
|
||||
comment_content = stripped[1:].strip()
|
||||
if is_chinese(comment_content):
|
||||
return f"{leading_space}# {translate_text(comment_content, target_lang)}\n"
|
||||
return line
|
||||
|
||||
# 2. 识别所有引号内的内容 (双引号或单引号)
|
||||
# 使用正则匹配引号对,同时处理转义引号 \"
|
||||
def replacer(match):
|
||||
quote_type = match.group(1) # ' 或 "
|
||||
content = match.group(2) # 引号内的文本内容
|
||||
if is_chinese(content):
|
||||
# 翻译内容,但保护里面的变量
|
||||
translated = process_content_with_vars(content, target_lang)
|
||||
return f"{quote_type}{translated}{quote_type}"
|
||||
return match.group(0)
|
||||
|
||||
# 匹配 "content" 或 'content'
|
||||
new_line = re.sub(r'([\'"])(.*?)(?<!\\)\1', replacer, line)
|
||||
return new_line
|
||||
|
||||
def translate_file(input_file, output_file, target_lang):
|
||||
print(f"Translating to {target_lang}...")
|
||||
if not os.path.exists(input_file): return False
|
||||
|
||||
with open(input_file, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
total = len(lines)
|
||||
with open(output_file, 'w', encoding='utf-8') as f_out:
|
||||
for i, line in enumerate(lines):
|
||||
if (i + 1) % 10 == 0 or i + 1 == total:
|
||||
print(f"\rProgress: {(i+1)/total*100:.1f}%", end='')
|
||||
|
||||
# 只要行内有中文,就尝试用通用引擎翻译
|
||||
if is_chinese(line):
|
||||
f_out.write(universal_translator(line, target_lang))
|
||||
else:
|
||||
f_out.write(line)
|
||||
print(f"\n{target_lang} Success.")
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
input_file = 'kejilion.sh'
|
||||
langs = {'en': 'en', 'tw': 'zh-TW', 'kr': 'ko', 'jp': 'ja'}
|
||||
for dir_name, lang_code in langs.items():
|
||||
translate_file(input_file, f'{dir_name}/kejilion.sh', lang_code)
|
||||
EOF
|
||||
|
||||
- name: Run translation
|
||||
run: python translate.py
|
||||
|
||||
- name: Check for changes
|
||||
id: check_changes
|
||||
run: |
|
||||
git add .
|
||||
git diff --staged --quiet || echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Commit and push changes
|
||||
if: steps.check_changes.outputs.has_changes == 'true'
|
||||
run: |
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
git commit -m "🌐 Weekly translation update - $(date +'%Y-%m-%d %H:%M:%S')"
|
||||
git push
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## Translation Summary 📊" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Language | Status |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY
|
||||
for dir in en tw kr jp; do
|
||||
[ -f "$dir/kejilion.sh" ] && status="✅ Success" || status="❌ Failed"
|
||||
echo "| $dir | $status |" >> $GITHUB_STEP_SUMMARY
|
||||
done
|
||||
67
CF-Under-Attack.sh
Normal file
67
CF-Under-Attack.sh
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 设置变量
|
||||
EMAIL="AAAA"
|
||||
API_KEY="BBBB"
|
||||
ZONE_ID="CCCC"
|
||||
LOAD_THRESHOLD=5.0 # 设置高负载阈值
|
||||
|
||||
TELEGRAM_BOT_TOKEN="输入TG机器人API"
|
||||
CHAT_ID="输入TG用户ID"
|
||||
|
||||
|
||||
# 获取当前系统负载
|
||||
CURRENT_LOAD=$(uptime | awk -F'load average:' '{ print $2 }' | cut -d, -f1 | awk '{print $1}')
|
||||
|
||||
echo "当前系统负载: $CURRENT_LOAD"
|
||||
|
||||
|
||||
send_tg_notification() {
|
||||
local MESSAGE=$1
|
||||
curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" -d "chat_id=$CHAT_ID" -d "text=$MESSAGE"
|
||||
}
|
||||
|
||||
|
||||
|
||||
# 获取当前的“Under Attack”模式状态
|
||||
STATUS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/settings/security_level" \
|
||||
-H "X-Auth-Email: $EMAIL" \
|
||||
-H "X-Auth-Key: $API_KEY" \
|
||||
-H "Content-Type: application/json" | jq -r '.result.value')
|
||||
|
||||
echo "当前的Under Attack模式状态: $STATUS"
|
||||
|
||||
# 检查系统负载是否高于阈值
|
||||
if (( $(echo "$CURRENT_LOAD > $LOAD_THRESHOLD" | bc -l) )); then
|
||||
if [ "$STATUS" != "under_attack" ]; then
|
||||
echo "系统负载高于阈值,开启Under Attack模式"
|
||||
# send_tg_notification "系统负载高于阈值,开启Under Attack模式"
|
||||
NEW_STATUS="under_attack"
|
||||
else
|
||||
echo "系统负载高,但Under Attack模式已经开启"
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
if [ "$STATUS" == "under_attack" ]; then
|
||||
echo "系统负载低于阈值,关闭Under Attack模式"
|
||||
# send_tg_notification "系统负载低于阈值,关闭Under Attack模式"
|
||||
NEW_STATUS="high"
|
||||
else
|
||||
echo "系统负载低,Under Attack模式已经关闭"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# 更新“Under Attack”模式状态
|
||||
RESPONSE=$(curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/settings/security_level" \
|
||||
-H "X-Auth-Email: $EMAIL" \
|
||||
-H "X-Auth-Key: $API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data "{\"value\":\"$NEW_STATUS\"}")
|
||||
|
||||
if [[ $(echo $RESPONSE | jq -r '.success') == "true" ]]; then
|
||||
echo "成功更新Under Attack模式状态为: $NEW_STATUS"
|
||||
else
|
||||
echo "更新Under Attack模式状态失败"
|
||||
echo "响应: $RESPONSE"
|
||||
fi
|
||||
201
LICENSE
Normal file
201
LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
33
Limiting_Shut_down.sh
Normal file
33
Limiting_Shut_down.sh
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 获取总的接收和发送流量
|
||||
output=$(awk 'BEGIN { rx_total = 0; tx_total = 0 }
|
||||
$1 ~ /^(eth|ens|enp|eno)[0-9]+/ { rx_total += $2; tx_total += $10 }
|
||||
END {
|
||||
printf("%.0f Bytes %.0f Bytes", rx_total, tx_total);
|
||||
}' /proc/net/dev)
|
||||
|
||||
|
||||
# 获取接收和发送的流量数据
|
||||
rx=$(echo "$output" | awk '{print $1}')
|
||||
tx=$(echo "$output" | awk '{print $3}')
|
||||
|
||||
# 显示当前流量使用情况
|
||||
echo "当前接收流量: $rx"
|
||||
echo "当前发送流量: $tx"
|
||||
|
||||
threshold_gb=110
|
||||
|
||||
# 将GB转换为字节
|
||||
threshold=$((threshold_gb * 1024 * 1024 * 1024))
|
||||
|
||||
# 检查是否达到流量阈值
|
||||
if (( $rx > $threshold || $tx > $threshold )); then
|
||||
echo "流量达到${threshold},正在关闭服务器..."
|
||||
# 在此处执行关闭服务器的命令,例如:
|
||||
shutdown -h now
|
||||
# 或者
|
||||
# systemctl poweroff
|
||||
else
|
||||
echo "当前流量未达到${threshold},继续监视..."
|
||||
fi
|
||||
48
Limiting_Shut_down1.sh
Normal file
48
Limiting_Shut_down1.sh
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 获取总的接收流量(只统计公网网卡)
|
||||
rx_output=$(awk 'BEGIN { rx_total = 0 }
|
||||
$1 ~ /^(eth|ens|enp|eno)[0-9]+/ { rx_total += $2 }
|
||||
END {
|
||||
printf("%.0f Bytes", rx_total);
|
||||
}' /proc/net/dev)
|
||||
|
||||
# 获取总的发送流量(只统计公网网卡)
|
||||
tx_output=$(awk 'BEGIN { tx_total = 0 }
|
||||
$1 ~ /^(eth|ens|enp|eno)[0-9]+/ { tx_total += $10 }
|
||||
END {
|
||||
printf("%.0f Bytes", tx_total);
|
||||
}' /proc/net/dev)
|
||||
|
||||
# 获取接收流量数据
|
||||
rx=$(echo "$rx_output" | awk '{print $1}')
|
||||
|
||||
# 获取发送流量数据
|
||||
tx=$(echo "$tx_output" | awk '{print $1}')
|
||||
|
||||
# 显示当前流量使用情况
|
||||
echo "当前接收流量: $rx Bytes"
|
||||
echo "当前发送流量: $tx Bytes"
|
||||
|
||||
rx_threshold_gb=110
|
||||
tx_threshold_gb=120
|
||||
|
||||
# 将GB转换为字节
|
||||
rx_threshold=$((rx_threshold_gb * 1024 * 1024 * 1024))
|
||||
tx_threshold=$((tx_threshold_gb * 1024 * 1024 * 1024))
|
||||
|
||||
# 检查是否达到接收流量阈值
|
||||
if (( rx > rx_threshold )); then
|
||||
echo "接收流量达到${rx_threshold_gb}GB (${rx_threshold} Bytes),正在关闭服务器..."
|
||||
shutdown -h now
|
||||
else
|
||||
echo "当前接收流量未达到${rx_threshold_gb}GB (${rx_threshold} Bytes),继续监视..."
|
||||
fi
|
||||
|
||||
# 检查是否达到发送流量阈值
|
||||
if (( tx > tx_threshold )); then
|
||||
echo "发送流量达到${tx_threshold_gb}GB (${tx_threshold} Bytes),正在关闭服务器..."
|
||||
shutdown -h now
|
||||
else
|
||||
echo "当前发送流量未达到${tx_threshold_gb}GB (${tx_threshold} Bytes),继续监视..."
|
||||
fi
|
||||
32
PandoraNext/config.json
Normal file
32
PandoraNext/config.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"bind": "0.0.0.0:8181",
|
||||
"tls": {
|
||||
"enabled": false,
|
||||
"cert_file": "",
|
||||
"key_file": ""
|
||||
},
|
||||
"timeout": 600,
|
||||
"proxy_url": "",
|
||||
"license_id": "github",
|
||||
"public_share": false,
|
||||
"site_password": "",
|
||||
"setup_password": "webgptpasswd",
|
||||
"server_tokens": true,
|
||||
"proxy_api_prefix": "",
|
||||
"isolated_conv_title": "*",
|
||||
"disable_signup": false,
|
||||
"auto_conv_arkose": false,
|
||||
"proxy_file_service": false,
|
||||
"custom_doh_host": "",
|
||||
"captcha": {
|
||||
"provider": "",
|
||||
"site_key": "",
|
||||
"site_secret": "",
|
||||
"site_login": false,
|
||||
"setup_login": false,
|
||||
"oai_username": false,
|
||||
"oai_password": false,
|
||||
"oai_signup": false
|
||||
},
|
||||
"whitelist": null
|
||||
}
|
||||
17
PandoraNext/tokens.json
Normal file
17
PandoraNext/tokens.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"test-1": {
|
||||
"token": "access token / session token / refresh token",
|
||||
"shared": true,
|
||||
"show_user_info": false
|
||||
},
|
||||
"test-2": {
|
||||
"token": "access token / session token / refresh token",
|
||||
"shared": true,
|
||||
"show_user_info": true,
|
||||
"plus": true
|
||||
},
|
||||
"test2": {
|
||||
"token": "access token / session token / refresh token / share token / username & password",
|
||||
"password": "12345"
|
||||
}
|
||||
}
|
||||
10
README.fa.md
Normal file
10
README.fa.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## 📜 معرفی (معرفی اسکریپت)
|
||||
|
||||
ابزار اسکریپت KejiLion یک جعبهابزار همهکاره برای مدیریت، نظارت و تست سیستمهای لینوکس است. این ابزار با هدف ارائه راهحلهای ساده و کارآمد برای کاربران مبتدی و حرفهای طراحی شده است. از جمله ویژگیهای برجسته آن میتوان به مدیریت پیشرفته Docker، استقرار خودکار LNMP (Linux + Nginx + MySQL + PHP)، بهینهسازی امنیتی سایتها، ابزارهای تست شبکه و پشتیبانگیری/بازیابی کامل اشاره کرد. همچنین این اسکریپت با پشتیبانی از نصب پنلها و ابزارهای محبوب، نگهداری سیستم را بسیار آسان میکند.
|
||||
|
||||
هدف ما تبدیل شدن به بهترین ابزار اسکریپت لینوکس با نصب یککلیکی در سراسر اینترنت است تا پشتیبانی فنی سریع، ساده و حرفهای برای کاربران فراهم شود.
|
||||
|
||||
## 🚀 نصب با یک کلیک (One-Click Installation) FA
|
||||
|
||||
```bash
|
||||
bash <(curl -sL kejilion.sh) ir
|
||||
65
README.ja.md
Normal file
65
README.ja.md
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
# ワンクリック スクリプト ツール (kejilion.sh)
|
||||
|
||||
## 📜 はじめに
|
||||
TechLion のシェル スクリプト ツールは、Linux の監視、テスト、管理用に設計されたオールインワンのスクリプト ツールボックスです。初心者でも経験豊富なユーザーでも、このツールは便利なソリューションを提供します。オリジナルの Docker 管理機能を統合しており、コンテナ化されたアプリケーションを簡単に管理できます。 LNMP ウェブサイト構築ソリューションは、ウェブサイトを迅速に構築するのに役立ち、サイトの最適化、防御、バックアップ、復元、移行などの機能が完全に装備されています。また、さまざまなシステム ツール パネルのインストールと使用を統合し、システムのメンテナンスを容易にします。私たちの目標は、ネットワーク全体で最高の Linux ワンクリック スクリプト ツールとなり、ユーザーに効率的で便利な技術サポートを提供することです。
|
||||
|
||||
***
|
||||
|
||||
## 🌐 サポートされているシステム
|
||||
>Ubuntu
|
||||
>Debian
|
||||
>CentOS
|
||||
>Alpine
|
||||
>Kali
|
||||
>Arch
|
||||
>RedHat
|
||||
>Fedora
|
||||
>Alma
|
||||
>Rocky
|
||||
***
|
||||
|
||||
## 🚀 ワンクリックインストール
|
||||
```bash
|
||||
bash <(curl -sL kejilion.sh) jp
|
||||
```
|
||||
|
||||
***
|
||||
## 🖼️ 実際のスクリーンショット
|
||||
|
||||
<img src="https://kejilion.sh/img/screenshots/kejilionsh_jp.webp" alt="日本語版プレビュー" width="75%"/>
|
||||
|
||||
|
||||
***
|
||||
|
||||
## 📦 コア機能
|
||||
|
||||
- **システム情報概要**: CPU、メモリ、ディスク、帯域幅などの動作状態をすばやく表示します。
|
||||
|
||||
- **ネットワーク テスト ツール**: 統合速度テスト、バックホール、遅延、パケット損失検出など。
|
||||
|
||||
- **Docker コンテナ管理**: 独自のコンテナ可視化 + 強化されたコンテナ制御コマンド
|
||||
|
||||
- **LNMPワンクリックデプロイメント**: Nginx + MySQL + PHPサイトを簡単に構築
|
||||
|
||||
- **ウェブサイトの防御と最適化**:CC対策、クローラー対策、ファイアウォールの自動構成、パフォーマンスの最適化
|
||||
|
||||
- **バックアップと移行**: サイトとデータベースのワンクリックバックアップ/復元/リモート移行
|
||||
|
||||
- **BBRアクセラレーション最適化**:カーネルアクセラレーション、ネットワーク輻輳制御インテリジェントスイッチング
|
||||
|
||||
- **アプリケーションマーケット統合**: 主流のツールとパネルが組み込まれており、一般的なサービスのワンクリックインストールをサポートします。
|
||||
|
||||
- **自動更新メカニズム**: スクリプトのバージョンを定期的にチェックして、最新かつ安定した状態を維持します
|
||||
|
||||
***
|
||||
|
||||
## 💖 私たちをサポートしてください
|
||||
脚本は大丈夫だと思います。 USTD TRC20報酬
|
||||
|
||||
<strong style="color: navy;">TCP3PLGUTG9Z4z4tnHHSLbw5bgp8NXhTT3</strong>
|
||||
|
||||
***
|
||||
|
||||
## スターの歴史
|
||||
[](https://star-history.com/#kejilion/sh&Date)
|
||||
68
README.kr.md
Normal file
68
README.kr.md
Normal file
@@ -0,0 +1,68 @@
|
||||
|
||||
# 원클릭 스크립트 도구(kejilion.sh)
|
||||
|
||||
## 📜 소개
|
||||
TechLion의 Shell Script Tool은 Linux 모니터링, 테스트, 관리를 위해 설계된 올인원 스크립팅 툴박스입니다. 초보자든 숙련된 사용자든, 이 도구는 편리한 솔루션을 제공할 수 있습니다. 원래의 Docker 관리 기능을 통합하여 컨테이너화된 애플리케이션을 쉽게 관리할 수 있습니다. LNMP 웹사이트 구축 솔루션은 웹사이트를 빠르게 구축하는 데 도움이 되며, 사이트 최적화, 방어, 백업, 복구 및 마이그레이션 기능을 완벽하게 갖추고 있습니다. 다양한 시스템 도구 패널의 설치와 사용을 통합하여 시스템 유지관리를 더욱 쉽게 만들어줍니다. 저희의 목표는 전체 네트워크 상에서 최고의 Linux 원클릭 스크립팅 도구가 되어 사용자에게 효율적이고 편리한 기술 지원을 제공하는 것입니다.
|
||||
|
||||
***
|
||||
|
||||
## 🌐 지원 시스템
|
||||
>Ubuntu
|
||||
>Debian
|
||||
>CentOS
|
||||
>Alpine
|
||||
>Kali
|
||||
>Arch
|
||||
>RedHat
|
||||
>Fedora
|
||||
>Alma
|
||||
>Rocky
|
||||
***
|
||||
|
||||
|
||||
## 🚀 원클릭 설치
|
||||
```bash
|
||||
bash <(curl -sL kejilion.sh) kr
|
||||
```
|
||||
***
|
||||
|
||||
## 🖼️ 실제 화면 미리보기 (Preview)
|
||||
|
||||
<img src="https://kejilion.sh/img/screenshots/kejilionsh_kr.webp" alt="한국어 버전 미리보기" width="75%"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
***
|
||||
## 📦 핵심 기능
|
||||
|
||||
- **시스템 정보 개요**: CPU, 메모리, 디스크, 대역폭 등의 작동 상태를 빠르게 표시합니다.
|
||||
|
||||
- **네트워크 테스트 도구**: 통합 속도 테스트, 백홀, 지연, 패킷 손실 감지 등
|
||||
|
||||
- **Docker 컨테이너 관리**: 독점적인 컨테이너 시각화 + 향상된 컨테이너 제어 명령
|
||||
|
||||
- **LNMP 원클릭 배포**: Nginx + MySQL + PHP 사이트를 쉽게 구축
|
||||
|
||||
- **웹사이트 방어 및 최적화**: CC 차단, 크롤러 차단, 방화벽 자동 구성 및 성능 최적화
|
||||
|
||||
- **백업 및 마이그레이션**: 사이트 및 데이터베이스의 원클릭 백업/복원/원격 마이그레이션
|
||||
|
||||
- **BBR 가속 최적화**: 커널 가속, 네트워크 혼잡 제어 지능형 스위칭
|
||||
|
||||
- **애플리케이션 마켓 통합**: 기본 제공 도구 및 패널, 공통 서비스의 원클릭 설치 지원
|
||||
|
||||
- **자동 업데이트 메커니즘**: 스크립트 버전을 정기적으로 확인하여 최신 상태로 유지하고 안정성을 유지합니다.
|
||||
|
||||
***
|
||||
|
||||
## 💖 저희를 지원해주세요
|
||||
대본은 괜찮은 것 같아요. USTD TRC20 보상
|
||||
|
||||
<strong style="color: navy;">TCP3PLGUTG9Z4z4tnHHSLbw5bgp8NXhTT3</strong>
|
||||
|
||||
***
|
||||
|
||||
## 스타 역사
|
||||
[](https://star-history.com/#kejilion/sh&Date)
|
||||
154
README.md
Normal file
154
README.md
Normal file
@@ -0,0 +1,154 @@
|
||||
<br><br><br>
|
||||
<div align="center">
|
||||
<img src="https://kejilion.sh/kejilionsh_logo.webp?v=2" alt="logo" width="650">
|
||||
</div>
|
||||
|
||||
<div align="center" style="margin-top:-200px;">
|
||||
<h1 style="font-size:150px;">KEJILION.SH - 科技lion一键脚本工具</h1>
|
||||
</div>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="/README.md">
|
||||
<img src="https://img.shields.io/badge/简体中文-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="简体中文" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="/README.tw.md">
|
||||
<img src="https://img.shields.io/badge/繁體中文-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="繁體中文" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="/README.md">
|
||||
<img src="https://img.shields.io/badge/English-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="English" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="/README.kr.md">
|
||||
<img src="https://img.shields.io/badge/한국어-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="한국어" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="/README.ja.md">
|
||||
<img src="https://img.shields.io/badge/日本語-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="日本語" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="/README.ru.md">
|
||||
<img src="https://img.shields.io/badge/Русский-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="Русский" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="/README.fa.md">
|
||||
<img src="https://img.shields.io/badge/فارسی-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="فارسی" style="margin: 5px;">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<br><br><br>
|
||||
|
||||
|
||||
## 📜 介绍 (Introduction)
|
||||
科技Lion 的 Shell 脚本工具是一款全能脚本工具箱,专为 Linux 监控、测试和管理而设计。无论您是初学者还是经验丰富的用户,该工具都能为您提供便捷的解决方案。集成了独创的 Docker 管理功能,让您轻松管理容器化应用;LNMP建站解决方案能帮助您快速搭建网站,站点优化、防御、备份还原迁移一应俱全;并且整合了各类系统工具面板的安装及使用,使系统维护变得更加简单。我们的目标是成为全网最优秀的 Linux 一键脚本工具,为用户提供高效、便捷的科技支持。
|
||||
|
||||
KejiLion's Shell script tool is an all-in-one script toolbox designed for Linux monitoring, testing, and management. Whether you are a beginner or an experienced user, this tool offers convenient solutions. It integrates unique Docker management features, enabling easy containerized application management. The LNMP site-building solution helps you quickly set up websites, covering optimization, defense, backup, restoration, and migration. It also includes the installation and use of various system tool panels, making system maintenance simpler. Our goal is to become the best Linux one-click script tool on the internet, providing users with efficient and convenient tech support.
|
||||
|
||||
<br><br>
|
||||
|
||||
|
||||
## 🌐 支持系统 (Supported Systems)
|
||||
|
||||
<p align="left">
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Ubuntu-FFB6C1?style=for-the-badge&logo=ubuntu&logoColor=black" alt="Ubuntu" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Debian-AFEEEE?style=for-the-badge&logo=debian&logoColor=black" alt="Debian" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/CentOS-98FB98?style=for-the-badge&logo=centos&logoColor=black" alt="CentOS" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/alpinelinux-ADD8E6?style=for-the-badge&logo=alpinelinux&logoColor=black" alt="alpinelinux" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Kali-D3D3D3?style=for-the-badge&logo=kali-linux&logoColor=black" alt="Kali" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Arch-FFFFE0?style=for-the-badge&logo=archlinux&logoColor=black" alt="Arch" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/RedHat-FFE4E1?style=for-the-badge&logo=redhat&logoColor=black" alt="RedHat" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Fedora-FFD700?style=for-the-badge&logo=fedora&logoColor=black" alt="Fedora" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/almalinux-FFEFD5?style=for-the-badge&logo=almalinux&logoColor=black" alt="almalinux" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Rocky-FFFACD?style=for-the-badge&logo=rocky-linux&logoColor=black" alt="Rocky" style="margin: 5px;">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<br><br>
|
||||
|
||||
> **🔱 Fork 说明**:本仓库 fork 自 [kejilion/sh](https://github.com/kejilion/sh),主要修复 certbot standalone 在部分服务器上因 Docker NAT 端口映射 (`-p 80:80`) 导致 Let's Encrypt HTTP-01 验证超时的问题,改用 `--network host` 直接占用宿主机 80 端口完成验证。其余逻辑与上游保持一致。
|
||||
|
||||
## 🚀 一键安装 (One-Click Installation) CN
|
||||
```bash
|
||||
bash <(curl -sL https://gitea.mygoband.com/openclaw/sh/raw/branch/main/kejilion.sh)
|
||||
```
|
||||
|
||||
## 🚀 一键安装 (One-Click Installation) EN
|
||||
```bash
|
||||
bash <(curl -sL https://gitea.mygoband.com/openclaw/sh/raw/branch/main/kejilion.sh) en
|
||||
```
|
||||
|
||||
<br><br>
|
||||
|
||||
## 🖼️ 效果图预览 (Preview)
|
||||
<p>
|
||||
<img src="https://kejilion.sh/img/screenshots/kejilionsh.webp" alt="中文版" width="48%" style="display:inline-block; margin-right:10px;"/>
|
||||
<img src="https://kejilion.sh/img/screenshots/kejilionsh_en.webp" alt="English Version" width="48%" style="display:inline-block;"/>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br><br>
|
||||
## 📦 核心功能 (Core Features)
|
||||
|
||||
- **系统信息概览**:快速展示 CPU、内存、磁盘、带宽等运行状态
|
||||
*System status overview: CPU, memory, disk, bandwidth and more*<br>
|
||||
|
||||
- **网络测试工具**:集成测速、回程、延迟、丢包检测等
|
||||
*Network tools: speed test, route trace, latency, packet loss test*<br>
|
||||
|
||||
- **Docker 容器管理**:独家容器可视化 + 容器控制增强命令
|
||||
*Advanced Docker management with enhanced commands and visualization*<br>
|
||||
|
||||
- **LNMP 一键部署**:轻松搭建 Nginx + MySQL + PHP 站点
|
||||
*One-click LNMP stack deployment (Nginx, MySQL, PHP)*<br>
|
||||
|
||||
- **网站防御与优化**:防CC、防爬虫,自动配置防火墙与性能优化
|
||||
*Site defense and optimization: anti-CC, anti-crawler, firewall and tuning*<br>
|
||||
|
||||
- **备份与迁移**:站点与数据库一键备份/恢复/远程迁移
|
||||
*Backup & migration: one-click site/database backup and remote restore*<br>
|
||||
|
||||
- **BBR 加速优化**:内核加速、网络拥塞控制智能切换
|
||||
*Network acceleration: BBR/tcp congestion control optimization*<br>
|
||||
|
||||
- **应用市场集成**:内置主流工具与面板,支持一键安装常用服务
|
||||
*App Store integration: built-in panels and tools for one-click deployment*<br>
|
||||
|
||||
- **自动更新机制**:定时检测脚本版本,保持最新最稳定
|
||||
*Auto-update engine: ensure you're always running the latest version*<br>
|
||||
|
||||
|
||||
<br><br>
|
||||
|
||||
## 💖 支持我们 (Support Us)
|
||||
觉得脚本还可以 USTD TRC20 打赏
|
||||
|
||||
Feel free to support us with USTD TRC20 donations.
|
||||
|
||||
<strong style="color: navy;">TCP3PLGUTG9Z4z4tnHHSLbw5bgp8NXhTT3</strong>
|
||||
|
||||
<br><br>
|
||||
|
||||
## ⭐ Star History
|
||||
[](https://star-history.com/#kejilion/sh&Date)
|
||||
127
README.ru.md
Normal file
127
README.ru.md
Normal file
@@ -0,0 +1,127 @@
|
||||
<br><br><br>
|
||||
<div align="center">
|
||||
<img src="https://kejilion.sh/img/screenshots/kejilionsh_logo.webp?v=2" alt="логотип" width="650">
|
||||
</div>
|
||||
|
||||
<div align="center" style="margin-top:-200px;">
|
||||
<h1 style="font-size:150px;">KEJILION.SH - инструмент для создания скриптов Technology Lion с одним ключом</h1>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="/README.md">
|
||||
<img src="https://img.shields.io/badge/简体中文-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="简体中文" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="/README.tw.md">
|
||||
<img src="https://img.shields.io/badge/繁體中文-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="繁體中文" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="/README.md">
|
||||
<img src="https://img.shields.io/badge/English-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="English" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="/README.kr.md">
|
||||
<img src="https://img.shields.io/badge/한국어-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="한국어" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="/README.ja.md">
|
||||
<img src="https://img.shields.io/badge/日本語-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="日本語" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="/README.ru.md">
|
||||
<img src="https://img.shields.io/badge/Русский-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="Русский" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="/README.fa.md">
|
||||
<img src="https://img.shields.io/badge/فارسی-2F4F4F?style=for-the-badge&logo=google-chrome&logoColor=white" alt="فارسی" style="margin: 5px;">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<br><br><br>
|
||||
|
||||
|
||||
## 📜 Введение)
|
||||
Shell Script Tool от TechLion — это комплексный набор инструментов для написания скриптов, предназначенный для мониторинга, тестирования и управления Linux. Независимо от того, являетесь ли вы новичком или опытным пользователем, этот инструмент может предоставить вам удобное решение. Он интегрирует оригинальную функцию управления Docker, позволяя вам легко управлять контейнеризированными приложениями; Решение для создания веб-сайтов LNMP поможет вам быстро создать веб-сайт, оно полностью оснащено функциями оптимизации, защиты, резервного копирования, восстановления и миграции сайта; и интегрирует установку и использование различных панелей системных инструментов для упрощения обслуживания системы. Наша цель — стать лучшим инструментом для создания скриптов Linux в один клик во всей сети и предоставить пользователям эффективную и удобную технологическую поддержку.
|
||||
|
||||
<br><br>
|
||||
|
||||
## 🌐 Система поддержки
|
||||
|
||||
<p align="влево">
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Ubuntu-FFB6C1?style=for-the-badge&logo=ubuntu&logoColor=black" alt="Ubuntu" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Debian-AFEEEE?style=for-the-badge&logo=debian&logoColor=black" alt="Debian" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/CentOS-98FB98?style=for-the-badge&logo=centos&logoColor=black" alt="CentOS" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/alpinelinux-ADD8E6?style=for-the-badge&logo=alpinelinux&logoColor=black" alt="alpinelinux" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Kali-D3D3D3?style=for-the-badge&logo=kali-linux&logoColor=black" alt="Kali" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Arch-FFFFE0?style=for-the-badge&logo=archlinux&logoColor=black" alt="Arch" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/RedHat-FFE4E1?style=for-the-badge&logo=redhat&logoColor=black" alt="RedHat" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Fedora-FFD700?style=for-the-badge&logo=fedora&logoColor=black" alt="Fedora" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/almalinux-FFEFD5?style=for-the-badge&logo=almalinux&logoColor=black" alt="almalinux" style="margin: 5px;">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Rocky-FFFACD?style=for-the-badge&logo=rocky-linux&logoColor=black" alt="Rocky" style="margin: 5px;">
|
||||
</a>
|
||||
</р>
|
||||
|
||||
|
||||
|
||||
<br><br>
|
||||
|
||||
## 🚀 Установка в один клик
|
||||
```bash
|
||||
bash <(curl -sL kejilion.sh) ru
|
||||
```
|
||||
<br><br>
|
||||
|
||||
## 🖼️ Предпросмотр интерфейса
|
||||
<img src="https://kejilion.sh/img/screenshots/kejilionsh_ru.webp" alt="Предпросмотр русской версии" width="75%"/>
|
||||
|
||||
|
||||
|
||||
<br><br>
|
||||
## 📦 Основные характеристики
|
||||
|
||||
- **Обзор информации о системе**: быстрое отображение рабочего состояния ЦП, памяти, диска, пропускной способности и т. д.
|
||||
|
||||
- **Инструменты тестирования сети**: интегрированный тест скорости, транзитная связь, задержка, обнаружение потери пакетов и т. д.
|
||||
|
||||
- **Управление контейнерами Docker**: эксклюзивная визуализация контейнеров + улучшенные команды управления контейнерами
|
||||
|
||||
- **Развертывание LNMP в один клик**: простая сборка сайта Nginx + MySQL + PHP
|
||||
|
||||
- **Защита и оптимизация веб-сайта**: защита от CC, защита от сканеров, автоматическая настройка брандмауэра и оптимизация производительности
|
||||
|
||||
- **Резервное копирование и миграция**: резервное копирование/восстановление/удаленная миграция сайтов и баз данных в один клик
|
||||
|
||||
- **Оптимизация ускорения BBR**: ускорение ядра, интеллектуальное переключение управления перегрузками сети
|
||||
|
||||
- **Интеграция с рынком приложений**: встроенные основные инструменты и панели, поддержка установки распространенных служб одним щелчком мыши
|
||||
|
||||
- **Механизм автоматического обновления**: регулярное определение версий скриптов для поддержания последних и наиболее стабильных версий
|
||||
|
||||
<br><br>
|
||||
|
||||
## 💖 Поддержите нас
|
||||
Я думаю, что сценарий хорош. Награда USTD TRC20
|
||||
<strong style="color: navy;">TCP3PLGUTG9Z4z4tnHHSLbw5bgp8NXhTT3</strong>
|
||||
|
||||
<br><br>
|
||||
|
||||
## ⭐ Звездные тренды
|
||||
[](https://star-history.com/#kejilion/sh&Date)
|
||||
67
README.tw.md
Normal file
67
README.tw.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# 一鍵腳本工具 (kejilion.sh)
|
||||
|
||||
## 📜 介紹 (Introduction)
|
||||
科技Lion 的 Shell 腳本工具是一款全能腳本工具箱,專為 Linux 監控、測試和管理而設計。無論您是初學者還是經驗豐富的用戶,該工具都能為您提供便利的解決方案。整合了獨創的 Docker 管理功能,讓您輕鬆管理容器化應用;LNMP建站解決方案可協助您快速建置網站,網站最佳化、防禦、備份還原遷移一應俱全;並且整合了各類系統工具面板的安裝及使用,使系統維護變得更加簡單。我們的目標是成為全網最優秀的 Linux 一鍵腳本工具,提供使用者高效、便利的科技支援。
|
||||
|
||||
***
|
||||
|
||||
## 🌐 支援系統
|
||||
>Ubuntu
|
||||
>Debian
|
||||
>CentOS
|
||||
>Alpine
|
||||
>Kali
|
||||
>Arch
|
||||
>RedHat
|
||||
>Fedora
|
||||
>Alma
|
||||
>Rocky
|
||||
***
|
||||
|
||||
## 🚀 一鍵安裝
|
||||
```bash
|
||||
bash <(curl -sL kejilion.sh) tw
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## 🖼️ 실제 화면 미리보기 (Preview)
|
||||
|
||||
<img src="https://kejilion.sh/img/screenshots/kejilionsh_tw.webp" alt="한국어 버전 미리보기" width="75%"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
***
|
||||
## 📦 核心功能
|
||||
|
||||
- **系統資訊概覽**:快速展示 CPU、記憶體、磁碟、頻寬等運作狀態
|
||||
|
||||
- **網路測試工具**:整合測速、回程、延遲、丟包偵測等
|
||||
|
||||
- **Docker 容器管理**:獨家容器視覺化 + 容器控制增強指令
|
||||
|
||||
- **LNMP 一鍵部署**:輕鬆建立 Nginx + MySQL + PHP 站點
|
||||
|
||||
- **網站防禦與最佳化**:防CC、防爬蟲,自動設定防火牆與效能最佳化
|
||||
|
||||
- **備份與遷移**:網站與資料庫一鍵備份/還原/遠端遷移
|
||||
|
||||
- **BBR 加速優化**:核心加速、網路擁塞控制智慧切換
|
||||
|
||||
- **應用市場整合**:內建主流工具與面板,支援一鍵安裝常用服務
|
||||
|
||||
- **自動更新機制**:定時偵測腳本版本,保持最新最穩定
|
||||
|
||||
***
|
||||
|
||||
## 💖 支持我們
|
||||
覺得腳本還可以 USTD TRC20 打賞
|
||||
|
||||
<strong style="color: navy;">TCP3PLGUTG9Z4z4tnHHSLbw5bgp8NXhTT3</strong>
|
||||
|
||||
***
|
||||
|
||||
## Star History
|
||||
[](https://star-history.com/#kejilion/sh&Date)
|
||||
29
TG-SSH-check-notify.sh
Normal file
29
TG-SSH-check-notify.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
|
||||
|
||||
# 获取登录信息
|
||||
country=$(curl -s ipinfo.io/$public_ip/country)
|
||||
isp_info=$(curl -s ipinfo.io/org | sed -e 's/\"//g' | awk -F' ' '{print $2}')
|
||||
|
||||
ipv4_address=$(curl -s ipv4.ip.sb)
|
||||
masked_ip=$(echo $ipv4_address | awk -F'.' '{print "*."$3"."$4}')
|
||||
|
||||
|
||||
IP=$(echo $SSH_CONNECTION | awk '{print $1}')
|
||||
TIME=$(date +"%Y年%m月%d日 %H:%M:%S")
|
||||
# 查询IP地址对应的地区信息
|
||||
#LOCATION=$(curl -s https://ipapi.co/$IP/json/ | jq -r '.city')
|
||||
LOCATION=$(curl -s "http://opendata.baidu.com/api.php?query=$IP&co=&resource_id=6006&oe=utf8&format=json" | jq -r '.data[0].location')
|
||||
# 获取当前用户名
|
||||
USERNAME=$(whoami)
|
||||
# 发送Telegram消息
|
||||
MESSAGE="ℹ️ 登录信息:
|
||||
登录机器:${isp_info}-${country}-${masked_ip}
|
||||
登录名:$USERNAME
|
||||
登录IP:$IP
|
||||
登录时间:$TIME
|
||||
登录地区:$LOCATION"
|
||||
|
||||
curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" -d "chat_id=$CHAT_ID&text=$MESSAGE" > /dev/null 2>&1
|
||||
102
TG-check-notify.sh
Normal file
102
TG-check-notify.sh
Normal file
@@ -0,0 +1,102 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 你需要配置Telegram Bot Token和Chat ID
|
||||
TELEGRAM_BOT_TOKEN="输入TG的机器人API"
|
||||
CHAT_ID="输入TG的接收通知的账号ID"
|
||||
|
||||
|
||||
# 你可以修改监控阈值设置
|
||||
CPU_THRESHOLD=70
|
||||
MEMORY_THRESHOLD=70
|
||||
DISK_THRESHOLD=70
|
||||
NETWORK_THRESHOLD_GB=1000
|
||||
|
||||
|
||||
|
||||
# 获取设备信息的变量
|
||||
country=$(curl -s ipinfo.io/$public_ip/country)
|
||||
isp_info=$(curl -s ipinfo.io/org | sed -e 's/\"//g' | awk -F' ' '{print $2}')
|
||||
|
||||
ipv4_address=$(curl -s ipv4.ip.sb)
|
||||
masked_ip=$(echo $ipv4_address | awk -F'.' '{print "*."$3"."$4}')
|
||||
|
||||
# 发送Telegram通知的函数
|
||||
send_tg_notification() {
|
||||
local MESSAGE=$1
|
||||
curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" -d "chat_id=$CHAT_ID" -d "text=$MESSAGE"
|
||||
}
|
||||
|
||||
|
||||
# 获取CPU使用率
|
||||
get_cpu_usage() {
|
||||
awk '{u=$2+$4; t=$2+$4+$5; if (NR==1){u1=u; t1=t;} else printf "%.0f\n", (($2+$4-u1) * 100 / (t-t1))}' \
|
||||
<(grep 'cpu ' /proc/stat) <(sleep 1; grep 'cpu ' /proc/stat)
|
||||
}
|
||||
|
||||
# 获取内存使用率
|
||||
get_memory_usage() {
|
||||
free | awk '/Mem/ {printf("%.0f"), $3/$2 * 100}'
|
||||
}
|
||||
|
||||
# 获取硬盘使用率
|
||||
get_disk_usage() {
|
||||
df / | awk 'NR==2 {print $5}' | sed 's/%//'
|
||||
}
|
||||
|
||||
# 获取总的接收流量(以 GB 为单位)
|
||||
get_rx_bytes() {
|
||||
awk 'BEGIN { rx_total = 0 }
|
||||
# 匹配常见的公网网卡命名: eth*, ens*, enp*, eno*
|
||||
$1 ~ /^(eth|ens|enp|eno)[0-9]+/ { rx_total += $2 }
|
||||
END {
|
||||
printf("%.2f", rx_total / (1024 * 1024 * 1024));
|
||||
}' /proc/net/dev
|
||||
}
|
||||
|
||||
# 获取总的发送流量(以 GB 为单位)
|
||||
get_tx_bytes() {
|
||||
awk 'BEGIN { tx_total = 0 }
|
||||
# 匹配常见的公网网卡命名: eth*, ens*, enp*, eno*
|
||||
$1 ~ /^(eth|ens|enp|eno)[0-9]+/ { tx_total += $10 }
|
||||
END {
|
||||
printf("%.2f", tx_total / (1024 * 1024 * 1024));
|
||||
}' /proc/net/dev
|
||||
}
|
||||
|
||||
# 检查并发送通知
|
||||
check_and_notify() {
|
||||
local USAGE=$1
|
||||
local TYPE=$2
|
||||
local THRESHOLD=$3
|
||||
local CURRENT_VALUE=$4
|
||||
|
||||
if (( $(echo "$USAGE > $THRESHOLD" | bc -l) )); then
|
||||
send_tg_notification "警告: ${isp_info}-${country}-${masked_ip} 的 $TYPE 使用率已达到 $USAGE%,超过阈值 $THRESHOLD%。"
|
||||
fi
|
||||
}
|
||||
|
||||
# 主循环
|
||||
while true; do
|
||||
CPU_USAGE=$(get_cpu_usage)
|
||||
MEMORY_USAGE=$(get_memory_usage)
|
||||
DISK_USAGE=$(get_disk_usage)
|
||||
RX_GB=$(get_rx_bytes)
|
||||
TX_GB=$(get_tx_bytes)
|
||||
|
||||
check_and_notify $CPU_USAGE "CPU" $CPU_THRESHOLD $CPU_USAGE
|
||||
check_and_notify $MEMORY_USAGE "内存" $MEMORY_THRESHOLD $MEMORY_USAGE
|
||||
check_and_notify $DISK_USAGE "硬盘" $DISK_THRESHOLD $DISK_USAGE
|
||||
|
||||
# 检查入站流量是否超过阈值
|
||||
if (( $(echo "$RX_GB > $NETWORK_THRESHOLD_GB" | bc -l) )); then
|
||||
send_tg_notification "警告: ${isp_info}-${country}-${masked_ip} 的入站流量已达到 ${RX_GB}GB,超过阈值 ${NETWORK_THRESHOLD_GB}GB。"
|
||||
fi
|
||||
|
||||
# 检查出站流量是否超过阈值
|
||||
if (( $(echo "$TX_GB > $NETWORK_THRESHOLD_GB" | bc -l) )); then
|
||||
send_tg_notification "警告: ${isp_info}-${country}-${masked_ip} 的出站流量已达到 ${TX_GB}GB,超过阈值 ${NETWORK_THRESHOLD_GB}GB。"
|
||||
fi
|
||||
|
||||
# 休眠5分钟
|
||||
sleep 300
|
||||
done
|
||||
4
apps/README.md
Normal file
4
apps/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# 🚀 开发者应用入驻指南 (kejilion.sh)
|
||||
|
||||
https://github.com/kejilion/apps
|
||||
|
||||
30
archive.key
Normal file
30
archive.key
Normal file
@@ -0,0 +1,30 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v2
|
||||
|
||||
mQENBFhxW04BCAC61HuxBVf1XJiQjXu/DSAtVcnuK38geDoDjcqFtHskFy32NgJG
|
||||
X118EFNym6noF+oibaSftI9yjHthWvMnYZ/+DPwd7YZhbAjBvxMIQCsP6cFVxrgc
|
||||
VV8g+uh4TCfbpalDBFoncRhQCgkmDN9Vd4kIWRh6BHJuzpKB/h2KxUHZVEKgWlK2
|
||||
dR1xUtbrc+kp8gLwPbxTgC3tZ4x2uMMMlnbyCMSRa5oJ/AvoW4W1XphKL9ivsFHM
|
||||
PSQkUBDvgv2RPw+0XBxPy8SYE0r0onx0ZIpjJRTODt3bSV6/0owwlpNogV9bT8HY
|
||||
kl3+w3mTwax6S1akHZuJtLkZS0uUBz1BHt5bABEBAAG0IVhhbk1vZCBLZXJuZWwg
|
||||
PGtlcm5lbEB4YW5tb2Qub3JnPokBNwQTAQgAIQUCWHFbTgIbAwULCQgHAgYVCAkK
|
||||
CwIEFgIDAQIeAQIXgAAKCRCG99Ce5zTmIwTmB/9/S4rmwU6efDgEaBDwBDbOfLBA
|
||||
P2+kDpabjG4K+V4NSvDqlPN49KrI7C21jHghAa2VuTPbSZVQ9ziUd5DjX9OuXov8
|
||||
CYVG+rrlG1UadHS8SBpgw0gNylEvo9/U6u0hl8mrbVOlpzu+eE+e4cMTHax2y580
|
||||
fC2xmnM8wKgyRFEyVc6ilWU+UNTAeUFlg0YfU3cV1Ut4DzVFfamtNYg0p7Q/9MSy
|
||||
VgFpt5C2U5prk4wi++51OgrtaNhMrUhzYXLINWVF6IrXhQ+mkI/FWXUZ0oyVo55v
|
||||
+dQzuds/gos90q+tKyE514pYAmwQSftSjf+RmHOMpPQyMZZKSywrz4vlfveDuQEN
|
||||
BFhxW04BCACs5bXq73MDb2+AsvNL2XkkbnzmE4K3k0gejB9OxrO+puAZn3wWyYIk
|
||||
b0Op8qVUh+/FIiW/uFfmdFD8BypC3YkCNfg6e74f5TT3qQciccpMGy62teo3jfhT
|
||||
T8E1OL1i76ALq7eNbByJKiKLBrTUDM6BDIeRZBWXQMase4+aqUAP47Kd/ByPsmCh
|
||||
/pzb6yPdDPKwkspELssdPXYI7enddjQsCPoBko0j8CTPgKqMTeCuKMXCtD2gtRBN
|
||||
eoVj4cbjZoZvBh8oJktzbYA8FX8eKdxIXhSP9MoVOPSWhxIQdwzkzUPK+0vUV8jA
|
||||
NBTnGOkrRJPOHGPJWFWnTUGrzvcwi7czABEBAAGJAR8EGAEIAAkFAlhxW04CGwwA
|
||||
CgkQhvfQnuc05iMIswgAmzSpCHFGKdkFLdC673FidJcL8adKFTO5Mpyholc5N8vG
|
||||
ROJbpso+DpssF14NKoBfBWqPRgHxYzHakxHiNf0R2+EEwXH3rblzpx3PXzB0OgNe
|
||||
T9T0UStrGgc9nZ8nZVURHZZ2z5zakEWS+rB2TiSxz3YArR3wiTHQW49G09uZvfp6
|
||||
5Mim2w+eUxbQ689eT0DlDI1d2eDP/j5lrv1elsg3kBE2Awzdvi8DdGUpMFrSsYJw
|
||||
WS85uZrwbeAs/nPO62wNIvAbbRsWnDg3AV3vc02eRvy52tTBY1W/67N02M4AxgPd
|
||||
ukDDFZMifwa03yTHD/a57O4dFOnzsEVojBnbzQ7W7w==
|
||||
=HKlF
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
66
auto_cert_renewal-1.sh
Normal file
66
auto_cert_renewal-1.sh
Normal file
@@ -0,0 +1,66 @@
|
||||
# 定义证书存储目录
|
||||
certs_directory="/etc/letsencrypt/live/"
|
||||
|
||||
days_before_expiry=5 # 设置在证书到期前几天触发续签
|
||||
|
||||
# 遍历所有证书文件
|
||||
for cert_dir in $certs_directory*; do
|
||||
# 获取域名
|
||||
domain=$(basename "$cert_dir")
|
||||
|
||||
# 忽略 README 目录
|
||||
if [ "$domain" = "README" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# 输出正在检查的证书信息
|
||||
echo "检查证书过期日期: ${domain}"
|
||||
|
||||
# 获取fullchain.pem文件路径
|
||||
cert_file="${cert_dir}/fullchain.pem"
|
||||
|
||||
# 获取证书过期日期
|
||||
expiration_date=$(openssl x509 -enddate -noout -in "${cert_file}" | cut -d "=" -f 2-)
|
||||
|
||||
# 输出证书过期日期
|
||||
echo "过期日期: ${expiration_date}"
|
||||
|
||||
# 将日期转换为时间戳
|
||||
expiration_timestamp=$(date -d "${expiration_date}" +%s)
|
||||
current_timestamp=$(date +%s)
|
||||
|
||||
# 计算距离过期还有几天
|
||||
days_until_expiry=$(( ($expiration_timestamp - $current_timestamp) / 86400 ))
|
||||
|
||||
# 检查是否需要续签(在满足续签条件的情况下)
|
||||
if [ $days_until_expiry -le $days_before_expiry ]; then
|
||||
echo "证书将在${days_before_expiry}天内过期,正在进行自动续签。"
|
||||
|
||||
# 停止 Nginx
|
||||
docker stop nginx
|
||||
|
||||
iptables -P INPUT ACCEPT
|
||||
iptables -P FORWARD ACCEPT
|
||||
iptables -P OUTPUT ACCEPT
|
||||
iptables -F
|
||||
|
||||
ip6tables -P INPUT ACCEPT
|
||||
ip6tables -P FORWARD ACCEPT
|
||||
ip6tables -P OUTPUT ACCEPT
|
||||
ip6tables -F
|
||||
|
||||
# 续签证书
|
||||
certbot certonly --standalone -d $domain --email your@email.com --agree-tos --no-eff-email --force-renewal
|
||||
|
||||
# 启动 Nginx
|
||||
docker start nginx
|
||||
|
||||
echo "证书已成功续签。"
|
||||
else
|
||||
# 若未满足续签条件,则输出证书仍然有效
|
||||
echo "证书仍然有效,距离过期还有 ${days_until_expiry} 天。"
|
||||
fi
|
||||
|
||||
# 输出分隔线
|
||||
echo "--------------------------"
|
||||
done
|
||||
93
auto_cert_renewal.sh
Normal file
93
auto_cert_renewal.sh
Normal file
@@ -0,0 +1,93 @@
|
||||
# 定义证书存储目录
|
||||
certs_directory="/home/web/certs/"
|
||||
days_before_expiry=15 # 设置在证书到期前几天触发续签
|
||||
|
||||
# 遍历所有证书文件
|
||||
for cert_file in $certs_directory*_cert.pem; do
|
||||
# 获取域名
|
||||
yuming=$(basename "$cert_file" "_cert.pem")
|
||||
|
||||
# 输出正在检查的证书信息
|
||||
echo "检查证书过期日期: ${yuming}"
|
||||
|
||||
# 获取证书过期日期
|
||||
expiration_date=$(openssl x509 -enddate -noout -in "${certs_directory}${yuming}_cert.pem" | cut -d "=" -f 2-)
|
||||
|
||||
# 输出证书过期日期
|
||||
echo "过期日期: ${expiration_date}"
|
||||
|
||||
# 将日期转换为时间戳
|
||||
expiration_timestamp=$(date -d "${expiration_date}" +%s)
|
||||
current_timestamp=$(date +%s)
|
||||
|
||||
# 计算距离过期还有几天
|
||||
days_until_expiry=$(( ($expiration_timestamp - $current_timestamp) / 86400 ))
|
||||
|
||||
if [ $days_until_expiry -le $days_before_expiry ]; then
|
||||
|
||||
echo "证书将在${days_before_expiry}天内过期,正在进行自动续签。"
|
||||
|
||||
# 1. 检查目录是否存在
|
||||
docker exec nginx [ -d /var/www/letsencrypt ] && DIR_OK=true || DIR_OK=false
|
||||
|
||||
# 2. 检查配置文件是否包含关键字
|
||||
# 假设你的配置文件在容器内的 /etc/nginx/conf.d/ 目录下(这是 Nginx 容器的默认路径)
|
||||
docker exec nginx grep -q "letsencrypt" /etc/nginx/conf.d/$yuming.conf && CONF_OK=true || CONF_OK=false
|
||||
|
||||
# 输出结果
|
||||
echo "--- 自动化环境检测报告 ---"
|
||||
if [ "$DIR_OK" = true ]; then echo "✅ 目录检测:/var/www/letsencrypt 存在"; else echo "❌ 目录检测:/var/www/letsencrypt 不存在"; fi
|
||||
if [ "$CONF_OK" = true ]; then echo "✅ 配置检测:$yuming.conf 已包含续签规则"; else echo "❌ 配置检测:$yuming.conf 未发现 letsencrypt 字样"; fi
|
||||
|
||||
if [ "$DIR_OK" = true ] && [ "$CONF_OK" = true ]; then
|
||||
docker run --rm -v /etc/letsencrypt/:/etc/letsencrypt certbot/certbot delete --cert-name "$yuming" -n
|
||||
|
||||
docker run --rm \
|
||||
-v "/etc/letsencrypt:/etc/letsencrypt" \
|
||||
-v "/home/web/letsencrypt:/var/www/letsencrypt" \
|
||||
certbot/certbot certonly \
|
||||
--webroot \
|
||||
-w /var/www/letsencrypt \
|
||||
-d "$yuming" \
|
||||
--email your@email.com \
|
||||
--agree-tos \
|
||||
--no-eff-email \
|
||||
--key-type ecdsa \
|
||||
--force-renewal
|
||||
|
||||
mkdir -p /home/web/certs/
|
||||
cp /etc/letsencrypt/live/$yuming/fullchain.pem /home/web/certs/${yuming}_cert.pem > /dev/null 2>&1
|
||||
cp /etc/letsencrypt/live/$yuming/privkey.pem /home/web/certs/${yuming}_key.pem > /dev/null 2>&1
|
||||
|
||||
openssl rand -out /home/web/certs/ticket12.key 48
|
||||
openssl rand -out /home/web/certs/ticket13.key 80
|
||||
|
||||
docker exec nginx nginx -t && docker exec nginx nginx -s reload
|
||||
|
||||
else
|
||||
docker run --rm -v /etc/letsencrypt/:/etc/letsencrypt certbot/certbot delete --cert-name "$yuming" -n
|
||||
|
||||
docker stop nginx > /dev/null 2>&1
|
||||
|
||||
docker run --rm --network host -v /etc/letsencrypt/:/etc/letsencrypt certbot/certbot certonly --standalone --http-01-address 0.0.0.0 --http-01-port 80 -d $yuming --email your@email.com --agree-tos --no-eff-email --force-renewal --key-type ecdsa
|
||||
|
||||
mkdir -p /home/web/certs/
|
||||
cp /etc/letsencrypt/live/$yuming/fullchain.pem /home/web/certs/${yuming}_cert.pem > /dev/null 2>&1
|
||||
cp /etc/letsencrypt/live/$yuming/privkey.pem /home/web/certs/${yuming}_key.pem > /dev/null 2>&1
|
||||
|
||||
openssl rand -out /home/web/certs/ticket12.key 48
|
||||
openssl rand -out /home/web/certs/ticket13.key 80
|
||||
|
||||
docker start nginx > /dev/null 2>&1
|
||||
|
||||
fi
|
||||
|
||||
echo "证书已成功续签。"
|
||||
else
|
||||
# 若未满足续签条件,则输出证书仍然有效
|
||||
echo "证书仍然有效,距离过期还有 ${days_until_expiry} 天。"
|
||||
fi
|
||||
|
||||
# 输出分隔线
|
||||
echo "--------------------------"
|
||||
done
|
||||
10
beifen.sh
Normal file
10
beifen.sh
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Create a tar archive of the web directory
|
||||
cd /home/ && tar czvf web_$(date +"%Y%m%d%H%M%S").tar.gz web
|
||||
|
||||
# Transfer the tar archive to another VPS
|
||||
cd /home/ && ls -t /home/*.tar.gz | head -1 | xargs -I {} sshpass -p 123456 scp -o StrictHostKeyChecking=no -P 22 {} root@0.0.0.0:/home/
|
||||
|
||||
# Keep only 5 tar archives and delete the rest
|
||||
cd /home/ && ls -t /home/*.tar.gz | tail -n +4 | xargs -I {} rm {}
|
||||
11
check_x86-64_psabi.sh
Normal file
11
check_x86-64_psabi.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/awk -f
|
||||
|
||||
BEGIN {
|
||||
while (!/flags/) if (getline < "/proc/cpuinfo" != 1) exit 1
|
||||
if (/lm/&&/cmov/&&/cx8/&&/fpu/&&/fxsr/&&/mmx/&&/syscall/&&/sse2/) level = 1
|
||||
if (level == 1 && /cx16/&&/lahf/&&/popcnt/&&/sse4_1/&&/sse4_2/&&/ssse3/) level = 2
|
||||
if (level == 2 && /avx/&&/avx2/&&/bmi1/&&/bmi2/&&/f16c/&&/fma/&&/abm/&&/movbe/&&/xsave/) level = 3
|
||||
if (level == 3 && /avx512f/&&/avx512bw/&&/avx512cd/&&/avx512dq/&&/avx512vl/) level = 3
|
||||
if (level > 0) { print "CPU supports x86-64-v" level; exit level + 1 }
|
||||
exit 1
|
||||
}
|
||||
88
cloudflare.conf
Normal file
88
cloudflare.conf
Normal file
@@ -0,0 +1,88 @@
|
||||
#
|
||||
# Author: Mike Rushton
|
||||
#
|
||||
# IMPORTANT
|
||||
#
|
||||
# Please set jail.local's permission to 640 because it contains your CF API key.
|
||||
#
|
||||
# This action depends on curl (and optionally jq).
|
||||
# Referenced from http://www.normyee.net/blog/2012/02/02/adding-cloudflare-support-to-fail2ban by NORM YEE
|
||||
#
|
||||
# To get your CloudFlare API Key: https://www.cloudflare.com/a/account/my-account
|
||||
#
|
||||
# CloudFlare API error codes: https://www.cloudflare.com/docs/host-api.html#s4.2
|
||||
|
||||
[Definition]
|
||||
|
||||
# Option: actionstart
|
||||
# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
|
||||
# Values: CMD
|
||||
#
|
||||
actionstart =
|
||||
|
||||
# Option: actionstop
|
||||
# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
|
||||
# Values: CMD
|
||||
#
|
||||
actionstop =
|
||||
|
||||
# Option: actioncheck
|
||||
# Notes.: command executed once before each actionban command
|
||||
# Values: CMD
|
||||
#
|
||||
actioncheck =
|
||||
|
||||
# Option: actionban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Values: CMD
|
||||
#
|
||||
# API v1
|
||||
#actionban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=ban' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
|
||||
# API v4
|
||||
actionban = curl -s -o /dev/null -X POST <_cf_api_prms> \
|
||||
-d '{"mode":"block","configuration":{"target":"<cftarget>","value":"<ip>"},"notes":"Fail2Ban <name>"}' \
|
||||
<_cf_api_url>
|
||||
|
||||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <time> unix timestamp of the ban time
|
||||
# Values: CMD
|
||||
#
|
||||
# API v1
|
||||
#actionunban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=nul' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
|
||||
# API v4
|
||||
actionunban = id=$(curl -s -X GET <_cf_api_prms> \
|
||||
"<_cf_api_url>?mode=block&configuration_target=<cftarget>&configuration_value=<ip>&page=1&per_page=1¬es=Fail2Ban%%20<name>" \
|
||||
| { jq -r '.result[0].id' 2>/dev/null || tr -d '\n' | sed -nE 's/^.*"result"\s*:\s*\[\s*\{\s*"id"\s*:\s*"([^"]+)".*$/\1/p'; })
|
||||
if [ -z "$id" ]; then echo "<name>: id for <ip> cannot be found"; exit 0; fi;
|
||||
curl -s -o /dev/null -X DELETE <_cf_api_prms> "<_cf_api_url>/$id"
|
||||
|
||||
_cf_api_url = https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules
|
||||
_cf_api_prms = -H 'X-Auth-Email: <cfuser>' -H 'X-Auth-Key: <cftoken>' -H 'Content-Type: application/json'
|
||||
|
||||
[Init]
|
||||
|
||||
# If you like to use this action with mailing whois lines, you could use the composite action
|
||||
# action_cf_mwl predefined in jail.conf, just define in your jail:
|
||||
#
|
||||
# action = %(action_cf_mwl)s
|
||||
# # Your CF account e-mail
|
||||
# cfemail =
|
||||
# # Your CF API Key
|
||||
# cfapikey =
|
||||
|
||||
cftoken = APIKEY00000
|
||||
|
||||
cfuser = kejilion@outlook.com
|
||||
|
||||
cftarget = ip
|
||||
|
||||
[Init?family=inet6]
|
||||
cftarget = ip6
|
||||
22246
cn/kejilion.sh
Normal file
22246
cn/kejilion.sh
Normal file
File diff suppressed because it is too large
Load Diff
1
cn/tests/openclaw/tests_openclaw_memory_auto_setup_smoke.sh
Symbolic link
1
cn/tests/openclaw/tests_openclaw_memory_auto_setup_smoke.sh
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../tests/openclaw/tests_openclaw_memory_auto_setup_smoke.sh
|
||||
214
cn/tests/openclaw/tests_openclaw_memory_menu_smoke.sh
Executable file
214
cn/tests/openclaw/tests_openclaw_memory_menu_smoke.sh
Executable file
@@ -0,0 +1,214 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
SCRIPT="$REPO_ROOT/kejilion.sh"
|
||||
WORKDIR="${TMPDIR:-/tmp}/openclaw-memory-menu-test-$$"
|
||||
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw/workspace/memory"
|
||||
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
|
||||
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
# 抽取 OpenClaw 记忆菜单实现
|
||||
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
break_end() { return 0; }
|
||||
send_stats() { return 0; }
|
||||
EOF_INNER
|
||||
|
||||
awk 'BEGIN{p=0} /openclaw_memory_config_file\(\) \{/{p=1} /openclaw_memory_menu\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
|
||||
awk 'BEGIN{p=0} /openclaw_memory_menu\(\) \{/{p=1} /openclaw_backup_restore_menu\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
|
||||
chmod +x "$WORKDIR/harness.sh"
|
||||
|
||||
# stub: openclaw
|
||||
cat > "$WORKDIR/bin/openclaw" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
cmd="$*"
|
||||
if [[ "$cmd" == "config get"* ]]; then
|
||||
key="$3"
|
||||
case "$key" in
|
||||
memory.backend) echo "qmd" ;;
|
||||
memory.qmd.includeDefaultMemory) echo "true" ;;
|
||||
memory.qmd.command) echo "qmd" ;;
|
||||
agents.defaults.memorySearch.local.modelPath) echo "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf" ;;
|
||||
agents.defaults.memorySearch.provider) echo "local" ;;
|
||||
*) echo "" ;;
|
||||
esac
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "config set"* ]]; then
|
||||
key="$3"
|
||||
val="$4"
|
||||
if [[ "$key" == "memory.qmd.includeDefaultMemory" ]]; then
|
||||
echo "$val" > "${HOME}/.openclaw/includeDefaultMemory"
|
||||
fi
|
||||
if [[ "$key" == "memory.backend" ]]; then
|
||||
echo "$val" > "${HOME}/.openclaw/backend"
|
||||
fi
|
||||
if [[ "$key" == "memory.qmd.command" ]]; then
|
||||
echo "$val" > "${HOME}/.openclaw/qmd_command"
|
||||
fi
|
||||
if [[ "$key" == "agents.defaults.memorySearch.provider" ]]; then
|
||||
echo "$val" > "${HOME}/.openclaw/provider"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "memory status" ]]; then
|
||||
cat <<TXT
|
||||
Provider: qmd (requested: qmd)
|
||||
Vector: ready
|
||||
Indexed: 23/14 files
|
||||
Workspace: ${HOME}/.openclaw/workspace
|
||||
Store: ${OPENCLAW_STORE:-~/.openclaw/workspace/memory/index.sqlite}
|
||||
TXT
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "memory index"* ]]; then
|
||||
echo "$cmd" >> "${HOME}/.openclaw/index_calls"
|
||||
echo "index ok"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "gateway restart" ]]; then
|
||||
echo "$cmd" >> "${HOME}/.openclaw/gateway_calls"
|
||||
echo "gateway restarted"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "gateway stop" ]]; then
|
||||
echo "gateway stopped"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "gateway start" ]]; then
|
||||
echo "gateway started"
|
||||
exit 0
|
||||
fi
|
||||
echo "mock openclaw $*"
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/openclaw"
|
||||
|
||||
# stub: qmd
|
||||
cat > "$WORKDIR/bin/qmd" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
exit 0
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/qmd"
|
||||
|
||||
# stub: curl (用于网络探测)
|
||||
cat > "$WORKDIR/bin/curl" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
if [[ "$*" == *"huggingface.co"* ]]; then
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$*" == *"hf-mirror.com"* ]]; then
|
||||
exit 0
|
||||
fi
|
||||
exit 0
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/curl"
|
||||
|
||||
export HOME="$WORKDIR/home"
|
||||
export PATH="$WORKDIR/bin:$PATH"
|
||||
export TERM=xterm
|
||||
|
||||
# minimal openclaw.json
|
||||
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
|
||||
{
|
||||
"memory": {
|
||||
"backend": "qmd",
|
||||
"qmd": {"command": "qmd", "includeDefaultMemory": true}
|
||||
},
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"memorySearch": {
|
||||
"provider": "local",
|
||||
"local": {
|
||||
"modelPath": "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
|
||||
# mock index sqlite
|
||||
mkdir -p "$HOME/.openclaw/workspace/memory"
|
||||
touch "$HOME/.openclaw/workspace/memory/index.sqlite"
|
||||
|
||||
# mock memory files
|
||||
cat > "$HOME/.openclaw/workspace/MEMORY.md" <<'TXT'
|
||||
# MEMORY
|
||||
line1
|
||||
line2
|
||||
line3
|
||||
TXT
|
||||
cat > "$HOME/.openclaw/workspace/memory/2026-03-11.md" <<'TXT'
|
||||
# 2026-03-11
|
||||
foo
|
||||
bar
|
||||
TXT
|
||||
|
||||
source "$WORKDIR/harness.sh"
|
||||
|
||||
run_menu() {
|
||||
local input="$1"
|
||||
local title="$2"
|
||||
local outfile="$3"
|
||||
echo "[TEST] $title"
|
||||
printf "%b" "$input" | openclaw_memory_menu >"$outfile"
|
||||
}
|
||||
|
||||
# 1) 状态展示(直接返回)
|
||||
run_menu "0\n" "status" "$WORKDIR/out_status.txt"
|
||||
# 2) 更新索引(增量)
|
||||
run_menu "1\nyes\n\n\n0\n" "index" "$WORKDIR/out_index.txt"
|
||||
# 3) 查看记忆文件(列表+查看)
|
||||
run_menu "2\n1\n\n\n\n\n0\n0\n" "files" "$WORKDIR/out_files.txt"
|
||||
# 4) 索引修复(执行修复 + 不立即重建)
|
||||
run_menu "3\ny\n\n\n0\n" "fix" "$WORKDIR/out_fix.txt"
|
||||
# 5) 记忆方案(自动推荐并取消)
|
||||
run_menu "4\n1\n\n0\n0\n" "scheme" "$WORKDIR/out_scheme.txt"
|
||||
|
||||
# 6) Store 缺失时仍执行 rebuild
|
||||
: > "$HOME/.openclaw/index_calls"
|
||||
OPENCLAW_STORE="~/.openclaw/workspace/memory/missing.sqlite" \
|
||||
openclaw_memory_rebuild_index_safe >"$WORKDIR/out_missing.txt"
|
||||
|
||||
export WORKDIR
|
||||
python3 - <<'PY'
|
||||
import glob
|
||||
import os
|
||||
|
||||
flag_path = os.path.expanduser('~/.openclaw/includeDefaultMemory')
|
||||
if not os.path.exists(flag_path):
|
||||
raise SystemExit('includeDefaultMemory not updated')
|
||||
flag = open(flag_path, 'r', encoding='utf-8').read().strip()
|
||||
if flag != 'false':
|
||||
raise SystemExit('includeDefaultMemory not updated')
|
||||
# ensure backup created
|
||||
baks = glob.glob(os.path.expanduser('~/.openclaw/workspace/memory/index.sqlite.bak.*'))
|
||||
if not baks:
|
||||
raise SystemExit('index sqlite backup not created')
|
||||
# ensure missing store still triggers force index
|
||||
calls_path = os.path.expanduser('~/.openclaw/index_calls')
|
||||
if not os.path.exists(calls_path):
|
||||
raise SystemExit('index calls not recorded')
|
||||
with open(calls_path, 'r', encoding='utf-8') as fh:
|
||||
calls = fh.read()
|
||||
if 'memory index --force' not in calls:
|
||||
raise SystemExit('force index not called for missing store')
|
||||
# ensure gateway restart called after rebuild
|
||||
gw_calls_path = os.path.expanduser('~/.openclaw/gateway_calls')
|
||||
if not os.path.exists(gw_calls_path):
|
||||
raise SystemExit('gateway restart not recorded')
|
||||
with open(gw_calls_path, 'r', encoding='utf-8') as fh:
|
||||
gw_calls = fh.read()
|
||||
if 'gateway restart' not in gw_calls:
|
||||
raise SystemExit('gateway restart not called after rebuild')
|
||||
# ensure warning emitted
|
||||
with open(os.path.join(os.environ['WORKDIR'], 'out_missing.txt'), 'r', encoding='utf-8') as fh:
|
||||
out = fh.read()
|
||||
if 'Store 原始值' not in out:
|
||||
raise SystemExit('missing store warning not found')
|
||||
if '索引已重建并自动重启网关' not in out:
|
||||
raise SystemExit('auto restart message not found')
|
||||
print('SMOKE_OK')
|
||||
PY
|
||||
44
custom_mysql_config-1.cnf
Normal file
44
custom_mysql_config-1.cnf
Normal file
@@ -0,0 +1,44 @@
|
||||
[mysqld]
|
||||
|
||||
# 连接和线程管理
|
||||
max_connections = 200 # 小机型避免连接风暴
|
||||
thread_cache_size = 64
|
||||
interactive_timeout = 20
|
||||
wait_timeout = 20
|
||||
|
||||
# InnoDB设置
|
||||
innodb_buffer_pool_size = 512M # 2G RAM 中最重要的分配,留足系统空间
|
||||
innodb_buffer_pool_instances = 1 # 小内存不需要分片
|
||||
innodb_log_buffer_size = 8M
|
||||
innodb_redo_log_capacity = 64M
|
||||
innodb_lock_wait_timeout = 30
|
||||
innodb_file_per_table = 1
|
||||
innodb_flush_log_at_trx_commit = 1
|
||||
innodb_io_capacity = 400 # 普通SSD预期值
|
||||
innodb_io_capacity_max = 800
|
||||
|
||||
# 缓存和表限制
|
||||
table_open_cache = 512
|
||||
open_files_limit = 20000
|
||||
tmp_table_size = 32M
|
||||
max_heap_table_size = 32M
|
||||
max_allowed_packet = 32M
|
||||
|
||||
# 缓冲区大小
|
||||
sort_buffer_size = 2M
|
||||
read_buffer_size = 512K
|
||||
join_buffer_size = 1M
|
||||
|
||||
# 日志管理
|
||||
log_error_verbosity = 3
|
||||
slow_query_log = 1
|
||||
slow_query_log_file = /var/log/mysql/slow.log
|
||||
long_query_time = 1
|
||||
log_queries_not_using_indexes = 1
|
||||
|
||||
# 其他
|
||||
sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
|
||||
performance_schema=ON
|
||||
disable-log-bin
|
||||
|
||||
|
||||
45
custom_mysql_config.cnf
Normal file
45
custom_mysql_config.cnf
Normal file
@@ -0,0 +1,45 @@
|
||||
[mysqld]
|
||||
|
||||
# 连接和线程管理
|
||||
max_connections = 2048
|
||||
thread_cache_size = 512
|
||||
interactive_timeout = 30
|
||||
wait_timeout = 30
|
||||
|
||||
# 查询缓存(MySQL8废除Query Cache,不配置)
|
||||
# no query_cache_size or query_cache_type needed
|
||||
|
||||
# InnoDB设置
|
||||
innodb_buffer_pool_size = 4096M # ⭐如果内存允许,给到一半RAM,数据库读性能暴涨
|
||||
innodb_buffer_pool_instances = 4 # ⭐分片更细(>2G建议4-8分片)
|
||||
innodb_log_buffer_size = 32M # ⭐更大事务写更稳
|
||||
innodb_redo_log_capacity = 128M # ⭐防止大事务日志爆满
|
||||
innodb_lock_wait_timeout = 30
|
||||
innodb_file_per_table = 1
|
||||
innodb_flush_log_at_trx_commit = 1 # ⭐坚持ACID,不放松一致性
|
||||
innodb_io_capacity = 2000 # ⭐如果是NVMe磁盘可以调高(>SSD)
|
||||
innodb_io_capacity_max = 4000
|
||||
|
||||
# 缓存和表限制
|
||||
table_open_cache = 4000 # ⭐动态表多的网站,加大打开表缓存
|
||||
open_files_limit = 65535 # ⭐极限设置(注意宿主机ulimit)
|
||||
tmp_table_size = 64M # ⭐调大,减少临时表落磁盘
|
||||
max_heap_table_size = 64M # ⭐配合tmp_table_size,一起调大
|
||||
max_allowed_packet = 64M # ⭐适配大SQL(比如大BLOB字段)
|
||||
|
||||
# 缓冲区大小
|
||||
sort_buffer_size = 8M # ⭐适配中大型ORDER BY场景
|
||||
read_buffer_size = 2M
|
||||
join_buffer_size = 4M # ⭐稍微放大Join场景效率更高
|
||||
|
||||
# 日志管理
|
||||
log_error_verbosity = 3 # ⭐详细日志,便于追查慢问题
|
||||
slow_query_log = 1 # ⭐打开慢查询日志
|
||||
slow_query_log_file = /var/log/mysql/slow.log
|
||||
long_query_time = 1 # ⭐超过1秒算慢查询
|
||||
log_queries_not_using_indexes = 1 # ⭐记录无索引扫描
|
||||
|
||||
# 其他
|
||||
sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
|
||||
performance_schema=ON # ⭐生产环境推荐打开,监控分析好用
|
||||
disable-log-bin
|
||||
22252
en/kejilion.sh
Normal file
22252
en/kejilion.sh
Normal file
File diff suppressed because it is too large
Load Diff
76
en/to-en.py
Normal file
76
en/to-en.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from deep_translator import GoogleTranslator
|
||||
import re
|
||||
import os
|
||||
|
||||
def is_chinese(text):
|
||||
return bool(re.search(r'[\u4e00-\u9fff]', text))
|
||||
|
||||
def translate_text(text):
|
||||
try:
|
||||
return GoogleTranslator(source='zh-CN', target='en').translate(text)
|
||||
except Exception as e:
|
||||
print(f"\nTranslation error: {e}")
|
||||
return text
|
||||
|
||||
def translate_line_preserving_variables(line):
|
||||
"""
|
||||
Translate only Chinese parts in echo/read/send_stats commands, excluding shell variables
|
||||
"""
|
||||
# Match double or single quoted strings
|
||||
def repl(match):
|
||||
full_string = match.group(0)
|
||||
quote = full_string[0]
|
||||
content = full_string[1:-1]
|
||||
|
||||
# Split by variable expressions
|
||||
parts = re.split(r'(\$\{?\w+\}?)', content)
|
||||
translated_parts = [
|
||||
translate_text(p) if is_chinese(p) else p
|
||||
for p in parts
|
||||
]
|
||||
return quote + ''.join(translated_parts) + quote
|
||||
|
||||
return re.sub(r'(?:\'[^\']*\'|"[^"]*")', repl, line)
|
||||
|
||||
def translate_file(input_file, output_file):
|
||||
total_lines = sum(1 for _ in open(input_file, 'r', encoding='utf-8'))
|
||||
processed_lines = 0
|
||||
|
||||
with open(input_file, 'r', encoding='utf-8') as f_in, \
|
||||
open(output_file, 'w', encoding='utf-8') as f_out:
|
||||
|
||||
for line in f_in:
|
||||
processed_lines += 1
|
||||
progress = processed_lines / total_lines * 100
|
||||
print(f"\rProcessing: {progress:.1f}% ({processed_lines}/{total_lines})", end='')
|
||||
|
||||
leading_space = re.match(r'^(\s*)', line).group(1)
|
||||
stripped = line.strip()
|
||||
|
||||
if stripped.startswith('#') and is_chinese(stripped):
|
||||
comment_mark = '#'
|
||||
comment_text = stripped[1:].strip()
|
||||
if comment_text:
|
||||
translated = translate_text(comment_text)
|
||||
f_out.write(f"{leading_space}{comment_mark} {translated}\n")
|
||||
else:
|
||||
f_out.write(line)
|
||||
|
||||
elif any(cmd in stripped for cmd in ['echo', 'read', 'send_stats']) and is_chinese(stripped):
|
||||
translated_line = translate_line_preserving_variables(line)
|
||||
f_out.write(translated_line)
|
||||
|
||||
else:
|
||||
f_out.write(line)
|
||||
|
||||
print("\nTranslation completed.")
|
||||
print(f"Original file size: {os.path.getsize(input_file)} bytes")
|
||||
print(f"Translated file size: {os.path.getsize(output_file)} bytes")
|
||||
|
||||
if __name__ == "__main__":
|
||||
input_file = 'kejilion.sh'
|
||||
output_file = 'kejilion_en.sh'
|
||||
translate_file(input_file, output_file)
|
||||
9
fail2ban-nginx-cc.conf
Normal file
9
fail2ban-nginx-cc.conf
Normal file
@@ -0,0 +1,9 @@
|
||||
[Definition]
|
||||
# failregex = ^<HOST> .* "(GET|POST|HEAD).*HTTP.*" (404|503) .*$
|
||||
# failregex = ^<HOST> .* "(GET|POST|HEAD).*HTTP.*" (404|503|444) .*
|
||||
# failregex = ^<HOST> .* "(GET|POST|HEAD).*HTTP.*" (403|404|429) .*
|
||||
# failregex = ^<HOST> .* "(GET|POST|HEAD).*HTTP.*" ([45]\d\d) .*
|
||||
# ignoreregex =.*(robots.txt|favicon.ico|jpg|png)
|
||||
failregex = ^<HOST> .* HTTP.* (403|429) .*$
|
||||
ignoreregex = ^.*(\/(?:robots\.txt|favicon\.ico|.*\.(?:jpg|png|gif|jpeg|svg|webp|bmp|tiff|css|js|woff|woff2|eot|ttf|otf))$)
|
||||
|
||||
631
hermes_manager.sh
Executable file
631
hermes_manager.sh
Executable file
@@ -0,0 +1,631 @@
|
||||
#!/bin/bash
|
||||
# Hermes Agent 终端管理脚本 (轻量版)
|
||||
# 设计哲学:极简、直观、调用原生功能
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 确保 hermes 命令可用 (处理环境变量未加载的情况)
|
||||
if ! command -v hermes >/dev/null 2>&1; then
|
||||
if [ -d "$HOME/.hermes/hermes-agent/venv/bin" ]; then
|
||||
export PATH="$HOME/.hermes/hermes-agent/venv/bin:$PATH"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 检查是否安装
|
||||
|
||||
# --- 科技lion 增强版 API 管理核心 ---
|
||||
CONFIG_FILE="$HOME/.hermes/config.yaml"
|
||||
|
||||
config_tool() {
|
||||
python3 - "$CONFIG_FILE" "$@" <<'EOF'
|
||||
import sys, yaml, json, os
|
||||
|
||||
path = sys.argv[1]
|
||||
action = sys.argv[2]
|
||||
|
||||
def load():
|
||||
if not os.path.exists(path):
|
||||
return {}
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
return yaml.safe_load(f) or {}
|
||||
except:
|
||||
return {}
|
||||
|
||||
def save(d):
|
||||
with open(path, 'w', encoding='utf-8') as f:
|
||||
yaml.dump(d, f, sort_keys=False, allow_unicode=True)
|
||||
|
||||
try:
|
||||
data = load()
|
||||
if action == "get_info":
|
||||
m = data.get('model', {})
|
||||
res = {"m": m.get('default', '-'), "p": m.get('provider', '-'), "u": m.get('base_url', '-')}
|
||||
print(json.dumps(res))
|
||||
|
||||
elif action == "list_p":
|
||||
print(json.dumps(data.get('custom_providers', [])))
|
||||
|
||||
elif action == "add_p":
|
||||
n, u, k, m = sys.argv[3:7]
|
||||
ps = data.get('custom_providers', [])
|
||||
if not isinstance(ps, list): ps = []
|
||||
ps = [p for p in ps if p.get('name') != n]
|
||||
ps.append({"name": n, "base_url": u, "api_key": k, "model": m})
|
||||
data['custom_providers'] = ps
|
||||
save(data)
|
||||
|
||||
elif action == "bulk_add":
|
||||
n_base, u, k, models_json = sys.argv[3:7]
|
||||
new_m_ids = json.loads(models_json)
|
||||
ps = data.get('custom_providers', [])
|
||||
if not isinstance(ps, list): ps = []
|
||||
# 移除旧的同前缀条目
|
||||
ps = [p for p in ps if not (isinstance(p, dict) and p.get('name', '').startswith(n_base + "/"))]
|
||||
# 移除可能存在的同名根条目
|
||||
ps = [p for p in ps if p.get('name') != n_base]
|
||||
for m_id in new_m_ids:
|
||||
ps.append({"name": f"{n_base}/{m_id}", "base_url": u, "api_key": k, "model": m_id})
|
||||
data['custom_providers'] = ps
|
||||
save(data)
|
||||
|
||||
elif action == "del_p":
|
||||
n = sys.argv[3]
|
||||
ps = data.get('custom_providers', [])
|
||||
if isinstance(ps, list):
|
||||
data['custom_providers'] = [p for p in ps if p.get('name') != n and not p.get('name', '').startswith(n + "/")]
|
||||
save(data)
|
||||
|
||||
elif action == "list_groups":
|
||||
ps = data.get('custom_providers', [])
|
||||
groups = []
|
||||
seen = set()
|
||||
for p in (ps if isinstance(ps, list) else []):
|
||||
name = p.get('name', '')
|
||||
g = name.split('/')[0] if '/' in name else name
|
||||
if g and g not in seen:
|
||||
seen.add(g)
|
||||
cnt = sum(1 for x in ps if x.get('name', '') == g or x.get('name', '').startswith(g + '/'))
|
||||
groups.append({"name": g, "count": cnt})
|
||||
print(json.dumps(groups))
|
||||
|
||||
elif action == "switch":
|
||||
n, u, k, m = sys.argv[3:7]
|
||||
data['model'] = {"default": m, "provider": "custom", "base_url": u, "api_key": k}
|
||||
save(data)
|
||||
|
||||
except Exception as e:
|
||||
# 确保出错时返回合法的 JSON 避免 Bash 报错
|
||||
print(json.dumps([]))
|
||||
sys.exit(1)
|
||||
EOF
|
||||
}
|
||||
|
||||
install_gum() {
|
||||
if command -v gum >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
echo -e "${YELLOW}正在安装 gum (交互式选择器)...${NC}"
|
||||
if command -v apt >/dev/null 2>&1; then
|
||||
mkdir -p /etc/apt/keyrings
|
||||
curl -fsSL https://repo.charm.sh/apt/gpg.key | gpg --dearmor -o /etc/apt/keyrings/charm.gpg 2>/dev/null
|
||||
echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | tee /etc/apt/sources.list.d/charm.list > /dev/null
|
||||
apt update -qq && apt install -y -qq gum
|
||||
elif command -v dnf >/dev/null 2>&1 || command -v yum >/dev/null 2>&1; then
|
||||
cat > /etc/yum.repos.d/charm.repo <<'REPO'
|
||||
[charm]
|
||||
name=Charm
|
||||
baseurl=https://repo.charm.sh/yum/
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
gpgkey=https://repo.charm.sh/yum/gpg.key
|
||||
REPO
|
||||
rpm --import https://repo.charm.sh/yum/gpg.key
|
||||
if command -v dnf >/dev/null 2>&1; then dnf install -y gum; else yum install -y gum; fi
|
||||
elif command -v zypper >/dev/null 2>&1; then
|
||||
zypper --non-interactive install gum
|
||||
fi
|
||||
}
|
||||
|
||||
hermes_model_probe() {
|
||||
local target_model="$1"
|
||||
local ps_json="$2"
|
||||
local probe_timeout=15
|
||||
local provider_name request_model base_url api_key
|
||||
|
||||
# 从 custom_providers 中查找对应条目
|
||||
provider_name="$target_model"
|
||||
local entry=$(echo "$ps_json" | jq -c --arg n "$provider_name" '.[] | select(.name == $n)' 2>/dev/null)
|
||||
if [ -z "$entry" ]; then
|
||||
HERMES_PROBE_STATUS="ERROR"
|
||||
HERMES_PROBE_MESSAGE="未找到模型配置"
|
||||
HERMES_PROBE_LATENCY="-"
|
||||
HERMES_PROBE_REPLY="-"
|
||||
return 1
|
||||
fi
|
||||
|
||||
base_url=$(echo "$entry" | jq -r .base_url)
|
||||
api_key=$(echo "$entry" | jq -r .api_key)
|
||||
request_model=$(echo "$entry" | jq -r .model)
|
||||
base_url="${base_url%/}"
|
||||
|
||||
local tmp_response
|
||||
tmp_response=$(mktemp)
|
||||
|
||||
# 使用 Python 探测,精确计时
|
||||
local probe_result
|
||||
probe_result=$(python3 - "$base_url" "$api_key" "$request_model" "$tmp_response" "$probe_timeout" <<'PYEOF'
|
||||
import sys, time, json
|
||||
try:
|
||||
import urllib.request, urllib.error
|
||||
except ImportError:
|
||||
print("1|0|0")
|
||||
sys.exit(0)
|
||||
|
||||
base_url, api_key, model, resp_path, timeout = sys.argv[1:6]
|
||||
timeout = int(timeout)
|
||||
url = base_url + "/chat/completions"
|
||||
payload = json.dumps({"model": model, "messages": [{"role": "user", "content": "hi"}], "temperature": 0, "max_tokens": 16}).encode()
|
||||
req = urllib.request.Request(url, data=payload, headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
}, method="POST")
|
||||
|
||||
start = time.time()
|
||||
body = b""
|
||||
status = 0
|
||||
exit_code = 0
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||
status = getattr(resp, "status", 200)
|
||||
body = resp.read()
|
||||
except urllib.error.HTTPError as e:
|
||||
status = getattr(e, "code", 0) or 0
|
||||
body = e.read()
|
||||
exit_code = 22
|
||||
except Exception as e:
|
||||
body = str(e).encode("utf-8", errors="replace")
|
||||
exit_code = 1
|
||||
|
||||
elapsed = int((time.time() - start) * 1000)
|
||||
with open(resp_path, "wb") as f:
|
||||
f.write(body)
|
||||
print(f"{exit_code}|{status}|{elapsed}")
|
||||
PYEOF
|
||||
)
|
||||
|
||||
local p_exit p_http p_latency
|
||||
p_exit=${probe_result%%|*}
|
||||
p_http=${probe_result#*|}
|
||||
p_http=${p_http%%|*}
|
||||
p_latency=${probe_result##*|}
|
||||
|
||||
# 提取回复摘要
|
||||
local reply_preview
|
||||
reply_preview=$(python3 - "$tmp_response" <<'PYEOF'
|
||||
import json, sys
|
||||
from pathlib import Path
|
||||
raw = Path(sys.argv[1]).read_text(encoding="utf-8", errors="replace").strip()
|
||||
reply = ""
|
||||
if raw:
|
||||
try:
|
||||
data = json.loads(raw)
|
||||
if isinstance(data, dict):
|
||||
choices = data.get("choices") or []
|
||||
if choices and isinstance(choices[0], dict):
|
||||
msg = choices[0].get("message") or {}
|
||||
if isinstance(msg, dict):
|
||||
reply = msg.get("content") or ""
|
||||
if not reply:
|
||||
for key in ("error", "message", "detail"):
|
||||
v = data.get(key)
|
||||
if isinstance(v, str) and v.strip():
|
||||
reply = v.strip(); break
|
||||
if isinstance(v, dict):
|
||||
n = v.get("message")
|
||||
if isinstance(n, str) and n.strip():
|
||||
reply = n.strip(); break
|
||||
except:
|
||||
reply = raw
|
||||
reply = " ".join(str(reply).split())[:120]
|
||||
print(reply if reply else "(空返回)")
|
||||
PYEOF
|
||||
)
|
||||
rm -f "$tmp_response"
|
||||
|
||||
if [ "$p_exit" = "0" ] && [ "$p_http" -ge 200 ] 2>/dev/null && [ "$p_http" -lt 300 ] 2>/dev/null; then
|
||||
HERMES_PROBE_STATUS="OK"
|
||||
HERMES_PROBE_MESSAGE="HTTP ${p_http}"
|
||||
HERMES_PROBE_LATENCY="${p_latency}ms"
|
||||
HERMES_PROBE_REPLY="$reply_preview"
|
||||
return 0
|
||||
else
|
||||
HERMES_PROBE_STATUS="FAIL"
|
||||
HERMES_PROBE_MESSAGE="HTTP ${p_http:-0} / exit ${p_exit:-1}"
|
||||
HERMES_PROBE_LATENCY="${p_latency:-?}ms"
|
||||
HERMES_PROBE_REPLY="$reply_preview"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
hermes_probe_status_line() {
|
||||
local status_text="$1"
|
||||
local color_ok='\033[32m' color_fail='\033[31m' reset='\033[0m'
|
||||
if [ "$status_text" = "可用" ]; then
|
||||
printf "%b最小检测结果:%s%b\n" "$color_ok" "$status_text" "$reset"
|
||||
else
|
||||
printf "%b最小检测结果:%s%b\n" "$color_fail" "$status_text" "$reset"
|
||||
fi
|
||||
}
|
||||
|
||||
api_management_submenu() {
|
||||
while true; do
|
||||
clear
|
||||
info=$(config_tool get_info)
|
||||
echo -e "${BLUE}=======================================${NC}"
|
||||
echo -e " ${PURPLE}API & 模型管理 (OpenClaw 风格)${NC}"
|
||||
echo -e "${BLUE}=======================================${NC}"
|
||||
echo -e "${CYAN}当前激活模型:${NC} ${GREEN}$(echo $info | jq -r .m)${NC}"
|
||||
echo -e "---------------------------------------"
|
||||
echo -e "${CYAN}已配置 API 列表:${NC}"
|
||||
ps_list=$(config_tool list_p)
|
||||
if [ "$(echo "$ps_list" | jq '. | length')" -eq 0 ]; then
|
||||
echo -e " ${YELLOW}(暂无配置)${NC}"
|
||||
else
|
||||
echo "$ps_list" | jq -r '.[] | " ● [\(.name)] \(.model) | \(.base_url)"'
|
||||
fi
|
||||
echo -e "---------------------------------------"
|
||||
echo -e "1. ${YELLOW}切换模型 (带测速)${NC}"
|
||||
echo -e "2. 添加 API 供应商 (自动同步)${NC}"
|
||||
echo -e "3. 删除 API 供应商"
|
||||
echo -e "0. 返回主菜单"
|
||||
echo -e "---------------------------------------"
|
||||
read -p "选择序号: " sub_choice
|
||||
case "$sub_choice" in
|
||||
1)
|
||||
local orange="#FF8C00"
|
||||
local ps_json models_list model_count default_model selected_model confirm_switch
|
||||
|
||||
ps_json=$(config_tool list_p)
|
||||
model_count=$(echo "$ps_json" | jq '. | length')
|
||||
|
||||
if [ "$model_count" -eq 0 ] 2>/dev/null || [ -z "$model_count" ]; then
|
||||
echo -e "${RED}无 API 配置! 请先添加供应商。${NC}"
|
||||
sleep 1
|
||||
continue
|
||||
fi
|
||||
|
||||
# 构建带编号的模型列表
|
||||
models_list=$(echo "$ps_json" | jq -r '.[].name' | awk '{print "(" NR ") " $0}')
|
||||
default_model=$(config_tool get_info | jq -r .m)
|
||||
|
||||
while true; do
|
||||
clear
|
||||
install_gum
|
||||
|
||||
# 若 gum 不可用,降级为手动输入
|
||||
if ! command -v gum >/dev/null 2>&1; then
|
||||
echo "--- 模型管理 ---"
|
||||
echo "当前可用模型:"
|
||||
echo "$models_list"
|
||||
echo "当前默认:${default_model}"
|
||||
echo "----------------"
|
||||
read -e -p "请输入模型编号或名称 (输入 0 退出): " selected_model
|
||||
|
||||
if [ "$selected_model" = "0" ]; then
|
||||
break
|
||||
fi
|
||||
if [ -z "$selected_model" ]; then
|
||||
echo "错误:不能为空,请重试。"
|
||||
sleep 1
|
||||
continue
|
||||
fi
|
||||
# 如果输入的是纯数字,从列表中取名称
|
||||
if [[ "$selected_model" =~ ^[0-9]+$ ]]; then
|
||||
selected_model=$(echo "$ps_json" | jq -r --argjson i "$((selected_model-1))" '.[$i].name // empty')
|
||||
if [ -z "$selected_model" ]; then
|
||||
echo "序号无效,请重试。"
|
||||
sleep 1
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# gum 模式 — 完全复刻 openclaw 风格
|
||||
gum style --foreground "$orange" --bold "模型管理"
|
||||
gum style --foreground "$orange" "可用模型:${model_count}"
|
||||
gum style --foreground "$orange" "当前默认:${default_model}"
|
||||
echo ""
|
||||
gum style --faint "↑↓ 选择 / 输入搜索 / Enter 测试 / Esc 退出"
|
||||
echo ""
|
||||
|
||||
selected_model=$(echo "$models_list" | gum filter \
|
||||
--placeholder "搜索模型(如 cli-api/gpt-4o)" \
|
||||
--prompt "选择模型 > " \
|
||||
--indicator "➜ " \
|
||||
--prompt.foreground "$orange" \
|
||||
--indicator.foreground "$orange" \
|
||||
--cursor-text.foreground "$orange" \
|
||||
--match.foreground "$orange" \
|
||||
--header "" \
|
||||
--height 35)
|
||||
|
||||
if [ -z "$selected_model" ] || echo "$selected_model" | head -n 1 | grep -iqE '^(error|usage|gum:)'; then
|
||||
echo "操作已取消,正在退出..."
|
||||
break
|
||||
fi
|
||||
fi
|
||||
|
||||
# 去掉编号前缀 "(N) "
|
||||
selected_model=$(echo "$selected_model" | sed -E 's/^\([0-9]+\)[[:space:]]+//')
|
||||
|
||||
echo ""
|
||||
echo "正在检测模型: $selected_model"
|
||||
if hermes_model_probe "$selected_model" "$ps_json"; then
|
||||
hermes_probe_status_line "可用"
|
||||
else
|
||||
hermes_probe_status_line "不可用"
|
||||
fi
|
||||
echo "状态:$HERMES_PROBE_MESSAGE"
|
||||
echo "延迟:$HERMES_PROBE_LATENCY"
|
||||
echo "摘要:$HERMES_PROBE_REPLY"
|
||||
echo ""
|
||||
|
||||
printf "是否切换到该模型?[y/N,Esc 返回列表]: "
|
||||
IFS= read -rsn1 confirm_switch
|
||||
echo ""
|
||||
if [ "$confirm_switch" = $'\x1b' ]; then
|
||||
confirm_switch="no"
|
||||
else
|
||||
case "$confirm_switch" in
|
||||
[yY])
|
||||
IFS= read -rsn1 -t 5 _enter_key
|
||||
confirm_switch="yes"
|
||||
;;
|
||||
*) confirm_switch="no" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ "$confirm_switch" != "yes" ]; then
|
||||
echo "已返回模型选择列表。"
|
||||
sleep 1
|
||||
continue
|
||||
fi
|
||||
|
||||
# 执行切换
|
||||
local entry_data
|
||||
entry_data=$(echo "$ps_json" | jq -c --arg n "$selected_model" '.[] | select(.name == $n)')
|
||||
local sw_u sw_k sw_m
|
||||
sw_u=$(echo "$entry_data" | jq -r .base_url)
|
||||
sw_k=$(echo "$entry_data" | jq -r .api_key)
|
||||
sw_m=$(echo "$entry_data" | jq -r .model)
|
||||
|
||||
echo "正在切换模型为: $selected_model ..."
|
||||
config_tool switch "$selected_model" "$sw_u" "$sw_k" "$sw_m"
|
||||
|
||||
# 重启 gateway
|
||||
echo -e "${YELLOW}正在重启 Gateway...${NC}"
|
||||
hermes gateway stop >/dev/null 2>&1
|
||||
hermes gateway start >/dev/null 2>&1
|
||||
echo -e "${GREEN}✅ 模型已切换为: $sw_m${NC}"
|
||||
sleep 2
|
||||
break
|
||||
done
|
||||
;;
|
||||
2)
|
||||
echo -e "${CYAN}--- 添加新 API 供应商 ---${NC}"
|
||||
read -p "请输入供应商名称 (如: DeepSeek): " n
|
||||
[ -z "$n" ] && continue
|
||||
read -p "请输入 Base URL (如: https://api.deepseek.com/v1): " u
|
||||
[ -z "$u" ] && continue
|
||||
u="${u%/}"
|
||||
echo -ne "${YELLOW}请输入 API Key (输入隐藏): ${NC}"
|
||||
read -s k
|
||||
echo ""
|
||||
[ -z "$k" ] && continue
|
||||
|
||||
echo -e "${YELLOW}🔍 正在获取完整模型列表...${NC}"
|
||||
m_json=$(curl -s -m 10 -H "Authorization: Bearer $k" "$u/models")
|
||||
# 提取所有 ID
|
||||
m_list_str=$(echo "$m_json" | jq -r '.data[].id' 2>/dev/null | sort)
|
||||
|
||||
if [ -n "$m_list_str" ]; then
|
||||
# 转换为数组
|
||||
m_array=()
|
||||
while read -r line; do m_array+=("$line"); done <<< "$m_list_str"
|
||||
m_count=${#m_array[@]}
|
||||
|
||||
echo -e "${GREEN}✅ 发现 $m_count 个模型。请选择一个作为当前默认:${NC}"
|
||||
PS3="请输入序号: "
|
||||
select m_default in "${m_array[@]}"; do
|
||||
[ -n "$m_default" ] && break
|
||||
done
|
||||
|
||||
echo -e "---------------------------------------"
|
||||
read -p "是否同时添加该供应商的所有 $m_count 个模型?(y/N): " bulk_confirm
|
||||
if [[ "$bulk_confirm" =~ ^[Yy]$ ]]; then
|
||||
# 转换数组为 JSON
|
||||
m_json_list=$(echo "$m_list_str" | jq -R . | jq -s -c .)
|
||||
config_tool bulk_add "$n" "$u" "$k" "$m_json_list"
|
||||
config_tool switch "$n/$m_default" "$u" "$k" "$m_default"
|
||||
echo -e "${GREEN}✅ 已全量导入 $m_count 个模型。${NC}"
|
||||
else
|
||||
config_tool add_p "$n" "$u" "$k" "$m_default"
|
||||
echo -e "${GREEN}✅ 已添加单个模型: $m_default${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}❌ 无法获取列表。${NC}"
|
||||
read -p "请手动输入模型 ID: " m_manual
|
||||
[ -n "$m_manual" ] && config_tool add_p "$n" "$u" "$k" "$m_manual"
|
||||
fi
|
||||
sleep 2
|
||||
;;
|
||||
3)
|
||||
echo -e "${CYAN}已配置的供应商分组:${NC}"
|
||||
groups_json=$(config_tool list_groups)
|
||||
g_count=$(echo "$groups_json" | jq '. | length')
|
||||
if [ "$g_count" -eq 0 ]; then
|
||||
echo -e " ${YELLOW}(暂无配置)${NC}"
|
||||
sleep 1
|
||||
continue
|
||||
fi
|
||||
# 列出供应商分组
|
||||
g_names=()
|
||||
while read -r row; do
|
||||
g_name=$(echo "$row" | jq -r .name)
|
||||
g_cnt=$(echo "$row" | jq -r .count)
|
||||
g_names+=("$g_name")
|
||||
echo -e " ${GREEN}${#g_names[@]}.${NC} $g_name (${g_cnt} 个模型)"
|
||||
done < <(echo "$groups_json" | jq -c '.[]')
|
||||
echo -e " ${GREEN}0.${NC} 取消"
|
||||
read -p "选择要删除的供应商序号: " d_idx
|
||||
if [ "$d_idx" == "0" ] || [ -z "$d_idx" ]; then continue; fi
|
||||
d_name="${g_names[$((d_idx-1))]}"
|
||||
if [ -n "$d_name" ]; then
|
||||
read -p "确认删除 [$d_name] 及其所有模型? (y/N): " del_confirm
|
||||
if [[ "$del_confirm" =~ ^[Yy]$ ]]; then
|
||||
config_tool del_p "$d_name"
|
||||
echo -e "${RED}🗑️ 已删除 $d_name${NC}"
|
||||
sleep 1
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
0) break ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
check_installed() {
|
||||
if command -v hermes >/dev/null 2>&1; then return 0; else return 1; fi
|
||||
}
|
||||
|
||||
# 获取版本号
|
||||
get_version() {
|
||||
if check_installed; then
|
||||
hermes --version | head -n 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 获取网关状态
|
||||
get_gateway_status() {
|
||||
if check_installed; then
|
||||
# 匹配后台 gateway 进程或 systemd 服务
|
||||
if pgrep -f "hermes_cli.main gateway" > /dev/null || pgrep -f "hermes gateway run" > /dev/null || pgrep -f "hermes-gateway" > /dev/null; then
|
||||
echo -e "${GREEN}运行中${NC}"
|
||||
else
|
||||
echo -e "${RED}已停止${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}未安装${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
refresh_hermes_path() {
|
||||
if ! command -v hermes >/dev/null 2>&1; then
|
||||
if [ -d "$HOME/.hermes/hermes-agent/venv/bin" ]; then
|
||||
export PATH="$HOME/.hermes/hermes-agent/venv/bin:$PATH"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# 主菜单UI
|
||||
show_menu() {
|
||||
clear
|
||||
echo -e "${CYAN}=================================================${NC}"
|
||||
echo -e "${YELLOW} Hermes Agent 终端管理工具 ${NC}"
|
||||
echo -e "${CYAN}=================================================${NC}"
|
||||
echo -e " 运行状态 : $(get_gateway_status)"
|
||||
echo -e " 当前版本 : $(get_version)"
|
||||
echo -e "${CYAN}-------------------------------------------------${NC}"
|
||||
echo -e "${GREEN}1.${NC} 安装 Hermes Agent"
|
||||
echo -e "${GREEN}2.${NC} 启动 Gateway (消息网关/后台服务)"
|
||||
echo -e "${GREEN}3.${NC} 停止 Gateway"
|
||||
echo -e "${GREEN}4.${NC} API/模型管理 (提供商与模型切换)"
|
||||
echo -e "${GREEN}5.${NC} 启动终端对话UI (Interactive Chat)"
|
||||
echo -e "${GREEN}6.${NC} 运行初始化配置向导 (Setup Wizard)"
|
||||
echo -e "${GREEN}7.${NC} 检查并更新 Hermes"
|
||||
echo -e "${GREEN}8.${NC} 卸载 Hermes"
|
||||
echo -e "${GREEN}0.${NC} 退出"
|
||||
echo -e "${CYAN}=================================================${NC}"
|
||||
if ! read -p " 请输入数字 [0-8]: " choice; then
|
||||
echo -e "\n${GREEN}退出脚本。${NC}"
|
||||
exit 0
|
||||
fi
|
||||
echo ""
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
echo -e "${YELLOW}开始安装 Hermes Agent...${NC}"
|
||||
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
|
||||
refresh_hermes_path
|
||||
hermes gateway install
|
||||
hermes gateway start
|
||||
;;
|
||||
2)
|
||||
if check_installed; then
|
||||
echo -e "${YELLOW}正在启动 Gateway...${NC}"
|
||||
systemctl --user start hermes-gateway
|
||||
hermes gateway stop
|
||||
hermes gateway start
|
||||
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
|
||||
;;
|
||||
3)
|
||||
if check_installed; then
|
||||
echo -e "${YELLOW}正在停止 Gateway...${NC}"
|
||||
hermes gateway stop
|
||||
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
|
||||
;;
|
||||
4)
|
||||
if check_installed; then
|
||||
echo -e "${YELLOW}进入模型配置向导...${NC}"
|
||||
api_management_submenu
|
||||
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
|
||||
;;
|
||||
5)
|
||||
if check_installed; then
|
||||
echo -e "${YELLOW}即将进入交互式终端,输入 /exit 即可退出返回。${NC}"
|
||||
sleep 1
|
||||
hermes
|
||||
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
|
||||
;;
|
||||
6)
|
||||
if check_installed; then
|
||||
echo -e "${YELLOW}正在启动初始化配置向导...${NC}"
|
||||
hermes setup
|
||||
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
|
||||
;;
|
||||
7)
|
||||
if check_installed; then
|
||||
echo -e "${YELLOW}正在检查更新...${NC}"
|
||||
hermes update
|
||||
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
|
||||
;;
|
||||
8)
|
||||
if check_installed; then
|
||||
read -p "确定要卸载 Hermes 吗?所有数据将被清除。(y/N): " confirm
|
||||
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
||||
hermes uninstall
|
||||
else echo "已取消。"; fi
|
||||
else echo -e "${RED}请先安装 Hermes。${NC}"; fi
|
||||
;;
|
||||
0)
|
||||
echo -e "${GREEN}感谢使用,再见!${NC}"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}输入错误,请重新选择。${NC}"
|
||||
;;
|
||||
esac
|
||||
echo ""
|
||||
read -p "按回车键返回主菜单..."
|
||||
}
|
||||
|
||||
# 主循环
|
||||
while true; do
|
||||
show_menu
|
||||
done
|
||||
12193
ir/kejilion.sh
Normal file
12193
ir/kejilion.sh
Normal file
File diff suppressed because it is too large
Load Diff
76
ir/to-fa.py
Normal file
76
ir/to-fa.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from deep_translator import GoogleTranslator
|
||||
import re
|
||||
import os
|
||||
|
||||
def is_chinese(text):
|
||||
return bool(re.search(r'[\u4e00-\u9fff]', text))
|
||||
|
||||
def translate_text(text):
|
||||
try:
|
||||
return GoogleTranslator(source='zh-CN', target='fa').translate(text)
|
||||
except Exception as e:
|
||||
print(f"\nTranslation error: {e}")
|
||||
return text
|
||||
|
||||
def translate_line_preserving_variables(line):
|
||||
"""
|
||||
Translate only Chinese parts in echo/read/send_stats commands, excluding shell variables
|
||||
"""
|
||||
# Match double or single quoted strings
|
||||
def repl(match):
|
||||
full_string = match.group(0)
|
||||
quote = full_string[0]
|
||||
content = full_string[1:-1]
|
||||
|
||||
# Split by variable expressions
|
||||
parts = re.split(r'(\$\{?\w+\}?)', content)
|
||||
translated_parts = [
|
||||
translate_text(p) if is_chinese(p) else p
|
||||
for p in parts
|
||||
]
|
||||
return quote + ''.join(translated_parts) + quote
|
||||
|
||||
return re.sub(r'(?:\'[^\']*\'|"[^"]*")', repl, line)
|
||||
|
||||
def translate_file(input_file, output_file):
|
||||
total_lines = sum(1 for _ in open(input_file, 'r', encoding='utf-8'))
|
||||
processed_lines = 0
|
||||
|
||||
with open(input_file, 'r', encoding='utf-8') as f_in, \
|
||||
open(output_file, 'w', encoding='utf-8') as f_out:
|
||||
|
||||
for line in f_in:
|
||||
processed_lines += 1
|
||||
progress = processed_lines / total_lines * 100
|
||||
print(f"\rProcessing: {progress:.1f}% ({processed_lines}/{total_lines})", end='')
|
||||
|
||||
leading_space = re.match(r'^(\s*)', line).group(1)
|
||||
stripped = line.strip()
|
||||
|
||||
if stripped.startswith('#') and is_chinese(stripped):
|
||||
comment_mark = '#'
|
||||
comment_text = stripped[1:].strip()
|
||||
if comment_text:
|
||||
translated = translate_text(comment_text)
|
||||
f_out.write(f"{leading_space}{comment_mark} {translated}\n")
|
||||
else:
|
||||
f_out.write(line)
|
||||
|
||||
elif any(cmd in stripped for cmd in ['echo', 'read', 'send_stats']) and is_chinese(stripped):
|
||||
translated_line = translate_line_preserving_variables(line)
|
||||
f_out.write(translated_line)
|
||||
|
||||
else:
|
||||
f_out.write(line)
|
||||
|
||||
print("\nTranslation completed.")
|
||||
print(f"Original file size: {os.path.getsize(input_file)} bytes")
|
||||
print(f"Translated file size: {os.path.getsize(output_file)} bytes")
|
||||
|
||||
if __name__ == "__main__":
|
||||
input_file = 'kejilion.sh'
|
||||
output_file = 'kejilion_fa.sh'
|
||||
translate_file(input_file, output_file)
|
||||
22252
jp/kejilion.sh
Normal file
22252
jp/kejilion.sh
Normal file
File diff suppressed because it is too large
Load Diff
76
jp/to-jp.py
Normal file
76
jp/to-jp.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from deep_translator import GoogleTranslator
|
||||
import re
|
||||
import os
|
||||
|
||||
def is_chinese(text):
|
||||
return bool(re.search(r'[\u4e00-\u9fff]', text))
|
||||
|
||||
def translate_text(text):
|
||||
try:
|
||||
return GoogleTranslator(source='auto', target='ja').translate(text)
|
||||
except Exception as e:
|
||||
print(f"\nTranslation error: {e}")
|
||||
return text
|
||||
|
||||
def translate_line_preserving_variables(line):
|
||||
"""
|
||||
Translate only Chinese parts in echo/read/send_stats commands, excluding shell variables
|
||||
"""
|
||||
# Match double or single quoted strings
|
||||
def repl(match):
|
||||
full_string = match.group(0)
|
||||
quote = full_string[0]
|
||||
content = full_string[1:-1]
|
||||
|
||||
# Split by variable expressions
|
||||
parts = re.split(r'(\$\{?\w+\}?)', content)
|
||||
translated_parts = [
|
||||
translate_text(p) if is_chinese(p) else p
|
||||
for p in parts
|
||||
]
|
||||
return quote + ''.join(translated_parts) + quote
|
||||
|
||||
return re.sub(r'(?:\'[^\']*\'|"[^"]*")', repl, line)
|
||||
|
||||
def translate_file(input_file, output_file):
|
||||
total_lines = sum(1 for _ in open(input_file, 'r', encoding='utf-8'))
|
||||
processed_lines = 0
|
||||
|
||||
with open(input_file, 'r', encoding='utf-8') as f_in, \
|
||||
open(output_file, 'w', encoding='utf-8') as f_out:
|
||||
|
||||
for line in f_in:
|
||||
processed_lines += 1
|
||||
progress = processed_lines / total_lines * 100
|
||||
print(f"\rProcessing: {progress:.1f}% ({processed_lines}/{total_lines})", end='')
|
||||
|
||||
leading_space = re.match(r'^(\s*)', line).group(1)
|
||||
stripped = line.strip()
|
||||
|
||||
if stripped.startswith('#') and is_chinese(stripped):
|
||||
comment_mark = '#'
|
||||
comment_text = stripped[1:].strip()
|
||||
if comment_text:
|
||||
translated = translate_text(comment_text)
|
||||
f_out.write(f"{leading_space}{comment_mark} {translated}\n")
|
||||
else:
|
||||
f_out.write(line)
|
||||
|
||||
elif any(cmd in stripped for cmd in ['echo', 'read', 'send_stats']) and is_chinese(stripped):
|
||||
translated_line = translate_line_preserving_variables(line)
|
||||
f_out.write(translated_line)
|
||||
|
||||
else:
|
||||
f_out.write(line)
|
||||
|
||||
print("\nTranslation completed.")
|
||||
print(f"Original file size: {os.path.getsize(input_file)} bytes")
|
||||
print(f"Translated file size: {os.path.getsize(output_file)} bytes")
|
||||
|
||||
if __name__ == "__main__":
|
||||
input_file = 'kejilion.sh'
|
||||
output_file = 'kejilion_jp.sh'
|
||||
translate_file(input_file, output_file)
|
||||
22252
kejilion.sh
Normal file
22252
kejilion.sh
Normal file
File diff suppressed because it is too large
Load Diff
1514
kejilion_sh_log.txt
Normal file
1514
kejilion_sh_log.txt
Normal file
File diff suppressed because it is too large
Load Diff
22252
kr/kejilion.sh
Normal file
22252
kr/kejilion.sh
Normal file
File diff suppressed because it is too large
Load Diff
76
kr/to-kr.py
Normal file
76
kr/to-kr.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from deep_translator import GoogleTranslator
|
||||
import re
|
||||
import os
|
||||
|
||||
def is_chinese(text):
|
||||
return bool(re.search(r'[\u4e00-\u9fff]', text))
|
||||
|
||||
def translate_text(text):
|
||||
try:
|
||||
return GoogleTranslator(source='zh-CN', target='ko').translate(text)
|
||||
except Exception as e:
|
||||
print(f"\nTranslation error: {e}")
|
||||
return text
|
||||
|
||||
def translate_line_preserving_variables(line):
|
||||
"""
|
||||
Translate only Chinese parts in echo/read/send_stats commands, excluding shell variables
|
||||
"""
|
||||
# Match double or single quoted strings
|
||||
def repl(match):
|
||||
full_string = match.group(0)
|
||||
quote = full_string[0]
|
||||
content = full_string[1:-1]
|
||||
|
||||
# Split by variable expressions
|
||||
parts = re.split(r'(\$\{?\w+\}?)', content)
|
||||
translated_parts = [
|
||||
translate_text(p) if is_chinese(p) else p
|
||||
for p in parts
|
||||
]
|
||||
return quote + ''.join(translated_parts) + quote
|
||||
|
||||
return re.sub(r'(?:\'[^\']*\'|"[^"]*")', repl, line)
|
||||
|
||||
def translate_file(input_file, output_file):
|
||||
total_lines = sum(1 for _ in open(input_file, 'r', encoding='utf-8'))
|
||||
processed_lines = 0
|
||||
|
||||
with open(input_file, 'r', encoding='utf-8') as f_in, \
|
||||
open(output_file, 'w', encoding='utf-8') as f_out:
|
||||
|
||||
for line in f_in:
|
||||
processed_lines += 1
|
||||
progress = processed_lines / total_lines * 100
|
||||
print(f"\rProcessing: {progress:.1f}% ({processed_lines}/{total_lines})", end='')
|
||||
|
||||
leading_space = re.match(r'^(\s*)', line).group(1)
|
||||
stripped = line.strip()
|
||||
|
||||
if stripped.startswith('#') and is_chinese(stripped):
|
||||
comment_mark = '#'
|
||||
comment_text = stripped[1:].strip()
|
||||
if comment_text:
|
||||
translated = translate_text(comment_text)
|
||||
f_out.write(f"{leading_space}{comment_mark} {translated}\n")
|
||||
else:
|
||||
f_out.write(line)
|
||||
|
||||
elif any(cmd in stripped for cmd in ['echo', 'read', 'send_stats']) and is_chinese(stripped):
|
||||
translated_line = translate_line_preserving_variables(line)
|
||||
f_out.write(translated_line)
|
||||
|
||||
else:
|
||||
f_out.write(line)
|
||||
|
||||
print("\nTranslation completed.")
|
||||
print(f"Original file size: {os.path.getsize(input_file)} bytes")
|
||||
print(f"Translated file size: {os.path.getsize(output_file)} bytes")
|
||||
|
||||
if __name__ == "__main__":
|
||||
input_file = 'kejilion.sh'
|
||||
output_file = 'kejilion_kr.sh'
|
||||
translate_file(input_file, output_file)
|
||||
59
ldnmp.sh
Normal file
59
ldnmp.sh
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 获取用户输入,用于替换 docker-compose.yml 文件中的占位符
|
||||
read -p "请输入 数据库ROOT密码:" dbrootpasswd
|
||||
read -p "请输入 数据库用户名:" dbuse
|
||||
read -p "请输入 数据库用户密码:" dbusepasswd
|
||||
|
||||
|
||||
# 更新并安装必要的软件包
|
||||
DEBIAN_FRONTEND=noninteractive apt update -y
|
||||
DEBIAN_FRONTEND=noninteractive apt full-upgrade -y
|
||||
apt install -y curl wget sudo socat unzip tar htop
|
||||
|
||||
# 安装 Docker
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
|
||||
# 安装 Docker Compose
|
||||
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose
|
||||
|
||||
# 创建必要的目录和文件
|
||||
cd /home && mkdir -p web/html web/mysql web/certs web/conf.d web/redis && touch web/docker-compose.yml
|
||||
|
||||
# 下载 docker-compose.yml 文件并进行替换
|
||||
wget -O /home/web/docker-compose.yml https://raw.githubusercontent.com/kejilion/docker/main/LNMP-docker-compose-4.yml
|
||||
|
||||
|
||||
# 在 docker-compose.yml 文件中进行替换
|
||||
sed -i "s/webroot/$dbrootpasswd/g" /home/web/docker-compose.yml
|
||||
sed -i "s/kejilionYYDS/$dbusepasswd/g" /home/web/docker-compose.yml
|
||||
sed -i "s/kejilion/$dbuse/g" /home/web/docker-compose.yml
|
||||
|
||||
iptables -P INPUT ACCEPT
|
||||
iptables -P FORWARD ACCEPT
|
||||
iptables -P OUTPUT ACCEPT
|
||||
iptables -F
|
||||
|
||||
cd /home/web && docker-compose up -d
|
||||
|
||||
docker exec php apt update &&
|
||||
docker exec php apt install -y libmariadb-dev-compat libmariadb-dev libzip-dev libmagickwand-dev imagemagick &&
|
||||
docker exec php docker-php-ext-install mysqli pdo_mysql zip exif gd intl bcmath opcache &&
|
||||
docker exec php pecl install imagick &&
|
||||
docker exec php sh -c 'echo "extension=imagick.so" > /usr/local/etc/php/conf.d/imagick.ini' &&
|
||||
docker exec php pecl install redis &&
|
||||
docker exec php sh -c 'echo "extension=redis.so" > /usr/local/etc/php/conf.d/docker-php-ext-redis.ini' &&
|
||||
docker exec php sh -c 'echo "upload_max_filesize=50M \n post_max_size=50M" > /usr/local/etc/php/conf.d/uploads.ini' &&
|
||||
docker exec php sh -c 'echo "memory_limit=256M" > /usr/local/etc/php/conf.d/memory.ini'
|
||||
|
||||
|
||||
docker exec php74 apt update &&
|
||||
docker exec php74 apt install -y libmariadb-dev-compat libmariadb-dev libzip-dev libmagickwand-dev imagemagick &&
|
||||
docker exec php74 docker-php-ext-install mysqli pdo_mysql zip gd intl bcmath opcache &&
|
||||
docker exec php74 pecl install imagick &&
|
||||
docker exec php74 sh -c 'echo "extension=imagick.so" > /usr/local/etc/php/conf.d/imagick.ini' &&
|
||||
docker exec php74 pecl install redis &&
|
||||
docker exec php74 sh -c 'echo "extension=redis.so" > /usr/local/etc/php/conf.d/docker-php-ext-redis.ini' &&
|
||||
docker exec php74 sh -c 'echo "upload_max_filesize=50M \n post_max_size=50M" > /usr/local/etc/php/conf.d/uploads.ini' &&
|
||||
docker exec php74 sh -c 'echo "memory_limit=256M" > /usr/local/etc/php/conf.d/memory.ini'
|
||||
|
||||
442
mc.sh
Normal file
442
mc.sh
Normal file
@@ -0,0 +1,442 @@
|
||||
#!/bin/bash
|
||||
ln -sf ~/mc.sh /usr/local/bin/mcs
|
||||
|
||||
ip_address() {
|
||||
# 检测 IPv4 地址
|
||||
ipv4_address=$(curl -s --connect-timeout 5 ipv4.ip.sb 2>/dev/null || echo "")
|
||||
# 检测 IPv6 地址
|
||||
ipv6_address=$(curl -s --connect-timeout 5 ipv6.ip.sb 2>/dev/null || echo "")
|
||||
|
||||
# 设置显示变量
|
||||
if [ -n "$ipv4_address" ] && [ -n "$ipv6_address" ]; then
|
||||
ip_display="\033[93m IPv4: $ipv4_address:25565 IPv6: $ipv6_address:25565 \033[0m"
|
||||
elif [ -n "$ipv4_address" ]; then
|
||||
ip_display="\033[93m IPv4: $ipv4_address:25565 \033[0m"
|
||||
elif [ -n "$ipv6_address" ]; then
|
||||
ip_display="\033[93m IPv6: $ipv6_address:25565 \033[0m"
|
||||
else
|
||||
ip_display="\033[93m 无法获取IP地址 \033[0m"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
install() {
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "未提供软件包参数!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
for package in "$@"; do
|
||||
if ! command -v "$package" &>/dev/null; then
|
||||
if command -v apt &>/dev/null; then
|
||||
apt update -y && apt install -y "$package"
|
||||
elif command -v yum &>/dev/null; then
|
||||
yum -y update && yum -y install "$package"
|
||||
elif command -v apk &>/dev/null; then
|
||||
apk update && apk add "$package"
|
||||
else
|
||||
echo "未知的包管理器!"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
remove() {
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "未提供软件包参数!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
for package in "$@"; do
|
||||
if command -v apt &>/dev/null; then
|
||||
apt purge -y "$package"
|
||||
elif command -v yum &>/dev/null; then
|
||||
yum remove -y "$package"
|
||||
elif command -v apk &>/dev/null; then
|
||||
apk del "$package"
|
||||
else
|
||||
echo "未知的包管理器!"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
break_end() {
|
||||
echo -e "\033[0;32m操作完成\033[0m"
|
||||
echo "按任意键继续..."
|
||||
read -n 1 -s -r -p ""
|
||||
echo ""
|
||||
clear
|
||||
}
|
||||
|
||||
mc() {
|
||||
p
|
||||
exit
|
||||
}
|
||||
|
||||
|
||||
install_add_docker() {
|
||||
if [ -f "/etc/alpine-release" ]; then
|
||||
apk update
|
||||
apk add docker docker-compose
|
||||
rc-update add docker default
|
||||
service docker start
|
||||
else
|
||||
curl -fsSL https://get.docker.com | sh && ln -s /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin
|
||||
systemctl start docker
|
||||
systemctl enable docker
|
||||
fi
|
||||
}
|
||||
|
||||
install_docker() {
|
||||
if ! command -v docker &>/dev/null; then
|
||||
install_add_docker
|
||||
else
|
||||
echo "Docker 已经安装"
|
||||
fi
|
||||
}
|
||||
|
||||
mc_start() {
|
||||
ip_address
|
||||
docker start mcserver > /dev/null 2>&1
|
||||
echo -e "\033[0;32mMinecraft服务启动啦!\033[0m"
|
||||
echo -e "\033[0;32m游戏下载地址: https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj\033[0m"
|
||||
echo -e "\033[0;32m进入游戏连接:$ip_display\033[0;32m开始冒险吧!\033[0m"
|
||||
|
||||
}
|
||||
|
||||
mc_backup() {
|
||||
cd ~
|
||||
curl -sS -O https://kejilion.pro/mc_backup.sh && chmod +x mc_backup.sh
|
||||
}
|
||||
|
||||
mc_install_status() {
|
||||
CONTAINER_NAME="mcserver"
|
||||
|
||||
# 检查容器是否已安装
|
||||
if [ "$(docker ps -a -q -f name=$CONTAINER_NAME 2>/dev/null)" ]; then
|
||||
container_status="\e[32mMinecraft服务已安装\e[0m" # 绿色
|
||||
else
|
||||
container_status="\e[90mMinecraft服务未安装\e[0m" # 灰色
|
||||
fi
|
||||
|
||||
ip_address
|
||||
# 检查 Docker 容器是否正在运行
|
||||
if docker ps --format "table {{.Names}}" | grep -q "^$CONTAINER_NAME$"; then
|
||||
tmux_status="\e[32m已开服:$ip_display\e[0m" # 绿色
|
||||
else
|
||||
tmux_status="\e[90m未开服\e[0m" # 灰色
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
while true; do
|
||||
clear
|
||||
mc_install_status
|
||||
echo -e "\033[92m███╗ ███╗██╗███╗ ██╗███████╗ ██████╗██████╗ █████╗ ███████╗████████╗\033[0m"
|
||||
echo -e "\033[92m████╗ ████║██║████╗ ██║██╔════╝██╔════╝██╔══██╗██╔══██╗██╔════╝╚══██╔══╝\033[0m"
|
||||
echo -e "\033[92m██╔████╔██║██║██╔██╗ ██║█████╗ ██║ ██████╔╝███████║█████╗ ██║ \033[0m"
|
||||
echo -e "\033[92m██║╚██╔╝██║██║██║╚██╗██║██╔══╝ ██║ ██╔══██╗██╔══██║██╔══╝ ██║ \033[0m"
|
||||
echo -e "\033[92m██║ ╚═╝ ██║██║██║ ╚████║███████╗╚██████╗██║ ██║██║ ██║██║ ██║ \033[0m"
|
||||
echo -e "\033[92m╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ \033[0m"
|
||||
echo -e "\033[92mMinecraft开服一键脚本工具v1.0.1 by AkarinLiu\033[0m"
|
||||
echo -e "\033[92m-输入\033[92mmcs\033[92m可快速启动此脚本-\033[0m"
|
||||
echo -e "$container_status $tmux_status"
|
||||
echo "------------------------"
|
||||
echo "1. 安装Minecraft服务"
|
||||
echo "2. 开启Minecraft服务"
|
||||
echo "3. 关闭Minecraft服务"
|
||||
echo "4. 重启Minecraft服务"
|
||||
echo "------------------------"
|
||||
echo "5. 查看服务器状态"
|
||||
echo "6. 设置虚拟内存"
|
||||
echo "------------------------"
|
||||
echo "7. 导出游戏存档"
|
||||
echo "8. 导入游戏存档"
|
||||
echo "9. 定时备份游戏存档"
|
||||
echo "------------------------"
|
||||
echo "10. 修改游戏配置"
|
||||
echo "o. 添加管理员权限"
|
||||
echo "p. 删除管理员权限"
|
||||
echo "------------------------"
|
||||
echo "11. 更新Minecraft服务"
|
||||
echo "12. 卸载Minecraft服务"
|
||||
echo "------------------------"
|
||||
echo "k. 科技lion脚本工具箱"
|
||||
echo "------------------------"
|
||||
echo "00. 脚本更新"
|
||||
echo "------------------------"
|
||||
echo "0. 退出脚本"
|
||||
echo "------------------------"
|
||||
read -p "请输入你的选择: " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
clear
|
||||
install_docker
|
||||
docker run -d --name mcserver -p 25565:25565/tcp --restart=always -e EULA=true -e CREATE_CONSOLE_IN_PIPE=true -v mcserver:/data:rw itzg/minecraft-server
|
||||
clear
|
||||
mc_start
|
||||
;;
|
||||
|
||||
2)
|
||||
clear
|
||||
docker start $CONTAINER_NAME > /dev/null 2>&1
|
||||
mc_start
|
||||
;;
|
||||
|
||||
3)
|
||||
clear
|
||||
docker stop $CONTAINER_NAME > /dev/null 2>&1
|
||||
echo -e "\033[0;32mMinecraft服务已关闭\033[0m"
|
||||
;;
|
||||
|
||||
4)
|
||||
clear
|
||||
docker restart $CONTAINER_NAME > /dev/null 2>&1
|
||||
mc_start
|
||||
;;
|
||||
|
||||
5)
|
||||
clear
|
||||
install btop
|
||||
clear
|
||||
btop
|
||||
;;
|
||||
|
||||
6)
|
||||
clear
|
||||
swap_used=$(free -m | awk 'NR==3{print $3}')
|
||||
swap_total=$(free -m | awk 'NR==3{print $2}')
|
||||
|
||||
if [ "$swap_total" -eq 0 ]; then
|
||||
swap_percentage=0
|
||||
else
|
||||
swap_percentage=$((swap_used * 100 / swap_total))
|
||||
fi
|
||||
|
||||
swap_info="${swap_used}MB/${swap_total}MB (${swap_percentage}%)"
|
||||
|
||||
echo "当前虚拟内存: $swap_info"
|
||||
|
||||
read -p "是否调整大小?(Y/N): " choice
|
||||
|
||||
case "$choice" in
|
||||
[Yy])
|
||||
# 输入新的虚拟内存大小
|
||||
read -p "请输入虚拟内存大小MB: " new_swap
|
||||
|
||||
# 获取当前系统中所有的 swap 分区
|
||||
swap_partitions=$(grep -E '^/dev/' /proc/swaps | awk '{print $1}')
|
||||
|
||||
# 遍历并删除所有的 swap 分区
|
||||
for partition in $swap_partitions; do
|
||||
swapoff "$partition"
|
||||
wipefs -a "$partition" # 清除文件系统标识符
|
||||
mkswap -f "$partition"
|
||||
echo "已删除并重新创建 swap 分区: $partition"
|
||||
done
|
||||
|
||||
# 确保 /swapfile 不再被使用
|
||||
swapoff /swapfile
|
||||
|
||||
# 删除旧的 /swapfile
|
||||
rm -f /swapfile
|
||||
|
||||
# 创建新的 swap 分区
|
||||
dd if=/dev/zero of=/swapfile bs=1M count=$new_swap
|
||||
chmod 600 /swapfile
|
||||
mkswap /swapfile
|
||||
swapon /swapfile
|
||||
|
||||
if [ -f /etc/alpine-release ]; then
|
||||
echo "/swapfile swap swap defaults 0 0" >> /etc/fstab
|
||||
echo "nohup swapon /swapfile" >> /etc/local.d/swap.start
|
||||
chmod +x /etc/local.d/swap.start
|
||||
rc-update add local
|
||||
else
|
||||
echo "/swapfile swap swap defaults 0 0" >> /etc/fstab
|
||||
fi
|
||||
|
||||
echo "虚拟内存大小已调整为${new_swap}MB"
|
||||
;;
|
||||
[Nn])
|
||||
echo "已取消"
|
||||
;;
|
||||
*)
|
||||
echo "无效的选择,请输入 Y 或 N。"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
7)
|
||||
clear
|
||||
mkdir -p /home/game
|
||||
docker cp $CONTAINER_NAME:/data/world /home/game/mc/ > /dev/null 2>&1
|
||||
cd /home/game && tar czvf mcsave_$(date +"%Y%m%d%H%M%S").tar.gz mc > /dev/null 2>&1
|
||||
rm -rf /home/game/mc/
|
||||
echo -e "\033[0;32m游戏存档已导出存放在: /home/game/mc/\033[0m"
|
||||
;;
|
||||
8)
|
||||
clear
|
||||
docker stop mcserver > /dev/null 2>&1
|
||||
docker exec -it mcserver bash -c "rm -rf /data/world/*"
|
||||
cd /home/game/ && ls -t /home/game/mc/*.tar.gz | head -1 | xargs -I {} tar -xzf {}
|
||||
docker cp /home/game/mc/world/* mcserver:/data/world
|
||||
rm -rf /home/game/mc/
|
||||
echo -e "\033[0;32m游戏存档已导入\033[0m"
|
||||
docker restart mcserver > /dev/null 2>&1
|
||||
mc_start
|
||||
;;
|
||||
|
||||
9)
|
||||
clear
|
||||
echo "Minecraft游戏存档定时备份"
|
||||
echo "------------------------"
|
||||
echo "1. 每周备份 2. 每天备份 3. 每小时备份"
|
||||
echo "------------------------"
|
||||
read -p "请输入你的选择: " dingshi
|
||||
case $dingshi in
|
||||
1)
|
||||
mc_backup
|
||||
(crontab -l ; echo "0 0 * * 1 ./mc_backup.sh") | crontab - > /dev/null 2>&1
|
||||
echo "每周一备份,已设置"
|
||||
|
||||
;;
|
||||
2)
|
||||
mc_backup
|
||||
(crontab -l ; echo "0 3 * * * ./mc_backup.sh") | crontab - > /dev/null 2>&1
|
||||
echo "每天凌晨3点备份,已设置"
|
||||
|
||||
;;
|
||||
3)
|
||||
mc_backup
|
||||
(crontab -l ; echo "0 * * * * ./mc_backup.sh") | crontab - > /dev/null 2>&1
|
||||
echo "每小时整点备份,已设置"
|
||||
|
||||
;;
|
||||
*)
|
||||
echo "已取消"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
10)
|
||||
clear
|
||||
echo "配置游戏参数"
|
||||
echo "------------------------"
|
||||
read -p "设置游戏难度: (0.和平 1. 简单 2. 普通 3. 困难):" Difficulty
|
||||
case $Difficulty in
|
||||
0)
|
||||
docker exec --user 1000 mcserver mc-send-to-console difficulty peaceful
|
||||
;;
|
||||
1)
|
||||
docker exec --user 1000 mcserver mc-send-to-console difficulty easy
|
||||
;;
|
||||
|
||||
2)
|
||||
docker exec --user 1000 mcserver mc-send-to-console difficulty normal
|
||||
;;
|
||||
3)
|
||||
docker exec --user 1000 mcserver mc-send-to-console difficulty hard
|
||||
;;
|
||||
*)
|
||||
echo "-默认设置为普通难度"
|
||||
docker exec --user 1000 mcserver mc-send-to-console difficulty normal
|
||||
;;
|
||||
esac
|
||||
|
||||
read -p "死亡后掉落设置: (1. 掉落 2. 不掉落):" DeathPenalty
|
||||
case $DeathPenalty in
|
||||
1)
|
||||
docker exec --user 1000 mcserver mc-send-to-console gamerule KeepInventoy false
|
||||
;;
|
||||
|
||||
2)
|
||||
docker exec --user 1000 mcserver mc-send-to-console gamerule KeepInventoy true
|
||||
;;
|
||||
*)
|
||||
docker exec --user 1000 mcserver mc-send-to-console gamerule KeepInventoy false
|
||||
echo "-默认设置为掉落"
|
||||
;;
|
||||
esac
|
||||
|
||||
read -p "设置pvp模式: (1. 开启 2. 关闭):" mc_pvp
|
||||
|
||||
case $mc_pvp in
|
||||
1)
|
||||
docker exec --user 1000 mcserver mc-send-to-console gamerule pvp true
|
||||
;;
|
||||
2)
|
||||
docker exec --user 1000 mcserver mc-send-to-console gamerule pvp false
|
||||
;;
|
||||
*)
|
||||
docker exec --user 1000 mcserver mc-send-to-console gamerule pvp false
|
||||
echo "-默认关闭pvp模式"
|
||||
;;
|
||||
esac
|
||||
|
||||
# 更新配置
|
||||
echo -e "\033[0;32m游戏配置已更改\033[0m"
|
||||
;;
|
||||
|
||||
|
||||
11)
|
||||
clear
|
||||
docker stop mcserver > /dev/null 2>&1
|
||||
docker restart mcserver > /dev/null 2>&1
|
||||
docker exec -it mcserver bash -c "/home/steam/mcserver/mcserver.sh +login anonymous +app_update 2394010 validate +quit"
|
||||
clear
|
||||
echo -e "\033[0;32mMinecraft已更新\033[0m"
|
||||
mc_start
|
||||
;;
|
||||
|
||||
12)
|
||||
clear
|
||||
docker rm -f mcserver
|
||||
docker rmi -f itzg/minecraft-server
|
||||
;;
|
||||
o)
|
||||
read -p "请输入 Minecraft Java 版档案名称:" mc_op
|
||||
docker exec --user 1000 mcserver mc-send-to-console op $mc_op
|
||||
;;
|
||||
p)
|
||||
read -p "请输入 Minecraft Java 版档案名称:" mc_deop
|
||||
docker exec --user 1000 mcserver mc-send-to-console deop $mc_deop
|
||||
;;
|
||||
k)
|
||||
cd ~
|
||||
curl -sS -O https://kejilion.pro/kejilion.sh && chmod +x kejilion.sh && ./kejilion.sh
|
||||
exit
|
||||
;;
|
||||
|
||||
00)
|
||||
cd ~
|
||||
curl -sS -O https://kejilion.pro/mc_log.sh && chmod +x mc_log.sh && ./mc_log.sh
|
||||
rm mc_log.sh
|
||||
echo ""
|
||||
curl -sS -O https://kejilion.pro/mc.sh && chmod +x mc.sh
|
||||
echo "脚本已更新到最新版本!"
|
||||
break_end
|
||||
mc
|
||||
;;
|
||||
|
||||
|
||||
0)
|
||||
clear
|
||||
exit
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "无效的输入!"
|
||||
;;
|
||||
esac
|
||||
break_end
|
||||
done
|
||||
7
mc_backup.sh
Normal file
7
mc_backup.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
clear
|
||||
mkdir -p /home/game
|
||||
docker cp mcserver:/data /home/game/mc
|
||||
cd /home/game/mc && tar czvf mc_$(date +"%Y%m%d%H%M%S").tar.gz mc
|
||||
rm -rf /home/game/mc/
|
||||
echo -e "\033[0;32m游戏存档已导出存放在: /home/game/mc/\033[0m"
|
||||
7
mc_log.sh
Normal file
7
mc_log.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
clear
|
||||
echo "脚本更新日志"
|
||||
echo "------------------------"
|
||||
echo "2025-11-16 v1.0.1"
|
||||
echo "Minecraft 开服脚本魔改自 幻兽帕鲁开服脚本"
|
||||
echo "2025-11-17 v1.0.1"
|
||||
ech "对 Minecraft 的难度等细节进行了适配"
|
||||
608
network-optimize.sh
Normal file
608
network-optimize.sh
Normal file
@@ -0,0 +1,608 @@
|
||||
#!/bin/bash
|
||||
# ============================================================================
|
||||
# 自动网络性能检测与内核优化脚本
|
||||
# 适配:Debian/Ubuntu, CentOS/RHEL/Alma/Rocky, Alpine, Arch, Fedora
|
||||
# 作者:kejilion
|
||||
# ============================================================================
|
||||
|
||||
# ======================== 颜色定义 ========================
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
WHITE='\033[1;37m'
|
||||
NC='\033[0m'
|
||||
|
||||
# ======================== 工具函数 ========================
|
||||
info() { echo -e "${CYAN}[检测]${NC} $*"; }
|
||||
ok() { echo -e "${GREEN}[✓]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||||
fail() { echo -e "${RED}[✗]${NC} $*"; }
|
||||
title() { echo -e "\n${WHITE}━━━ $* ━━━${NC}"; }
|
||||
|
||||
# 获取内核主版本号用于兼容性判断
|
||||
kernel_version() {
|
||||
local ver
|
||||
ver=$(uname -r | grep -oP '^\d+\.\d+')
|
||||
echo "$ver"
|
||||
}
|
||||
|
||||
# 比较版本号: version_ge 5.4 4.9 => true
|
||||
version_ge() {
|
||||
printf '%s\n%s' "$2" "$1" | sort -V -C
|
||||
}
|
||||
|
||||
# 安全读取 sysctl 值
|
||||
sysctl_get() {
|
||||
sysctl -n "$1" 2>/dev/null || echo "N/A"
|
||||
}
|
||||
|
||||
# 获取发行版
|
||||
detect_distro() {
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
case "$ID" in
|
||||
debian|ubuntu|linuxmint|pop|kali) echo "debian" ;;
|
||||
centos|rhel|almalinux|rocky|ol|amzn|fedora) echo "rhel" ;;
|
||||
alpine) echo "alpine" ;;
|
||||
arch|manjaro|endeavouros) echo "arch" ;;
|
||||
opensuse*|sles) echo "suse" ;;
|
||||
*) echo "unknown" ;;
|
||||
esac
|
||||
elif [ -f /etc/debian_version ]; then
|
||||
echo "debian"
|
||||
elif [ -f /etc/redhat-release ]; then
|
||||
echo "rhel"
|
||||
else
|
||||
echo "unknown"
|
||||
fi
|
||||
}
|
||||
|
||||
# ======================== 网络检测 ========================
|
||||
|
||||
# 检测网卡速率(Mbps)
|
||||
detect_link_speed() {
|
||||
local speed=0
|
||||
local iface
|
||||
|
||||
# 获取默认路由网卡
|
||||
iface=$(ip route show default 2>/dev/null | awk '/default/{print $5}' | head -1)
|
||||
[ -z "$iface" ] && iface=$(ls /sys/class/net/ | grep -v lo | head -1)
|
||||
|
||||
if [ -n "$iface" ]; then
|
||||
# 尝试 ethtool
|
||||
if command -v ethtool &>/dev/null; then
|
||||
speed=$(ethtool "$iface" 2>/dev/null | awk '/Speed:/{gsub(/[^0-9]/,"",$2); print $2}')
|
||||
fi
|
||||
|
||||
# 尝试 sysfs
|
||||
if [ -z "$speed" ] || [ "$speed" = "0" ]; then
|
||||
speed=$(cat "/sys/class/net/$iface/speed" 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
|
||||
# VPS 虚拟网卡可能读不到速率,默认 1000
|
||||
[ -z "$speed" ] || [ "$speed" -le 0 ] 2>/dev/null && speed=1000
|
||||
|
||||
echo "$speed"
|
||||
}
|
||||
|
||||
# 检测网络延迟(ms,ping 多个公共 DNS)
|
||||
detect_latency() {
|
||||
local total=0 count=0 avg
|
||||
local targets=("8.8.8.8" "1.1.1.1" "9.9.9.9" "208.67.222.222")
|
||||
|
||||
for t in "${targets[@]}"; do
|
||||
local rtt
|
||||
rtt=$(ping -c 3 -W 2 "$t" 2>/dev/null | awk -F'/' '/avg/{print $5}')
|
||||
if [ -n "$rtt" ]; then
|
||||
total=$(awk "BEGIN{print $total + $rtt}")
|
||||
count=$((count + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$count" -gt 0 ]; then
|
||||
avg=$(awk "BEGIN{printf \"%.1f\", $total / $count}")
|
||||
else
|
||||
avg="50"
|
||||
fi
|
||||
|
||||
echo "$avg"
|
||||
}
|
||||
|
||||
# 检测丢包率(%)
|
||||
detect_packet_loss() {
|
||||
local loss
|
||||
loss=$(ping -c 10 -W 2 8.8.8.8 2>/dev/null | awk -F',' '/loss/{gsub(/%/,"",$3); gsub(/ packet/,"",$3); print $3+0}')
|
||||
[ -z "$loss" ] && loss=0
|
||||
echo "$loss"
|
||||
}
|
||||
|
||||
# 获取当前 TCP 拥塞算法
|
||||
detect_congestion() {
|
||||
sysctl_get net.ipv4.tcp_congestion_control
|
||||
}
|
||||
|
||||
# 检测总物理内存(MB)
|
||||
detect_memory_mb() {
|
||||
awk '/MemTotal/{printf "%d", $2/1024}' /proc/meminfo
|
||||
}
|
||||
|
||||
# ======================== 带宽分级 ========================
|
||||
# 返回: low / medium / high / ultra
|
||||
classify_bandwidth() {
|
||||
local speed=$1
|
||||
if [ "$speed" -lt 100 ]; then
|
||||
echo "low"
|
||||
elif [ "$speed" -lt 1000 ]; then
|
||||
echo "medium"
|
||||
elif [ "$speed" -lt 10000 ]; then
|
||||
echo "high"
|
||||
else
|
||||
echo "ultra"
|
||||
fi
|
||||
}
|
||||
|
||||
# ======================== 核心优化函数 ========================
|
||||
|
||||
auto_optimize_network() {
|
||||
local CONF="/etc/sysctl.d/99-network-optimize.conf"
|
||||
local BACKUP="/etc/sysctl.d/99-network-optimize.conf.bak.$(date +%s)"
|
||||
local KVER
|
||||
KVER=$(kernel_version)
|
||||
local DISTRO
|
||||
DISTRO=$(detect_distro)
|
||||
|
||||
echo -e "${WHITE}"
|
||||
echo "╔══════════════════════════════════════════════════════════════╗"
|
||||
echo "║ 自动网络性能检测与内核优化 ║"
|
||||
echo "╚══════════════════════════════════════════════════════════════╝"
|
||||
echo -e "${NC}"
|
||||
|
||||
# ── 权限检查 ──
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
fail "请以 root 权限运行"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# ── 环境信息 ──
|
||||
title "环境信息"
|
||||
info "发行版: $(. /etc/os-release 2>/dev/null && echo "$PRETTY_NAME" || echo "$DISTRO")"
|
||||
info "内核版本: $(uname -r)"
|
||||
info "架构: $(uname -m)"
|
||||
info "内存: $(detect_memory_mb) MB"
|
||||
|
||||
# ── 网络检测 ──
|
||||
title "网络性能检测"
|
||||
|
||||
local SPEED LATENCY LOSS CONGESTION MEM_MB BW_CLASS
|
||||
|
||||
info "检测网卡速率..."
|
||||
SPEED=$(detect_link_speed)
|
||||
ok "链路速率: ${SPEED} Mbps"
|
||||
|
||||
info "检测网络延迟..."
|
||||
LATENCY=$(detect_latency)
|
||||
ok "平均延迟: ${LATENCY} ms"
|
||||
|
||||
info "检测丢包率..."
|
||||
LOSS=$(detect_packet_loss)
|
||||
ok "丢包率: ${LOSS}%"
|
||||
|
||||
CONGESTION=$(detect_congestion)
|
||||
ok "当前拥塞算法: ${CONGESTION}"
|
||||
|
||||
MEM_MB=$(detect_memory_mb)
|
||||
BW_CLASS=$(classify_bandwidth "$SPEED")
|
||||
|
||||
title "检测结论"
|
||||
case "$BW_CLASS" in
|
||||
low) info "带宽等级: ${YELLOW}低带宽 (<100M)${NC} → 保守优化" ;;
|
||||
medium) info "带宽等级: ${CYAN}中带宽 (100M-1G)${NC} → 标准优化" ;;
|
||||
high) info "带宽等级: ${GREEN}高带宽 (1G-10G)${NC} → 激进优化" ;;
|
||||
ultra) info "带宽等级: ${WHITE}超高带宽 (>10G)${NC} → 极致优化" ;;
|
||||
esac
|
||||
|
||||
local HIGH_LATENCY=false
|
||||
if [ "$(awk "BEGIN{print ($LATENCY > 100)}")" = "1" ]; then
|
||||
HIGH_LATENCY=true
|
||||
warn "高延迟网络 (${LATENCY}ms > 100ms),启用额外优化"
|
||||
fi
|
||||
|
||||
# ── 备份现有配置 ──
|
||||
title "备份与准备"
|
||||
if [ -f "$CONF" ]; then
|
||||
cp "$CONF" "$BACKUP"
|
||||
ok "已备份: $BACKUP"
|
||||
else
|
||||
ok "首次优化,无需备份"
|
||||
fi
|
||||
|
||||
# ── 根据带宽等级设定参数 ──
|
||||
local TCP_RMEM_MAX TCP_WMEM_MAX TCP_RMEM TCP_WMEM
|
||||
local NETDEV_BUDGET NETDEV_BUDGET_USECS
|
||||
local SOMAXCONN NETDEV_BACKLOG
|
||||
local CONNTRACK_MAX
|
||||
|
||||
case "$BW_CLASS" in
|
||||
low)
|
||||
TCP_RMEM="4096 65536 2097152"
|
||||
TCP_WMEM="4096 65536 2097152"
|
||||
TCP_RMEM_MAX=2097152
|
||||
TCP_WMEM_MAX=2097152
|
||||
SOMAXCONN=1024
|
||||
NETDEV_BACKLOG=1000
|
||||
NETDEV_BUDGET=300
|
||||
NETDEV_BUDGET_USECS=2000
|
||||
CONNTRACK_MAX=65536
|
||||
;;
|
||||
medium)
|
||||
TCP_RMEM="4096 131072 16777216"
|
||||
TCP_WMEM="4096 131072 16777216"
|
||||
TCP_RMEM_MAX=16777216
|
||||
TCP_WMEM_MAX=16777216
|
||||
SOMAXCONN=4096
|
||||
NETDEV_BACKLOG=5000
|
||||
NETDEV_BUDGET=600
|
||||
NETDEV_BUDGET_USECS=4000
|
||||
CONNTRACK_MAX=262144
|
||||
;;
|
||||
high)
|
||||
TCP_RMEM="4096 262144 67108864"
|
||||
TCP_WMEM="4096 262144 67108864"
|
||||
TCP_RMEM_MAX=67108864
|
||||
TCP_WMEM_MAX=67108864
|
||||
SOMAXCONN=8192
|
||||
NETDEV_BACKLOG=10000
|
||||
NETDEV_BUDGET=1200
|
||||
NETDEV_BUDGET_USECS=6000
|
||||
CONNTRACK_MAX=524288
|
||||
;;
|
||||
ultra)
|
||||
TCP_RMEM="4096 524288 134217728"
|
||||
TCP_WMEM="4096 524288 134217728"
|
||||
TCP_RMEM_MAX=134217728
|
||||
TCP_WMEM_MAX=134217728
|
||||
SOMAXCONN=16384
|
||||
NETDEV_BACKLOG=20000
|
||||
NETDEV_BUDGET=2400
|
||||
NETDEV_BUDGET_USECS=8000
|
||||
CONNTRACK_MAX=1048576
|
||||
;;
|
||||
esac
|
||||
|
||||
# 高延迟追加:加大初始窗口
|
||||
local HIGH_LAT_EXTRA=""
|
||||
if $HIGH_LATENCY; then
|
||||
# 高延迟网络使用更大的初始拥塞窗口提升吞吐
|
||||
HIGH_LAT_EXTRA="
|
||||
# ── 高延迟网络额外优化 ──
|
||||
net.ipv4.tcp_slow_start_after_idle = 0
|
||||
net.ipv4.tcp_notsent_lowat = 16384"
|
||||
fi
|
||||
|
||||
# 内存不足时缩小缓冲区
|
||||
if [ "$MEM_MB" -lt 512 ]; then
|
||||
warn "内存不足 512MB,缩小 TCP 缓冲区"
|
||||
TCP_RMEM="4096 32768 1048576"
|
||||
TCP_WMEM="4096 32768 1048576"
|
||||
TCP_RMEM_MAX=1048576
|
||||
TCP_WMEM_MAX=1048576
|
||||
CONNTRACK_MAX=32768
|
||||
fi
|
||||
|
||||
# ── 根据内存大小设定 VM 参数 ──
|
||||
local SWAPPINESS MIN_FREE_KB
|
||||
if [ "$MEM_MB" -ge 16384 ]; then
|
||||
# 16G+ 大内存:激进
|
||||
SWAPPINESS=5
|
||||
MIN_FREE_KB=131072
|
||||
elif [ "$MEM_MB" -ge 4096 ]; then
|
||||
# 4G-16G:标准
|
||||
SWAPPINESS=10
|
||||
MIN_FREE_KB=65536
|
||||
elif [ "$MEM_MB" -ge 1024 ]; then
|
||||
# 1G-4G:保守
|
||||
SWAPPINESS=20
|
||||
MIN_FREE_KB=32768
|
||||
else
|
||||
# <1G:极度保守
|
||||
SWAPPINESS=30
|
||||
MIN_FREE_KB=16384
|
||||
fi
|
||||
|
||||
# ── BBR 检测与加载 ──
|
||||
title "TCP 拥塞算法优化"
|
||||
local USE_BBR=false
|
||||
local TARGET_CC="cubic"
|
||||
|
||||
if version_ge "$KVER" "4.9"; then
|
||||
# 内核 >= 4.9 支持 BBR
|
||||
if ! lsmod 2>/dev/null | grep -q tcp_bbr; then
|
||||
modprobe tcp_bbr 2>/dev/null
|
||||
if lsmod 2>/dev/null | grep -q tcp_bbr; then
|
||||
ok "BBR 模块已加载"
|
||||
else
|
||||
warn "BBR 模块加载失败,使用 cubic"
|
||||
fi
|
||||
fi
|
||||
|
||||
if sysctl net.ipv4.tcp_available_congestion_control 2>/dev/null | grep -q bbr; then
|
||||
USE_BBR=true
|
||||
TARGET_CC="bbr"
|
||||
ok "拥塞算法: cubic → ${GREEN}BBR${NC}"
|
||||
fi
|
||||
else
|
||||
warn "内核 $KVER < 4.9,不支持 BBR,保持 cubic"
|
||||
fi
|
||||
|
||||
# BBR 配合 fq 队列调度
|
||||
local QDISC="fq"
|
||||
if ! $USE_BBR; then
|
||||
QDISC="fq_codel"
|
||||
fi
|
||||
|
||||
# ── 生成优化配置 ──
|
||||
title "写入优化配置"
|
||||
|
||||
cat > "$CONF" << SYSCTL
|
||||
# ============================================================================
|
||||
# 自动网络优化配置
|
||||
# 生成时间: $(date '+%Y-%m-%d %H:%M:%S')
|
||||
# 带宽等级: $BW_CLASS (${SPEED}Mbps)
|
||||
# 平均延迟: ${LATENCY}ms | 丢包率: ${LOSS}%
|
||||
# 内核: $(uname -r) | 发行版: $DISTRO | 内存: ${MEM_MB}MB
|
||||
# ============================================================================
|
||||
|
||||
# ── TCP 拥塞控制 ──
|
||||
net.core.default_qdisc = $QDISC
|
||||
net.ipv4.tcp_congestion_control = $TARGET_CC
|
||||
|
||||
# ── TCP 缓冲区 ──
|
||||
net.core.rmem_max = $TCP_RMEM_MAX
|
||||
net.core.wmem_max = $TCP_WMEM_MAX
|
||||
net.core.rmem_default = $(echo "$TCP_RMEM" | awk '{print $2}')
|
||||
net.core.wmem_default = $(echo "$TCP_WMEM" | awk '{print $2}')
|
||||
net.ipv4.tcp_rmem = $TCP_RMEM
|
||||
net.ipv4.tcp_wmem = $TCP_WMEM
|
||||
net.ipv4.udp_rmem_min = 8192
|
||||
net.ipv4.udp_wmem_min = 8192
|
||||
|
||||
# ── 连接队列 ──
|
||||
net.core.somaxconn = $SOMAXCONN
|
||||
net.core.netdev_max_backlog = $NETDEV_BACKLOG
|
||||
net.core.netdev_budget = $NETDEV_BUDGET
|
||||
$(sysctl -n net.core.netdev_budget_usecs &>/dev/null && echo "net.core.netdev_budget_usecs = $NETDEV_BUDGET_USECS" || echo "# netdev_budget_usecs 不支持,跳过")
|
||||
|
||||
# ── TCP 连接优化 ──
|
||||
net.ipv4.tcp_fastopen = 3
|
||||
net.ipv4.tcp_tw_reuse = 1
|
||||
net.ipv4.tcp_fin_timeout = 15
|
||||
net.ipv4.tcp_keepalive_time = 300
|
||||
net.ipv4.tcp_keepalive_intvl = 30
|
||||
net.ipv4.tcp_keepalive_probes = 5
|
||||
net.ipv4.tcp_max_syn_backlog = $SOMAXCONN
|
||||
net.ipv4.tcp_max_tw_buckets = 65536
|
||||
net.ipv4.tcp_syncookies = 1
|
||||
net.ipv4.tcp_synack_retries = 2
|
||||
net.ipv4.tcp_syn_retries = 3
|
||||
net.ipv4.tcp_mtu_probing = 1
|
||||
net.ipv4.tcp_sack = 1
|
||||
net.ipv4.tcp_timestamps = 1
|
||||
net.ipv4.tcp_window_scaling = 1
|
||||
|
||||
# ── 内存与端口 ──
|
||||
net.ipv4.ip_local_port_range = 1024 65535
|
||||
net.ipv4.tcp_mem = $((MEM_MB * 1024 / 8)) $((MEM_MB * 1024 / 4)) $((MEM_MB * 1024 / 2))
|
||||
net.ipv4.tcp_max_orphans = 32768
|
||||
|
||||
# ── 安全与防护 ──
|
||||
net.ipv4.conf.all.rp_filter = 1
|
||||
net.ipv4.conf.default.rp_filter = 1
|
||||
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
||||
net.ipv4.icmp_ignore_bogus_error_responses = 1
|
||||
net.ipv4.conf.all.accept_redirects = 0
|
||||
net.ipv4.conf.default.accept_redirects = 0
|
||||
net.ipv4.conf.all.send_redirects = 0
|
||||
net.ipv4.conf.default.send_redirects = 0
|
||||
|
||||
# ── IPv6 优化 ──
|
||||
net.ipv6.conf.all.accept_redirects = 0
|
||||
net.ipv6.conf.default.accept_redirects = 0
|
||||
|
||||
# ── 虚拟内存优化 ──
|
||||
vm.swappiness = $SWAPPINESS
|
||||
vm.dirty_ratio = 15
|
||||
vm.dirty_background_ratio = 5
|
||||
vm.overcommit_memory = 1
|
||||
vm.min_free_kbytes = $MIN_FREE_KB
|
||||
vm.vfs_cache_pressure = 50
|
||||
|
||||
# ── CPU/内核调度优化 ──
|
||||
kernel.sched_autogroup_enabled = 0
|
||||
$([ -f /proc/sys/kernel/numa_balancing ] && echo "kernel.numa_balancing = 0" || echo "# numa_balancing 不支持,跳过")
|
||||
|
||||
# ── 文件描述符 ──
|
||||
fs.file-max = 1048576
|
||||
fs.nr_open = 1048576
|
||||
|
||||
# ── 连接跟踪 ──
|
||||
$(if [ -f /proc/sys/net/netfilter/nf_conntrack_max ]; then
|
||||
echo "net.netfilter.nf_conntrack_max = $CONNTRACK_MAX"
|
||||
echo "net.netfilter.nf_conntrack_tcp_timeout_established = 7200"
|
||||
echo "net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30"
|
||||
echo "net.netfilter.nf_conntrack_tcp_timeout_close_wait = 15"
|
||||
echo "net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 15"
|
||||
else
|
||||
echo "# conntrack 未启用,跳过"
|
||||
fi)
|
||||
$HIGH_LAT_EXTRA
|
||||
SYSCTL
|
||||
|
||||
ok "配置已写入: $CONF"
|
||||
|
||||
# ── 应用配置 ──
|
||||
title "应用优化"
|
||||
local apply_errors
|
||||
apply_errors=$(sysctl -p "$CONF" 2>&1 | grep -i "error\|invalid\|cannot" || true)
|
||||
|
||||
if [ -n "$apply_errors" ]; then
|
||||
warn "部分参数不支持(已跳过):"
|
||||
echo "$apply_errors" | while read -r line; do
|
||||
echo -e " ${YELLOW}$line${NC}"
|
||||
done
|
||||
fi
|
||||
ok "sysctl 参数已应用"
|
||||
|
||||
# ── 禁用透明大页面(减少延迟抖动) ──
|
||||
if [ -f /sys/kernel/mm/transparent_hugepage/enabled ]; then
|
||||
echo never > /sys/kernel/mm/transparent_hugepage/enabled 2>/dev/null && \
|
||||
ok "透明大页面已禁用" || warn "透明大页面禁用失败"
|
||||
fi
|
||||
|
||||
# ── 设置文件描述符限制 ──
|
||||
if ! grep -q "# network-optimize" /etc/security/limits.conf 2>/dev/null; then
|
||||
cat >> /etc/security/limits.conf << 'LIMITS'
|
||||
|
||||
# network-optimize - 自动网络优化添加
|
||||
* soft nofile 1048576
|
||||
* hard nofile 1048576
|
||||
root soft nofile 1048576
|
||||
root hard nofile 1048576
|
||||
LIMITS
|
||||
ok "文件描述符限制已更新"
|
||||
fi
|
||||
|
||||
# ── 持久化 BBR 模块 ──
|
||||
if $USE_BBR; then
|
||||
if [ ! -f /etc/modules-load.d/bbr.conf ]; then
|
||||
echo "tcp_bbr" > /etc/modules-load.d/bbr.conf 2>/dev/null
|
||||
ok "BBR 模块持久化"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── 优化摘要 ──
|
||||
title "优化摘要"
|
||||
echo -e "${WHITE}┌──────────────────────────────────────────────────┐${NC}"
|
||||
printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "项目" "值"
|
||||
echo -e "${WHITE}├──────────────────────────────────────────────────┤${NC}"
|
||||
printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "带宽等级" "$BW_CLASS (${SPEED}Mbps)"
|
||||
printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "拥塞算法" "$TARGET_CC"
|
||||
printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "队列调度" "$QDISC"
|
||||
printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "TCP 缓冲区(max)" "$(numfmt --to=iec $TCP_RMEM_MAX 2>/dev/null || echo ${TCP_RMEM_MAX})"
|
||||
printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "连接队列" "$SOMAXCONN"
|
||||
printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "网络延迟" "${LATENCY}ms"
|
||||
printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "高延迟优化" "$(if $HIGH_LATENCY; then echo '已启用'; else echo '未需要'; fi)"
|
||||
printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "配置文件" "$CONF"
|
||||
echo -e "${WHITE}└──────────────────────────────────────────────────┘${NC}"
|
||||
|
||||
echo -e "\n${GREEN}网络优化完成!${NC}"
|
||||
echo -e "回滚命令: ${CYAN}restore_network_defaults${NC}"
|
||||
}
|
||||
|
||||
# ======================== 回滚函数 ========================
|
||||
|
||||
restore_network_defaults() {
|
||||
local CONF="/etc/sysctl.d/99-network-optimize.conf"
|
||||
|
||||
title "回滚网络优化"
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
fail "请以 root 权限运行"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 查找最近的备份
|
||||
local latest_bak
|
||||
latest_bak=$(ls -t /etc/sysctl.d/99-network-optimize.conf.bak.* 2>/dev/null | head -1)
|
||||
|
||||
if [ -n "$latest_bak" ]; then
|
||||
cp "$latest_bak" "$CONF"
|
||||
sysctl -p "$CONF" 2>/dev/null
|
||||
ok "已从备份恢复: $latest_bak"
|
||||
elif [ -f "$CONF" ]; then
|
||||
rm -f "$CONF"
|
||||
sysctl --system 2>/dev/null
|
||||
ok "已删除优化配置,恢复系统默认"
|
||||
else
|
||||
warn "没有优化配置需要回滚"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 清理 limits.conf 添加的部分
|
||||
if grep -q "# network-optimize" /etc/security/limits.conf 2>/dev/null; then
|
||||
sed -i '/# network-optimize/,+4d' /etc/security/limits.conf
|
||||
ok "文件描述符限制已恢复"
|
||||
fi
|
||||
|
||||
# 清理 BBR 持久化
|
||||
rm -f /etc/modules-load.d/bbr.conf 2>/dev/null
|
||||
|
||||
ok "网络配置已回滚"
|
||||
}
|
||||
|
||||
# ======================== 查看当前状态 ========================
|
||||
|
||||
show_network_status() {
|
||||
title "当前网络内核参数"
|
||||
|
||||
local params=(
|
||||
"net.ipv4.tcp_congestion_control"
|
||||
"net.core.default_qdisc"
|
||||
"net.core.rmem_max"
|
||||
"net.core.wmem_max"
|
||||
"net.ipv4.tcp_rmem"
|
||||
"net.ipv4.tcp_wmem"
|
||||
"net.core.somaxconn"
|
||||
"net.core.netdev_max_backlog"
|
||||
"net.ipv4.tcp_fastopen"
|
||||
"net.ipv4.tcp_tw_reuse"
|
||||
"net.ipv4.tcp_fin_timeout"
|
||||
"net.ipv4.ip_local_port_range"
|
||||
"net.ipv4.tcp_mtu_probing"
|
||||
"fs.file-max"
|
||||
)
|
||||
|
||||
for p in "${params[@]}"; do
|
||||
local val
|
||||
val=$(sysctl_get "$p")
|
||||
printf " %-45s = %s\n" "$p" "$val"
|
||||
done
|
||||
|
||||
if [ -f /etc/sysctl.d/99-network-optimize.conf ]; then
|
||||
echo ""
|
||||
ok "优化配置已安装: /etc/sysctl.d/99-network-optimize.conf"
|
||||
else
|
||||
echo ""
|
||||
warn "未检测到优化配置"
|
||||
fi
|
||||
}
|
||||
|
||||
# ======================== 入口 ========================
|
||||
# 用法:
|
||||
# source network-optimize.sh
|
||||
# auto_optimize_network # 自动检测并优化
|
||||
# show_network_status # 查看当前状态
|
||||
# restore_network_defaults # 回滚到默认
|
||||
#
|
||||
# 或直接运行:
|
||||
# bash network-optimize.sh
|
||||
# ========================
|
||||
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]] || [[ -z "${BASH_SOURCE[0]}" ]]; then
|
||||
# 支持参数: bash xxx.sh restore / bash xxx.sh status
|
||||
# 支持环境变量: ACTION=restore curl ... | bash
|
||||
_action="${1:-${ACTION:-optimize}}"
|
||||
case "$_action" in
|
||||
restore|rollback|回滚)
|
||||
restore_network_defaults
|
||||
;;
|
||||
status|状态)
|
||||
show_network_status
|
||||
;;
|
||||
*)
|
||||
auto_optimize_network
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
62
nginx.local
Normal file
62
nginx.local
Normal file
@@ -0,0 +1,62 @@
|
||||
[fail2ban-nginx-cc]
|
||||
|
||||
enabled = true
|
||||
filter = fail2ban-nginx-cc
|
||||
chain = DOCKER-USER
|
||||
port = http,https
|
||||
action = cloudflare
|
||||
logpath = /home/web/log/nginx/access.log
|
||||
maxretry = 3
|
||||
bantime = 3600
|
||||
findtime = 3600
|
||||
ignoreip = 192.168.0.1/24
|
||||
|
||||
|
||||
|
||||
[nginx-http-auth]
|
||||
|
||||
enabled = true
|
||||
chain = DOCKER-USER
|
||||
mode = fallback
|
||||
port = http,https
|
||||
logpath = /home/web/log/nginx/error.log
|
||||
|
||||
|
||||
|
||||
[nginx-limit-req]
|
||||
|
||||
enabled = true
|
||||
chain = DOCKER-USER
|
||||
port = http,https
|
||||
action = cloudflare
|
||||
logpath = /home/web/log/nginx/error.log
|
||||
|
||||
|
||||
|
||||
[nginx-botsearch]
|
||||
|
||||
enabled = true
|
||||
chain = DOCKER-USER
|
||||
port = http,https
|
||||
action = cloudflare
|
||||
logpath = /home/web/log/nginx/error.log
|
||||
|
||||
|
||||
|
||||
[nginx-bad-request]
|
||||
|
||||
enabled = true
|
||||
chain = DOCKER-USER
|
||||
port = http,https
|
||||
action = cloudflare
|
||||
logpath = /home/web/log/nginx/access.log
|
||||
|
||||
|
||||
|
||||
[php-url-fopen]
|
||||
|
||||
enabled = true
|
||||
chain = DOCKER-USER
|
||||
port = http,https
|
||||
action = cloudflare
|
||||
logpath = /home/web/log/nginx/access.log
|
||||
37
optimized_php.ini
Normal file
37
optimized_php.ini
Normal file
@@ -0,0 +1,37 @@
|
||||
; security
|
||||
display_errors = Off
|
||||
error_reporting = E_ALL & ~E_NOTICE & ~E_WARNING & ~E_DEPRECATED
|
||||
|
||||
expose_php = Off
|
||||
allow_url_fopen = Off
|
||||
allow_url_include = Off
|
||||
disable_functions = passthru,system,proc_open,popen,parse_ini_file,show_source
|
||||
default_charset = "UTF-8"
|
||||
|
||||
session.cookie_httponly = 1
|
||||
session.cookie_secure = 1
|
||||
session.use_strict_mode = 1
|
||||
session.use_only_cookies = 1
|
||||
|
||||
|
||||
; Opcache配置
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.memory_consumption=512
|
||||
opcache.interned_strings_buffer=32
|
||||
opcache.max_accelerated_files=100000
|
||||
opcache.revalidate_freq=0
|
||||
opcache.validate_timestamps=0
|
||||
opcache.fast_shutdown=1
|
||||
opcache.save_comments=1
|
||||
opcache.file_update_protection=0
|
||||
opcache.max_wasted_percentage=5
|
||||
opcache.jit=tracing
|
||||
opcache.jit_buffer_size=64M
|
||||
|
||||
|
||||
; Realpath Cache配置
|
||||
realpath_cache_size=4096k
|
||||
realpath_cache_ttl=3600
|
||||
|
||||
|
||||
7
pal_backup.sh
Normal file
7
pal_backup.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
clear
|
||||
mkdir -p /home/game
|
||||
docker cp steamcmd:/home/steam/Steam/steamapps/common/PalServer/Pal/Saved/ /home/game/palworld/
|
||||
cd /home/game && tar czvf palworld_$(date +"%Y%m%d%H%M%S").tar.gz palworld
|
||||
rm -rf /home/game/palworld/
|
||||
echo -e "\033[0;32m游戏存档已导出存放在: /home/game/\033[0m"
|
||||
13
pal_log.sh
Normal file
13
pal_log.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
clear
|
||||
echo "脚本更新日志"
|
||||
echo "------------------------"
|
||||
echo "2024-2-1 v1.0"
|
||||
echo "风靡全球的幻兽帕鲁服务端管理面板上线!"
|
||||
echo "------------------------"
|
||||
echo "2024-2-2 v1.0.1"
|
||||
echo "增加了游戏存档定时备份,可选每周,每天,每小时"
|
||||
echo "主菜单增加了游戏服务安装状态以及开服情况的智能显示"
|
||||
echo "主菜单增加k选项与科技lion官方脚本工具联动。"
|
||||
echo "主菜单增加游戏配置修改功能"
|
||||
echo "对脚本细节调优,体验更好"
|
||||
echo "------------------------"
|
||||
445
palworld.sh
Normal file
445
palworld.sh
Normal file
@@ -0,0 +1,445 @@
|
||||
#!/bin/bash
|
||||
ln -sf ~/palworld.sh /usr/local/bin/p
|
||||
|
||||
ip_address() {
|
||||
ipv4_address=$(curl -s ipv4.ip.sb)
|
||||
ipv6_address=$(curl -s --max-time 1 ipv6.ip.sb)
|
||||
}
|
||||
|
||||
|
||||
install() {
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "未提供软件包参数!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
for package in "$@"; do
|
||||
if ! command -v "$package" &>/dev/null; then
|
||||
if command -v apt &>/dev/null; then
|
||||
apt update -y && apt install -y "$package"
|
||||
elif command -v yum &>/dev/null; then
|
||||
yum -y update && yum -y install "$package"
|
||||
elif command -v apk &>/dev/null; then
|
||||
apk update && apk add "$package"
|
||||
else
|
||||
echo "未知的包管理器!"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
remove() {
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "未提供软件包参数!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
for package in "$@"; do
|
||||
if command -v apt &>/dev/null; then
|
||||
apt purge -y "$package"
|
||||
elif command -v yum &>/dev/null; then
|
||||
yum remove -y "$package"
|
||||
elif command -v apk &>/dev/null; then
|
||||
apk del "$package"
|
||||
else
|
||||
echo "未知的包管理器!"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
break_end() {
|
||||
echo -e "\033[0;32m操作完成\033[0m"
|
||||
echo "按任意键继续..."
|
||||
read -n 1 -s -r -p ""
|
||||
echo ""
|
||||
clear
|
||||
}
|
||||
|
||||
palworld() {
|
||||
p
|
||||
exit
|
||||
}
|
||||
|
||||
|
||||
install_add_docker() {
|
||||
if [ -f "/etc/alpine-release" ]; then
|
||||
apk update
|
||||
apk add docker docker-compose
|
||||
rc-update add docker default
|
||||
service docker start
|
||||
else
|
||||
curl -fsSL https://get.docker.com | sh && ln -s /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin
|
||||
systemctl start docker
|
||||
systemctl enable docker
|
||||
fi
|
||||
}
|
||||
|
||||
install_docker() {
|
||||
if ! command -v docker &>/dev/null; then
|
||||
install_add_docker
|
||||
else
|
||||
echo "Docker 已经安装"
|
||||
fi
|
||||
}
|
||||
|
||||
pal_start() {
|
||||
ip_address
|
||||
tmux new -d -s my1 "docker exec -it steamcmd bash -c '/home/steam/Steam/steamapps/common/PalServer/PalServer.sh'"
|
||||
echo -e "\033[0;32m幻兽帕鲁服务启动啦!\033[0m"
|
||||
echo -e "\033[0;32m游戏下载地址: https://store.steampowered.com/app/1623730\033[0m"
|
||||
echo -e "\033[0;32m进入游戏连接:\033[93m $ipv4_address:8255 \033[0;32m开始冒险吧!\033[0m"
|
||||
|
||||
}
|
||||
|
||||
pal_backup() {
|
||||
cd ~
|
||||
curl -sS -O https://kejilion.pro/pal_backup.sh && chmod +x pal_backup.sh
|
||||
}
|
||||
|
||||
pal_install_status() {
|
||||
CONTAINER_NAME="steamcmd"
|
||||
|
||||
# 检查容器是否已安装
|
||||
if [ "$(docker ps -a -q -f name=$CONTAINER_NAME 2>/dev/null)" ]; then
|
||||
container_status="\e[32m幻兽帕鲁服务已安装\e[0m" # 绿色
|
||||
else
|
||||
container_status="\e[90m幻兽帕鲁服务未安装\e[0m" # 灰色
|
||||
fi
|
||||
|
||||
SESSION_NAME="my1"
|
||||
|
||||
ip_address
|
||||
# 检查 tmux 中是否存在指定的工作区
|
||||
if tmux has-session -t $SESSION_NAME 2>/dev/null; then
|
||||
tmux_status="\e[32m已开服:\033[93m $ipv4_address:8255\e[0m" # 绿色
|
||||
else
|
||||
tmux_status="\e[90m未开服\e[0m" # 灰色
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
while true; do
|
||||
clear
|
||||
pal_install_status
|
||||
echo -e "\033[93m . . ."
|
||||
echo "._ _.|. , _ ._.| _|"
|
||||
echo "[_)(_]| \/\/ (_)[ |(_]"
|
||||
echo "| "
|
||||
echo -e "\033[96m幻兽帕鲁开服一键脚本工具v1.0.2 by KEJILION\033[0m"
|
||||
echo -e "\033[96m-输入\033[93mp\033[96m可快速启动此脚本-\033[0m"
|
||||
echo -e "$container_status $tmux_status"
|
||||
echo "------------------------"
|
||||
echo "1. 安装幻兽帕鲁服务"
|
||||
echo "2. 开启幻兽帕鲁服务"
|
||||
echo "3. 关闭幻兽帕鲁服务"
|
||||
echo "4. 重启幻兽帕鲁服务"
|
||||
echo "------------------------"
|
||||
echo "5. 查看服务器状态"
|
||||
echo "6. 设置虚拟内存"
|
||||
echo "------------------------"
|
||||
echo "7. 导出游戏存档"
|
||||
echo "8. 导入游戏存档"
|
||||
echo "9. 定时备份游戏存档"
|
||||
echo "------------------------"
|
||||
echo "10. 修改游戏配置"
|
||||
echo "------------------------"
|
||||
echo "11. 更新幻兽帕鲁服务"
|
||||
echo "12. 卸载幻兽帕鲁服务"
|
||||
echo "------------------------"
|
||||
echo "k. 科技lion脚本工具箱"
|
||||
echo "------------------------"
|
||||
echo "00. 脚本更新"
|
||||
echo "------------------------"
|
||||
echo "0. 退出脚本"
|
||||
echo "------------------------"
|
||||
read -p "请输入你的选择: " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
clear
|
||||
install_docker
|
||||
install tmux
|
||||
docker run -dit --name steamcmd -p 8255:8211/udp --restart=always cm2network/steamcmd
|
||||
docker exec -it steamcmd bash -c "/home/steam/steamcmd/steamcmd.sh +login anonymous +app_update 2394010 validate +quit"
|
||||
clear
|
||||
pal_start
|
||||
;;
|
||||
|
||||
2)
|
||||
clear
|
||||
docker start steamcmd > /dev/null 2>&1
|
||||
pal_start
|
||||
;;
|
||||
|
||||
3)
|
||||
clear
|
||||
tmux kill-session -t my1
|
||||
docker stop steamcmd > /dev/null 2>&1
|
||||
echo -e "\033[0;32m幻兽帕鲁服务已关闭\033[0m"
|
||||
;;
|
||||
|
||||
4)
|
||||
clear
|
||||
tmux kill-session -t my1
|
||||
docker restart steamcmd > /dev/null 2>&1
|
||||
pal_start
|
||||
;;
|
||||
|
||||
5)
|
||||
clear
|
||||
install btop
|
||||
clear
|
||||
btop
|
||||
;;
|
||||
|
||||
6)
|
||||
clear
|
||||
swap_used=$(free -m | awk 'NR==3{print $3}')
|
||||
swap_total=$(free -m | awk 'NR==3{print $2}')
|
||||
|
||||
if [ "$swap_total" -eq 0 ]; then
|
||||
swap_percentage=0
|
||||
else
|
||||
swap_percentage=$((swap_used * 100 / swap_total))
|
||||
fi
|
||||
|
||||
swap_info="${swap_used}MB/${swap_total}MB (${swap_percentage}%)"
|
||||
|
||||
echo "当前虚拟内存: $swap_info"
|
||||
|
||||
read -p "是否调整大小?(Y/N): " choice
|
||||
|
||||
case "$choice" in
|
||||
[Yy])
|
||||
# 输入新的虚拟内存大小
|
||||
read -p "请输入虚拟内存大小MB: " new_swap
|
||||
|
||||
# 获取当前系统中所有的 swap 分区
|
||||
swap_partitions=$(grep -E '^/dev/' /proc/swaps | awk '{print $1}')
|
||||
|
||||
# 遍历并删除所有的 swap 分区
|
||||
for partition in $swap_partitions; do
|
||||
swapoff "$partition"
|
||||
wipefs -a "$partition" # 清除文件系统标识符
|
||||
mkswap -f "$partition"
|
||||
echo "已删除并重新创建 swap 分区: $partition"
|
||||
done
|
||||
|
||||
# 确保 /swapfile 不再被使用
|
||||
swapoff /swapfile
|
||||
|
||||
# 删除旧的 /swapfile
|
||||
rm -f /swapfile
|
||||
|
||||
# 创建新的 swap 分区
|
||||
dd if=/dev/zero of=/swapfile bs=1M count=$new_swap
|
||||
chmod 600 /swapfile
|
||||
mkswap /swapfile
|
||||
swapon /swapfile
|
||||
|
||||
if [ -f /etc/alpine-release ]; then
|
||||
echo "/swapfile swap swap defaults 0 0" >> /etc/fstab
|
||||
echo "nohup swapon /swapfile" >> /etc/local.d/swap.start
|
||||
chmod +x /etc/local.d/swap.start
|
||||
rc-update add local
|
||||
else
|
||||
echo "/swapfile swap swap defaults 0 0" >> /etc/fstab
|
||||
fi
|
||||
|
||||
echo "虚拟内存大小已调整为${new_swap}MB"
|
||||
;;
|
||||
[Nn])
|
||||
echo "已取消"
|
||||
;;
|
||||
*)
|
||||
echo "无效的选择,请输入 Y 或 N。"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
7)
|
||||
clear
|
||||
mkdir -p /home/game
|
||||
docker cp steamcmd:/home/steam/Steam/steamapps/common/PalServer/Pal/Saved/ /home/game/palworld/ > /dev/null 2>&1
|
||||
cd /home/game && tar czvf palworld_$(date +"%Y%m%d%H%M%S").tar.gz palworld > /dev/null 2>&1
|
||||
rm -rf /home/game/palworld/
|
||||
echo -e "\033[0;32m游戏存档已导出存放在: /home/game/\033[0m"
|
||||
;;
|
||||
8)
|
||||
clear
|
||||
tmux kill-session -t my1
|
||||
docker exec -it steamcmd bash -c "rm -rf /home/steam/Steam/steamapps/common/PalServer/Pal/Saved/*"
|
||||
cd /home/game/ && ls -t /home/game/*.tar.gz | head -1 | xargs -I {} tar -xzf {}
|
||||
docker cp /home/game/palworld/Config steamcmd:/home/steam/Steam/steamapps/common/PalServer/Pal/Saved/Config > /dev/null 2>&1
|
||||
docker cp /home/game/palworld/ImGui steamcmd:/home/steam/Steam/steamapps/common/PalServer/Pal/Saved/ImGui > /dev/null 2>&1
|
||||
docker cp /home/game/palworld/SaveGames steamcmd:/home/steam/Steam/steamapps/common/PalServer/Pal/Saved/SaveGames > /dev/null 2>&1
|
||||
docker exec -it -u root steamcmd bash -c "chmod -R 777 /home/steam/Steam/steamapps/common/PalServer/Pal/Saved/"
|
||||
rm -rf /home/game/palworld/
|
||||
echo -e "\033[0;32m游戏存档已导入\033[0m"
|
||||
docker restart steamcmd > /dev/null 2>&1
|
||||
pal_start
|
||||
;;
|
||||
|
||||
9)
|
||||
clear
|
||||
echo "幻兽帕鲁游戏存档定时备份"
|
||||
echo "------------------------"
|
||||
echo "1. 每周备份 2. 每天备份 3. 每小时备份"
|
||||
echo "------------------------"
|
||||
read -p "请输入你的选择: " dingshi
|
||||
case $dingshi in
|
||||
1)
|
||||
pal_backup
|
||||
(crontab -l ; echo "0 0 * * 1 ./pal_backup.sh") | crontab - > /dev/null 2>&1
|
||||
echo "每周一备份,已设置"
|
||||
|
||||
;;
|
||||
2)
|
||||
pal_backup
|
||||
(crontab -l ; echo "0 3 * * * ./pal_backup.sh") | crontab - > /dev/null 2>&1
|
||||
echo "每天凌晨3点备份,已设置"
|
||||
|
||||
;;
|
||||
3)
|
||||
pal_backup
|
||||
(crontab -l ; echo "0 * * * * ./pal_backup.sh") | crontab - > /dev/null 2>&1
|
||||
echo "每小时整点备份,已设置"
|
||||
|
||||
;;
|
||||
*)
|
||||
echo "已取消"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
10)
|
||||
clear
|
||||
tmux kill-session -t my1
|
||||
cd ~ && curl -sS -O https://kejilion.pro/PalWorldSettings.ini
|
||||
|
||||
echo "配置游戏参数"
|
||||
echo "------------------------"
|
||||
read -p "设置加入的密码(回车默认无密码): " server_password
|
||||
read -p "设置游戏难度: (1. 简单 2. 普通 3. 困难):" Difficulty
|
||||
case $Difficulty in
|
||||
1)
|
||||
Difficulty=1
|
||||
;;
|
||||
|
||||
2)
|
||||
Difficulty=2
|
||||
;;
|
||||
3)
|
||||
Difficulty=3
|
||||
;;
|
||||
*)
|
||||
echo "-默认设置为普通难度"
|
||||
Difficulty=2
|
||||
;;
|
||||
esac
|
||||
|
||||
read -p "经验值倍率: (回车默认1倍):" exp_rate
|
||||
ExpRate=${exp_rate:-1}
|
||||
read -p "死亡后掉落设置: (1. 掉落 2. 不掉落):" DeathPenalty
|
||||
case $DeathPenalty in
|
||||
1)
|
||||
DeathPenalty=All
|
||||
;;
|
||||
|
||||
2)
|
||||
DeathPenalty=None
|
||||
;;
|
||||
*)
|
||||
DeathPenalty=All
|
||||
echo "-默认设置为掉落"
|
||||
;;
|
||||
esac
|
||||
|
||||
read -p "设置pvp模式: (1. 开启 2. 关闭):" pal_pvp
|
||||
|
||||
case $pal_pvp in
|
||||
1)
|
||||
pal_pvp=True
|
||||
;;
|
||||
2)
|
||||
pal_pvp=False
|
||||
;;
|
||||
*)
|
||||
pal_pvp=False
|
||||
echo "-默认关闭pvp模式"
|
||||
;;
|
||||
esac
|
||||
|
||||
# 更新配置文件
|
||||
sed -i "s/ServerPassword=\"\"/ServerPassword=\"$server_password\"/" ~/PalWorldSettings.ini
|
||||
sed -i "s/Difficulty=2/Difficulty=$Difficulty/" ~/PalWorldSettings.ini
|
||||
sed -i "s/ExpRate=1.000000/ExpRate=$ExpRate/" ~/PalWorldSettings.ini
|
||||
sed -i "s/DeathPenalty=All/DeathPenalty=$DeathPenalty/" ~/PalWorldSettings.ini
|
||||
sed -i "s/bEnablePlayerToPlayerDamage=False/bEnablePlayerToPlayerDamage=$pal_pvp/" ~/PalWorldSettings.ini
|
||||
sed -i "s/bIsPvP=False/bIsPvP=$pal_pvp/" ~/PalWorldSettings.ini
|
||||
echo "------------------------"
|
||||
echo "配置文件已更新"
|
||||
|
||||
docker exec -it steamcmd bash -c "rm -f /home/steam/Steam/steamapps/common/PalServer/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini"
|
||||
docker cp ~/PalWorldSettings.ini steamcmd:/home/steam/Steam/steamapps/common/PalServer/Pal/Saved/Config/LinuxServer/ > /dev/null 2>&1
|
||||
docker exec -it -u root steamcmd bash -c "chmod -R 777 /home/steam/Steam/steamapps/common/PalServer/Pal/Saved/"
|
||||
rm -f ~/PalWorldSettings.ini
|
||||
echo -e "\033[0;32m游戏配置已导入\033[0m"
|
||||
docker restart steamcmd > /dev/null 2>&1
|
||||
pal_start
|
||||
;;
|
||||
|
||||
|
||||
11)
|
||||
clear
|
||||
tmux kill-session -t my1
|
||||
docker restart steamcmd > /dev/null 2>&1
|
||||
docker exec -it steamcmd bash -c "/home/steam/steamcmd/steamcmd.sh +login anonymous +app_update 2394010 validate +quit"
|
||||
clear
|
||||
echo -e "\033[0;32m幻兽帕鲁已更新\033[0m"
|
||||
pal_start
|
||||
;;
|
||||
|
||||
12)
|
||||
clear
|
||||
docker rm -f steamcmd
|
||||
docker rmi -f cm2network/steamcmd
|
||||
;;
|
||||
|
||||
k)
|
||||
cd ~
|
||||
curl -sS -O https://kejilion.pro/kejilion.sh && chmod +x kejilion.sh && ./kejilion.sh
|
||||
exit
|
||||
;;
|
||||
|
||||
00)
|
||||
cd ~
|
||||
curl -sS -O https://kejilion.pro/pal_log.sh && chmod +x pal_log.sh && ./pal_log.sh
|
||||
rm pal_log.sh
|
||||
echo ""
|
||||
curl -sS -O https://kejilion.pro/palworld.sh && chmod +x palworld.sh
|
||||
echo "脚本已更新到最新版本!"
|
||||
break_end
|
||||
palworld
|
||||
;;
|
||||
|
||||
|
||||
0)
|
||||
clear
|
||||
exit
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "无效的输入!"
|
||||
;;
|
||||
esac
|
||||
break_end
|
||||
done
|
||||
12222
ru/kejilion.sh
Normal file
12222
ru/kejilion.sh
Normal file
File diff suppressed because it is too large
Load Diff
76
ru/to-ru.py
Normal file
76
ru/to-ru.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from deep_translator import GoogleTranslator
|
||||
import re
|
||||
import os
|
||||
|
||||
def is_chinese(text):
|
||||
return bool(re.search(r'[\u4e00-\u9fff]', text))
|
||||
|
||||
def translate_text(text):
|
||||
try:
|
||||
return GoogleTranslator(source='zh-CN', target='ru').translate(text)
|
||||
except Exception as e:
|
||||
print(f"\nTranslation error: {e}")
|
||||
return text
|
||||
|
||||
def translate_line_preserving_variables(line):
|
||||
"""
|
||||
Translate only Chinese parts in echo/read/send_stats commands, excluding shell variables
|
||||
"""
|
||||
# Match double or single quoted strings
|
||||
def repl(match):
|
||||
full_string = match.group(0)
|
||||
quote = full_string[0]
|
||||
content = full_string[1:-1]
|
||||
|
||||
# Split by variable expressions
|
||||
parts = re.split(r'(\$\{?\w+\}?)', content)
|
||||
translated_parts = [
|
||||
translate_text(p) if is_chinese(p) else p
|
||||
for p in parts
|
||||
]
|
||||
return quote + ''.join(translated_parts) + quote
|
||||
|
||||
return re.sub(r'(?:\'[^\']*\'|"[^"]*")', repl, line)
|
||||
|
||||
def translate_file(input_file, output_file):
|
||||
total_lines = sum(1 for _ in open(input_file, 'r', encoding='utf-8'))
|
||||
processed_lines = 0
|
||||
|
||||
with open(input_file, 'r', encoding='utf-8') as f_in, \
|
||||
open(output_file, 'w', encoding='utf-8') as f_out:
|
||||
|
||||
for line in f_in:
|
||||
processed_lines += 1
|
||||
progress = processed_lines / total_lines * 100
|
||||
print(f"\rProcessing: {progress:.1f}% ({processed_lines}/{total_lines})", end='')
|
||||
|
||||
leading_space = re.match(r'^(\s*)', line).group(1)
|
||||
stripped = line.strip()
|
||||
|
||||
if stripped.startswith('#') and is_chinese(stripped):
|
||||
comment_mark = '#'
|
||||
comment_text = stripped[1:].strip()
|
||||
if comment_text:
|
||||
translated = translate_text(comment_text)
|
||||
f_out.write(f"{leading_space}{comment_mark} {translated}\n")
|
||||
else:
|
||||
f_out.write(line)
|
||||
|
||||
elif any(cmd in stripped for cmd in ['echo', 'read', 'send_stats']) and is_chinese(stripped):
|
||||
translated_line = translate_line_preserving_variables(line)
|
||||
f_out.write(translated_line)
|
||||
|
||||
else:
|
||||
f_out.write(line)
|
||||
|
||||
print("\nTranslation completed.")
|
||||
print(f"Original file size: {os.path.getsize(input_file)} bytes")
|
||||
print(f"Translated file size: {os.path.getsize(output_file)} bytes")
|
||||
|
||||
if __name__ == "__main__":
|
||||
input_file = 'kejilion.sh'
|
||||
output_file = 'kejilion_ru.sh'
|
||||
translate_file(input_file, output_file)
|
||||
38
run_openclaw_manager_matrix.sh
Executable file
38
run_openclaw_manager_matrix.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
IMAGES=(
|
||||
"debian:12"
|
||||
"debian:13"
|
||||
"ubuntu:22.04"
|
||||
"ubuntu:24.04"
|
||||
"rockylinux:9"
|
||||
"almalinux:9"
|
||||
"fedora:41"
|
||||
)
|
||||
|
||||
install_cmd() {
|
||||
case "$1" in
|
||||
debian:*|ubuntu:*)
|
||||
echo 'apt-get update -y >/dev/null && DEBIAN_FRONTEND=noninteractive apt-get install -y bash jq grep sed coreutils >/dev/null'
|
||||
;;
|
||||
rockylinux:*|almalinux:*|fedora:*)
|
||||
echo 'dnf install -y bash jq grep sed coreutils >/dev/null'
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
for img in "${IMAGES[@]}"; do
|
||||
echo "===== $img ====="
|
||||
cmd=$(install_cmd "$img")
|
||||
if docker run --rm -v "$PWD":/src -w /src "$img" bash -lc "$cmd && bash -n kejilion.sh && ./tests_openclaw_manager_smoke.sh"; then
|
||||
echo "RESULT $img OK"
|
||||
else
|
||||
echo "RESULT $img FAIL"
|
||||
fi
|
||||
echo
|
||||
done
|
||||
5
sshd.local
Normal file
5
sshd.local
Normal file
@@ -0,0 +1,5 @@
|
||||
[sshd]
|
||||
|
||||
enabled = true
|
||||
mode = normal
|
||||
backend = systemd
|
||||
36
tests/openclaw/README.md
Normal file
36
tests/openclaw/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# OpenClaw tests
|
||||
|
||||
这里的脚本用于对 `kejilion.sh` 中的 OpenClaw 相关功能做最小回归与冒烟测试,主要覆盖:
|
||||
|
||||
- API 模型同步/协议探测
|
||||
- 记忆菜单
|
||||
- 插件/技能菜单
|
||||
- API 列表颜色/对齐展示(最小验证)
|
||||
|
||||
## 运行方式
|
||||
|
||||
在仓库根目录执行(推荐):
|
||||
|
||||
```bash
|
||||
bash tests/openclaw/tests_openclaw_api_protocol_detect_smoke.sh
|
||||
bash tests/openclaw/tests_openclaw_api_sync_diff_smoke.sh
|
||||
bash tests/openclaw/tests_openclaw_memory_menu_smoke.sh
|
||||
bash tests/openclaw/tests_openclaw_plugin_skill_menu_smoke.sh
|
||||
bash tests/openclaw/tests_openclaw_api_color_align_min.sh
|
||||
```
|
||||
|
||||
也可在任意工作目录执行:
|
||||
|
||||
```bash
|
||||
/path/to/repo/tests/openclaw/tests_openclaw_memory_menu_smoke.sh
|
||||
```
|
||||
|
||||
## 安全说明
|
||||
|
||||
- 脚本会创建临时工作目录与临时 `HOME`(位于 `/tmp`),并在结束后自动清理(除非设置 `KEEP_WORKDIR=true`)。
|
||||
- 运行过程使用 stub/模拟命令,不会改动真实 OpenClaw 配置或系统状态。
|
||||
- 如需保留临时目录用于排查,请执行:
|
||||
|
||||
```bash
|
||||
KEEP_WORKDIR=true bash tests/openclaw/tests_openclaw_memory_menu_smoke.sh
|
||||
```
|
||||
130
tests/openclaw/tests_change_model_dual_probe_smoke.sh
Executable file
130
tests/openclaw/tests_change_model_dual_probe_smoke.sh
Executable file
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
SCRIPT="$REPO_ROOT/kejilion.sh"
|
||||
WORKDIR="${TMPDIR:-/tmp}/openclaw-change-model-dual-probe-$$"
|
||||
mkdir -p "$WORKDIR/home/.openclaw"
|
||||
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
|
||||
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
python3 - "$SCRIPT" "$WORKDIR" <<'PY'
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
script_path = Path(sys.argv[1])
|
||||
workdir = Path(sys.argv[2])
|
||||
text = script_path.read_text(encoding='utf-8')
|
||||
match = re.search(r'\n\t\topenclaw_model_probe\(\) \{.*?\n\t\t\}', text, re.S)
|
||||
if not match:
|
||||
raise SystemExit('openclaw_model_probe not found')
|
||||
func = match.group(0).strip('\n')
|
||||
|
||||
config = {
|
||||
"models": {
|
||||
"providers": {
|
||||
"demo": {
|
||||
"baseUrl": "https://api.example.com/v1",
|
||||
"apiKey": "sk-test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(workdir / 'home' / '.openclaw').mkdir(parents=True, exist_ok=True)
|
||||
(workdir / 'home' / '.openclaw' / 'openclaw.json').write_text(json.dumps(config, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
|
||||
|
||||
harness = f'''#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
HOME="{workdir / 'home'}"
|
||||
OPENCLAW_PROBE_STATUS=""
|
||||
OPENCLAW_PROBE_MESSAGE=""
|
||||
OPENCLAW_PROBE_LATENCY=""
|
||||
OPENCLAW_PROBE_REPLY=""
|
||||
{func}
|
||||
'''
|
||||
(workdir / 'harness.sh').write_text(harness, encoding='utf-8')
|
||||
PY
|
||||
|
||||
run_case() {
|
||||
local case_name="$1"
|
||||
CASE_NAME="$case_name" WORKDIR="$WORKDIR" bash <<'EOF_CASE'
|
||||
set -euo pipefail
|
||||
source "$WORKDIR/harness.sh"
|
||||
|
||||
python3() {
|
||||
if [ "$1" != "-" ]; then
|
||||
command python3 "$@"
|
||||
return
|
||||
fi
|
||||
|
||||
local script
|
||||
script=$(cat)
|
||||
shift
|
||||
|
||||
CASE_NAME="$CASE_NAME" command python3 - "$script" "$@" <<'PYWRAP'
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
script = sys.argv[1]
|
||||
args = sys.argv[2:]
|
||||
case = os.environ['CASE_NAME']
|
||||
|
||||
class Resp(io.BytesIO):
|
||||
def __init__(self, data, code=200):
|
||||
super().__init__(data)
|
||||
self.status = code
|
||||
def __enter__(self):
|
||||
return self
|
||||
def __exit__(self, exc_type, exc, tb):
|
||||
return False
|
||||
|
||||
real_urlopen = urllib.request.urlopen
|
||||
|
||||
def fake_urlopen(req, timeout=0):
|
||||
url = getattr(req, 'full_url', '')
|
||||
if case == 'fallback-success':
|
||||
if url.endswith('/chat/completions'):
|
||||
raise urllib.error.HTTPError(url, 404, 'Not Found', {}, io.BytesIO(b'{"error":"chat disabled"}'))
|
||||
if url.endswith('/responses'):
|
||||
body = json.dumps({"output": [{"content": [{"text": "pong from responses"}]}]}).encode('utf-8')
|
||||
return Resp(body, 200)
|
||||
elif case == 'all-fail':
|
||||
if url.endswith('/chat/completions'):
|
||||
raise urllib.error.HTTPError(url, 404, 'Not Found', {}, io.BytesIO(b'{"error":"chat disabled"}'))
|
||||
if url.endswith('/responses'):
|
||||
raise urllib.error.HTTPError(url, 404, 'Not Found', {}, io.BytesIO(b'{"error":"responses disabled"}'))
|
||||
return real_urlopen(req, timeout=timeout)
|
||||
|
||||
urllib.request.urlopen = fake_urlopen
|
||||
sys.argv = ['python3'] + args
|
||||
ns = {'__name__': '__main__'}
|
||||
exec(compile(script, '<stdin>', 'exec'), ns, ns)
|
||||
PYWRAP
|
||||
}
|
||||
|
||||
set +e
|
||||
openclaw_model_probe "demo/test-model"
|
||||
probe_rc=$?
|
||||
set -e
|
||||
printf 'RC=%s\nSTATUS=%s\nMESSAGE=%s\nLATENCY=%s\nREPLY=%s\n' "$probe_rc" "$OPENCLAW_PROBE_STATUS" "$OPENCLAW_PROBE_MESSAGE" "$OPENCLAW_PROBE_LATENCY" "$OPENCLAW_PROBE_REPLY"
|
||||
EOF_CASE
|
||||
}
|
||||
|
||||
out1=$(run_case fallback-success)
|
||||
printf '%s\n' "$out1" | grep -q 'STATUS=OK'
|
||||
printf '%s\n' "$out1" | grep -q 'MESSAGE=/responses -> HTTP 200'
|
||||
printf '%s\n' "$out1" | grep -q 'REPLY=pong from responses'
|
||||
|
||||
out2=$(run_case all-fail || true)
|
||||
printf '%s\n' "$out2" | grep -q 'RC=1'
|
||||
printf '%s\n' "$out2" | grep -q 'STATUS=FAIL'
|
||||
printf '%s\n' "$out2" | grep -q '/responses -> HTTP 404 / exit 22;/chat/completions -> HTTP 404 / exit 22'
|
||||
|
||||
printf '%s\n' 'SMOKE_OK'
|
||||
40
tests/openclaw/tests_openclaw_api_color_align_min.sh
Executable file
40
tests/openclaw/tests_openclaw_api_color_align_min.sh
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# 模拟脚本原生颜色变量
|
||||
# shellcheck disable=SC2034
|
||||
gl_hong='\033[31m'
|
||||
# shellcheck disable=SC2034
|
||||
gl_lv='\033[32m'
|
||||
# shellcheck disable=SC2034
|
||||
gl_huang='\033[33m'
|
||||
# shellcheck disable=SC2034
|
||||
gl_bai='\033[0m'
|
||||
|
||||
printf '%s\n' '--- OpenClaw API 列表(最小颜色/对齐验证) ---'
|
||||
printf '%-4s %-18s %-44s %-8s %-12s\n' '序号' '名称' 'API地址' '模型数量' '延迟/状态'
|
||||
printf '%s\n' '----------------------------------------------------------------------------------------------'
|
||||
|
||||
# 模拟 Python 计算后的字段(纯数据,不含颜色码)
|
||||
while IFS=$'\t' read -r rec_type idx name base_url model_count latency_txt latency_level; do
|
||||
[[ "$rec_type" == "ROW" ]] || continue
|
||||
|
||||
latency_color="$gl_bai"
|
||||
case "$latency_level" in
|
||||
low) latency_color="$gl_lv" ;;
|
||||
medium) latency_color="$gl_huang" ;;
|
||||
high|unavailable) latency_color="$gl_hong" ;;
|
||||
unchecked) latency_color="$gl_bai" ;;
|
||||
esac
|
||||
|
||||
# 颜色在 Shell 层拼接并输出
|
||||
printf '%-4s %-18s %-44s %b%-8s%b %b%-12s%b\n' \
|
||||
"$idx" "$name" "$base_url" \
|
||||
"$gl_huang" "$model_count" "$gl_bai" \
|
||||
"$latency_color" "$latency_txt" "$gl_bai"
|
||||
done <<'EOF'
|
||||
ROW 1. alpha-openai https://api.alpha.example/v1 128 120ms low
|
||||
ROW 2. beta-proxy https://api.beta.example/v1 64 1300ms medium
|
||||
ROW 3. gamma-down https://api.gamma.example/v1 0 不可用 unavailable
|
||||
ROW 4. delta-custom - 12 未检测 unchecked
|
||||
EOF
|
||||
79
tests/openclaw/tests_openclaw_api_protocol_detect_smoke.sh
Executable file
79
tests/openclaw/tests_openclaw_api_protocol_detect_smoke.sh
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
SCRIPT="$REPO_ROOT/kejilion.sh"
|
||||
WORKDIR="${TMPDIR:-/tmp}/openclaw-api-protocol-test-$$"
|
||||
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw"
|
||||
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
|
||||
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
# 抽取 OpenClaw API 添加逻辑(只验证:不再做协议探测/自动纠正)
|
||||
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
break_end() { return 0; }
|
||||
send_stats() { return 0; }
|
||||
start_gateway() { return 0; }
|
||||
install() { return 0; }
|
||||
EOF_INNER
|
||||
|
||||
awk 'BEGIN{p=0}
|
||||
/add-all-models-from-provider\(\) \{/{p=1}
|
||||
/openclaw_api_manage_list\(\) \{/{p=0}
|
||||
p{print}
|
||||
' "$SCRIPT" >> "$WORKDIR/harness.sh"
|
||||
|
||||
awk 'BEGIN{p=0}
|
||||
/openclaw_detect_api_protocol_by_provider\(\) \{/{p=1}
|
||||
/fix-openclaw-provider-protocol-interactive\(\) \{/{p=0}
|
||||
p{print}
|
||||
' "$SCRIPT" >> "$WORKDIR/harness.sh"
|
||||
|
||||
chmod +x "$WORKDIR/harness.sh"
|
||||
|
||||
# stub: curl
|
||||
cat > "$WORKDIR/bin/curl" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
args="$*"
|
||||
if [[ "$args" == *"/models"* ]]; then
|
||||
cat <<'JSON'
|
||||
{"data": [{"id": "gpt-4o"}, {"id": "gpt-4o-mini"}]}
|
||||
JSON
|
||||
exit 0
|
||||
fi
|
||||
printf ""
|
||||
exit 0
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/curl"
|
||||
|
||||
export HOME="$WORKDIR/home"
|
||||
export PATH="$WORKDIR/bin:$PATH"
|
||||
export TERM=xterm
|
||||
|
||||
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
|
||||
{
|
||||
"models": {
|
||||
"mode": "merge",
|
||||
"providers": {}
|
||||
},
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"models": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
|
||||
source "$WORKDIR/harness.sh"
|
||||
|
||||
# 添加 provider,不应依赖协议探测;若脚本内部仍调用已删除函数会报错
|
||||
add-all-models-from-provider "demo" "https://api.example.com/v1" "sk-test"
|
||||
|
||||
echo "SMOKE_OK: no protocol probing required during add"
|
||||
|
||||
# 协议修复函数现在不再修改配置,只输出提示
|
||||
openclaw_detect_api_protocol_by_provider "$HOME/.openclaw/openclaw.json" "demo" >/dev/null 2>&1 || true
|
||||
|
||||
echo "SMOKE_OK: protocol switch is available"
|
||||
133
tests/openclaw/tests_openclaw_api_sync_diff_smoke.sh
Executable file
133
tests/openclaw/tests_openclaw_api_sync_diff_smoke.sh
Executable file
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
SCRIPT="$REPO_ROOT/kejilion.sh"
|
||||
WORKDIR="${TMPDIR:-/tmp}/openclaw-api-sync-diff-test-$$"
|
||||
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw"
|
||||
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
|
||||
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
break_end() { return 0; }
|
||||
send_stats() { return 0; }
|
||||
start_gateway() { return 0; }
|
||||
install() { return 0; }
|
||||
EOF_INNER
|
||||
|
||||
awk 'BEGIN{p=0}
|
||||
/sync_openclaw_api_models\(\) \{/{p=1}
|
||||
/install_moltbot\(\) \{/{p=0}
|
||||
p{print}
|
||||
' "$SCRIPT" >> "$WORKDIR/harness.sh"
|
||||
chmod +x "$WORKDIR/harness.sh"
|
||||
|
||||
|
||||
cat > "$WORKDIR/bin/python3" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
if [[ "$1" != "-" ]]; then
|
||||
exec /usr/bin/python3 "$@"
|
||||
fi
|
||||
SCRIPT=$(cat)
|
||||
export SCRIPT
|
||||
/usr/bin/python3 - "${@:2}" <<"PY_STDIN"
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
|
||||
class _Resp(io.BytesIO):
|
||||
def __init__(self, data, code=200):
|
||||
super().__init__(data)
|
||||
self._code = code
|
||||
def getcode(self):
|
||||
return self._code
|
||||
def __enter__(self):
|
||||
return self
|
||||
def __exit__(self, exc_type, exc, tb):
|
||||
return False
|
||||
|
||||
_real = urllib.request.urlopen
|
||||
|
||||
def urlopen(req, timeout=0):
|
||||
url = getattr(req, "full_url", "") or getattr(req, "get_full_url", lambda: "")()
|
||||
if url.endswith("/models"):
|
||||
data = json.dumps({"data": [{"id": "alpha"}, {"id": "beta"}]}).encode("utf-8")
|
||||
return _Resp(data, 200)
|
||||
if url.endswith("/responses") or url.endswith("/chat/completions"):
|
||||
raise urllib.error.HTTPError(url, 404, "Not Found", {}, None)
|
||||
return _real(req, timeout=timeout)
|
||||
|
||||
urllib.request.urlopen = urlopen
|
||||
script = os.environ.get("SCRIPT", "")
|
||||
exec(compile(script, "<stdin>", "exec"))
|
||||
PY_STDIN
|
||||
exit $?
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/python3"
|
||||
|
||||
cat > "$WORKDIR/bin/curl" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
# fake curl for /models, /responses, /chat/completions (for shell probes)
|
||||
set -e
|
||||
args="$*"
|
||||
if [[ "$args" == *"/responses"* ]]; then
|
||||
printf "404"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$args" == *"/chat/completions"* ]]; then
|
||||
printf "404"
|
||||
exit 0
|
||||
fi
|
||||
printf "000"
|
||||
exit 0
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/curl"
|
||||
|
||||
export HOME="$WORKDIR/home"
|
||||
export PATH="$WORKDIR/bin:$PATH"
|
||||
export TERM=xterm
|
||||
export ENABLE_STATS=false
|
||||
export sh_v=testing
|
||||
|
||||
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
|
||||
{
|
||||
"models": {
|
||||
"mode": "merge",
|
||||
"providers": {
|
||||
"demo": {
|
||||
"api": "openai-completions",
|
||||
"baseUrl": "https://api.example.com/v1",
|
||||
"apiKey": "sk-test",
|
||||
"models": [
|
||||
{"id": "alpha", "name": "demo / alpha"},
|
||||
{"id": "gamma", "name": "demo / gamma"}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"models": {
|
||||
"demo/alpha": {},
|
||||
"demo/gamma": {}
|
||||
},
|
||||
"model": "demo/alpha"
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
|
||||
source "$WORKDIR/harness.sh"
|
||||
|
||||
output=$(sync_openclaw_api_models 2>&1 || true)
|
||||
|
||||
printf '%s\n' "$output" | grep -q "➕ 新增模型(1):"
|
||||
printf '%s\n' "$output" | grep -q "^ + beta$"
|
||||
printf '%s\n' "$output" | grep -q "➖ 删除模型(1):"
|
||||
printf '%s\n' "$output" | grep -q "^ - gamma$"
|
||||
|
||||
printf '%s\n' "SMOKE_OK"
|
||||
237
tests/openclaw/tests_openclaw_memory_auto_setup_smoke.sh
Normal file
237
tests/openclaw/tests_openclaw_memory_auto_setup_smoke.sh
Normal file
@@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
SCRIPT="$REPO_ROOT/kejilion.sh"
|
||||
WORKDIR="${TMPDIR:-/tmp}/openclaw-memory-auto-setup-test-$$"
|
||||
mkdir -p "$WORKDIR/bin"
|
||||
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
|
||||
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
# 抽取 OpenClaw 记忆菜单实现
|
||||
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
break_end() { return 0; }
|
||||
send_stats() { return 0; }
|
||||
EOF_INNER
|
||||
|
||||
awk 'BEGIN{p=0} /openclaw_memory_config_file\(\) \{/{p=1} /openclaw_memory_menu\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
|
||||
chmod +x "$WORKDIR/harness.sh"
|
||||
|
||||
# stub: openclaw
|
||||
cat > "$WORKDIR/bin/openclaw" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cmd="$*"
|
||||
config_dir="$HOME/.openclaw/config"
|
||||
mkdir -p "$config_dir"
|
||||
key_file() {
|
||||
local key="$1"
|
||||
echo "$config_dir/${key//./_}"
|
||||
}
|
||||
if [[ "$cmd" == "config get"* ]]; then
|
||||
key="$3"
|
||||
file=$(key_file "$key")
|
||||
if [ -f "$file" ]; then
|
||||
cat "$file"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "config set"* ]]; then
|
||||
key="$3"
|
||||
val="$4"
|
||||
file=$(key_file "$key")
|
||||
echo "$val" > "$file"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$1" == "memory" && "$2" == "status" ]]; then
|
||||
agent="main"
|
||||
while [ $# -gt 0 ]; do
|
||||
if [ "$1" = "--agent" ] && [ $# -ge 2 ]; then
|
||||
agent="$2"
|
||||
shift 2
|
||||
continue
|
||||
fi
|
||||
shift
|
||||
done
|
||||
cat <<TXT
|
||||
Provider: qmd (requested: qmd)
|
||||
Vector: ready
|
||||
Indexed: 0/0 files
|
||||
Workspace: ${HOME}/.openclaw/workspace
|
||||
Store: ${OPENCLAW_STORE:-~/.openclaw/workspace/memory/index.sqlite}
|
||||
TXT
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$1" == "memory" && "$2" == "index" ]]; then
|
||||
echo "$cmd" >> "${HOME}/.openclaw/index_calls"
|
||||
echo "index ok"
|
||||
exit 0
|
||||
fi
|
||||
echo "mock openclaw $*"
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/openclaw"
|
||||
|
||||
# stub: curl
|
||||
cat > "$WORKDIR/bin/curl" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
args=("$@")
|
||||
|
||||
is_head=0
|
||||
for arg in "${args[@]}"; do
|
||||
if [ "$arg" = "-I" ]; then
|
||||
is_head=1
|
||||
fi
|
||||
done
|
||||
url="${args[$((${#args[@]}-1))]}"
|
||||
|
||||
for arg in "${args[@]}"; do
|
||||
if [[ "$arg" == *"ipinfo.io/country"* ]]; then
|
||||
echo "${FAKE_COUNTRY:-US}"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$arg" == *"bun.sh/install"* ]]; then
|
||||
cat <<'SCRIPT'
|
||||
mkdir -p "$HOME/.bun/bin"
|
||||
cat > "$HOME/.bun/bin/bun" <<'BUN'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
if [[ "$1" == "install" && "$2" == "-g" ]]; then
|
||||
mkdir -p "$HOME/.bun/bin"
|
||||
cat > "$HOME/.bun/bin/qmd" <<'QMD'
|
||||
#!/usr/bin/env bash
|
||||
exit 0
|
||||
QMD
|
||||
chmod +x "$HOME/.bun/bin/qmd"
|
||||
fi
|
||||
echo "$*" >> "$HOME/.openclaw/bun_calls"
|
||||
exit 0
|
||||
BUN
|
||||
chmod +x "$HOME/.bun/bin/bun"
|
||||
SCRIPT
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$is_head" = "1" ]; then
|
||||
if [[ "$url" == *"huggingface.co"* ]]; then
|
||||
exit ${FAKE_HF_OK:-0}
|
||||
fi
|
||||
if [[ "$url" == *"hf-mirror.com"* ]]; then
|
||||
exit ${FAKE_MIRROR_OK:-0}
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
dest=""
|
||||
for ((i=0;i<${#args[@]};i++)); do
|
||||
if [ "${args[$i]}" = "-o" ] && [ $((i+1)) -lt ${#args[@]} ]; then
|
||||
dest="${args[$((i+1))]}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$dest" ] && [[ "$url" == *"embeddinggemma"* ]]; then
|
||||
mkdir -p "$(dirname "$dest")"
|
||||
echo "model" > "$dest"
|
||||
echo "$url" >> "$HOME/.openclaw/download_calls"
|
||||
exit 0
|
||||
fi
|
||||
exit 0
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/curl"
|
||||
|
||||
# stub: wget (fallback, not used in test)
|
||||
cat > "$WORKDIR/bin/wget" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
exit 0
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/wget"
|
||||
|
||||
export PATH="$WORKDIR/bin:$PATH"
|
||||
export TERM=xterm
|
||||
|
||||
source "$WORKDIR/harness.sh"
|
||||
|
||||
make_home() {
|
||||
local name="$1"
|
||||
local home="$WORKDIR/$name/home"
|
||||
mkdir -p "$home/.openclaw/workspace/memory" "$home/.openclaw"
|
||||
echo "$home"
|
||||
}
|
||||
|
||||
run_auto() {
|
||||
local input="$1"
|
||||
local scheme="$2"
|
||||
local outfile="$3"
|
||||
printf "%b" "$input" | openclaw_memory_auto_setup_run "$scheme" >"$outfile"
|
||||
}
|
||||
|
||||
# Case 1: qmd 不存在 -> 安装 -> 写入绝对路径 -> index
|
||||
HOME_QMD=$(make_home "qmd")
|
||||
export HOME="$HOME_QMD"
|
||||
FAKE_COUNTRY=US FAKE_HF_OK=0 FAKE_MIRROR_OK=1 \
|
||||
run_auto "yes\n" "qmd" "$WORKDIR/out_qmd.txt"
|
||||
|
||||
# Case 2: local 模型不存在 -> 下载 -> 写入 modelPath -> index (CN)
|
||||
HOME_LOCAL=$(make_home "local")
|
||||
export HOME="$HOME_LOCAL"
|
||||
FAKE_COUNTRY=CN FAKE_HF_OK=1 FAKE_MIRROR_OK=0 \
|
||||
run_auto "yes\n" "local" "$WORKDIR/out_local.txt"
|
||||
|
||||
# Case 3: 已存在 -> 跳过下载/安装
|
||||
HOME_SKIP=$(make_home "skip")
|
||||
export HOME="$HOME_SKIP"
|
||||
mkdir -p "$HOME/.openclaw/models/embedding" "$HOME/.openclaw/config"
|
||||
MODEL_EXIST="$HOME/.openclaw/models/embedding/embeddinggemma-300M-Q8_0.gguf"
|
||||
|
||||
echo "local" > "$HOME/.openclaw/config/memory_backend"
|
||||
echo "local" > "$HOME/.openclaw/config/agents_defaults_memorySearch_provider"
|
||||
echo "$MODEL_EXIST" > "$HOME/.openclaw/config/agents_defaults_memorySearch_local_modelPath"
|
||||
|
||||
touch "$MODEL_EXIST"
|
||||
|
||||
# 预置 qmd
|
||||
mkdir -p "$HOME/.bun/bin"
|
||||
cat > "$HOME/.bun/bin/qmd" <<'QMD'
|
||||
#!/usr/bin/env bash
|
||||
exit 0
|
||||
QMD
|
||||
chmod +x "$HOME/.bun/bin/qmd"
|
||||
export PATH="$HOME/.bun/bin:$PATH"
|
||||
|
||||
FAKE_COUNTRY=US FAKE_HF_OK=0 FAKE_MIRROR_OK=0 \
|
||||
run_auto "yes\n" "local" "$WORKDIR/out_skip.txt"
|
||||
|
||||
export WORKDIR
|
||||
python3 - <<'PY'
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
workdir = os.environ['WORKDIR']
|
||||
|
||||
# Case1: qmd command absolute + index called
|
||||
home_qmd = Path(workdir) / 'qmd' / 'home'
|
||||
qmd_cmd = (home_qmd / '.openclaw/config/memory_qmd_command').read_text().strip()
|
||||
if not qmd_cmd.startswith('/'):
|
||||
raise SystemExit('qmd command not absolute')
|
||||
index_calls = (home_qmd / '.openclaw/index_calls').read_text()
|
||||
if 'memory index' not in index_calls or '--force' not in index_calls:
|
||||
raise SystemExit('qmd preheat index not called')
|
||||
|
||||
# Case2: local model downloaded + modelPath written
|
||||
home_local = Path(workdir) / 'local' / 'home'
|
||||
model_path = (home_local / '.openclaw/config/agents_defaults_memorySearch_local_modelPath').read_text().strip()
|
||||
if not Path(model_path).exists():
|
||||
raise SystemExit('model file not downloaded')
|
||||
if 'hf-mirror.com' not in (Path(workdir) / 'out_local.txt').read_text():
|
||||
raise SystemExit('mirror not used for CN')
|
||||
|
||||
# Case3: skip download
|
||||
home_skip = Path(workdir) / 'skip' / 'home'
|
||||
download_calls = home_skip / '.openclaw/download_calls'
|
||||
if download_calls.exists():
|
||||
raise SystemExit('download should be skipped when model exists')
|
||||
print('SMOKE_OK')
|
||||
PY
|
||||
230
tests/openclaw/tests_openclaw_memory_menu_smoke.sh
Executable file
230
tests/openclaw/tests_openclaw_memory_menu_smoke.sh
Executable file
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
SCRIPT="$REPO_ROOT/kejilion.sh"
|
||||
WORKDIR="${TMPDIR:-/tmp}/openclaw-memory-menu-test-$$"
|
||||
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw/workspace/memory"
|
||||
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
|
||||
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
# 抽取 OpenClaw 记忆菜单实现
|
||||
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
break_end() { return 0; }
|
||||
send_stats() { return 0; }
|
||||
EOF_INNER
|
||||
|
||||
awk 'BEGIN{p=0} /openclaw_memory_config_file\(\) \{/{p=1} /openclaw_memory_menu\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
|
||||
awk 'BEGIN{p=0} /openclaw_memory_menu\(\) \{/{p=1} /openclaw_backup_restore_menu\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
|
||||
chmod +x "$WORKDIR/harness.sh"
|
||||
|
||||
# stub: openclaw
|
||||
cat > "$WORKDIR/bin/openclaw" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
cmd="$*"
|
||||
if [[ "$cmd" == "config get"* ]]; then
|
||||
key="$3"
|
||||
case "$key" in
|
||||
memory.backend) echo "qmd" ;;
|
||||
memory.qmd.includeDefaultMemory)
|
||||
if [ -f "${HOME}/.openclaw/includeDefaultMemory" ]; then
|
||||
cat "${HOME}/.openclaw/includeDefaultMemory"
|
||||
else
|
||||
echo "true"
|
||||
fi
|
||||
;;
|
||||
memory.qmd.command) echo "qmd" ;;
|
||||
agents.defaults.memorySearch.local.modelPath) echo "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf" ;;
|
||||
agents.defaults.memorySearch.provider) echo "local" ;;
|
||||
*) echo "" ;;
|
||||
esac
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "config set"* ]]; then
|
||||
key="$3"
|
||||
val="$4"
|
||||
if [[ "$key" == "memory.qmd.includeDefaultMemory" ]]; then
|
||||
echo "$val" > "${HOME}/.openclaw/includeDefaultMemory"
|
||||
fi
|
||||
if [[ "$key" == "memory.backend" ]]; then
|
||||
echo "$val" > "${HOME}/.openclaw/backend"
|
||||
fi
|
||||
if [[ "$key" == "memory.qmd.command" ]]; then
|
||||
echo "$val" > "${HOME}/.openclaw/qmd_command"
|
||||
fi
|
||||
if [[ "$key" == "agents.defaults.memorySearch.provider" ]]; then
|
||||
echo "$val" > "${HOME}/.openclaw/provider"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$1" == "memory" && "$2" == "status" ]]; then
|
||||
agent="main"
|
||||
while [ $# -gt 0 ]; do
|
||||
if [ "$1" = "--agent" ] && [ $# -ge 2 ]; then
|
||||
agent="$2"
|
||||
shift 2
|
||||
continue
|
||||
fi
|
||||
shift
|
||||
done
|
||||
cat <<TXT
|
||||
Provider: qmd (requested: qmd)
|
||||
Vector: ready
|
||||
Indexed: 23/14 files
|
||||
Workspace: ${HOME}/.openclaw/workspace
|
||||
Store: ${OPENCLAW_STORE:-~/.openclaw/workspace/memory/index.sqlite}
|
||||
TXT
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$1" == "memory" && "$2" == "index" ]]; then
|
||||
echo "$cmd" >> "${HOME}/.openclaw/index_calls"
|
||||
echo "index ok"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "gateway restart" ]]; then
|
||||
echo "$cmd" >> "${HOME}/.openclaw/gateway_calls"
|
||||
echo "gateway restarted"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "gateway stop" ]]; then
|
||||
echo "gateway stopped"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "gateway start" ]]; then
|
||||
echo "gateway started"
|
||||
exit 0
|
||||
fi
|
||||
echo "mock openclaw $*"
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/openclaw"
|
||||
|
||||
# stub: qmd
|
||||
cat > "$WORKDIR/bin/qmd" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
exit 0
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/qmd"
|
||||
|
||||
# stub: curl (用于网络探测)
|
||||
cat > "$WORKDIR/bin/curl" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
if [[ "$*" == *"huggingface.co"* ]]; then
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$*" == *"hf-mirror.com"* ]]; then
|
||||
exit 0
|
||||
fi
|
||||
exit 0
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/curl"
|
||||
|
||||
export HOME="$WORKDIR/home"
|
||||
export PATH="$WORKDIR/bin:$PATH"
|
||||
export TERM=xterm
|
||||
|
||||
# minimal openclaw.json
|
||||
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
|
||||
{
|
||||
"memory": {
|
||||
"backend": "qmd",
|
||||
"qmd": {"command": "qmd", "includeDefaultMemory": true}
|
||||
},
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"memorySearch": {
|
||||
"provider": "local",
|
||||
"local": {
|
||||
"modelPath": "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
|
||||
# mock index sqlite
|
||||
mkdir -p "$HOME/.openclaw/workspace/memory"
|
||||
touch "$HOME/.openclaw/workspace/memory/index.sqlite"
|
||||
|
||||
# mock memory files
|
||||
cat > "$HOME/.openclaw/workspace/MEMORY.md" <<'TXT'
|
||||
# MEMORY
|
||||
line1
|
||||
line2
|
||||
line3
|
||||
TXT
|
||||
cat > "$HOME/.openclaw/workspace/memory/2026-03-11.md" <<'TXT'
|
||||
# 2026-03-11
|
||||
foo
|
||||
bar
|
||||
TXT
|
||||
|
||||
source "$WORKDIR/harness.sh"
|
||||
|
||||
run_menu() {
|
||||
local input="$1"
|
||||
local title="$2"
|
||||
local outfile="$3"
|
||||
echo "[TEST] $title"
|
||||
printf "%b" "$input" | openclaw_memory_menu >"$outfile"
|
||||
}
|
||||
|
||||
# 1) 状态展示(直接返回)
|
||||
run_menu "0\n" "status" "$WORKDIR/out_status.txt"
|
||||
# 2) 更新索引(增量)
|
||||
run_menu "1\nyes\n\n\n0\n" "index" "$WORKDIR/out_index.txt"
|
||||
# 3) 查看记忆文件(列表+查看)
|
||||
run_menu "2\n1\n\n\n\n\n0\n0\n" "files" "$WORKDIR/out_files.txt"
|
||||
# 4) 索引修复(检测到 includeDefaultMemory=false → 恢复为 true 并重建)
|
||||
echo "false" > "$HOME/.openclaw/includeDefaultMemory"
|
||||
run_menu "3\ny\n\n\n0\n" "fix" "$WORKDIR/out_fix.txt"
|
||||
# 5) 记忆方案(自动推荐并取消)
|
||||
run_menu "4\n1\n\n0\n0\n" "scheme" "$WORKDIR/out_scheme.txt"
|
||||
|
||||
# 6) Store 缺失时仍执行 rebuild
|
||||
: > "$HOME/.openclaw/index_calls"
|
||||
OPENCLAW_STORE="~/.openclaw/workspace/memory/missing.sqlite" \
|
||||
openclaw_memory_rebuild_index_safe >"$WORKDIR/out_missing.txt"
|
||||
|
||||
export WORKDIR
|
||||
python3 - <<'PY'
|
||||
import glob
|
||||
import os
|
||||
|
||||
flag_path = os.path.expanduser('~/.openclaw/includeDefaultMemory')
|
||||
if not os.path.exists(flag_path):
|
||||
raise SystemExit('includeDefaultMemory not updated')
|
||||
flag = open(flag_path, 'r', encoding='utf-8').read().strip()
|
||||
if flag != 'true':
|
||||
raise SystemExit('includeDefaultMemory not restored to true')
|
||||
# ensure backup created
|
||||
baks = glob.glob(os.path.expanduser('~/.openclaw/workspace/memory/index.sqlite.bak.*'))
|
||||
if not baks:
|
||||
raise SystemExit('index sqlite backup not created')
|
||||
# ensure missing store still triggers force index
|
||||
calls_path = os.path.expanduser('~/.openclaw/index_calls')
|
||||
if not os.path.exists(calls_path):
|
||||
raise SystemExit('index calls not recorded')
|
||||
with open(calls_path, 'r', encoding='utf-8') as fh:
|
||||
calls = fh.read()
|
||||
if 'memory index' not in calls or '--force' not in calls:
|
||||
raise SystemExit('force index not called for missing store')
|
||||
# ensure gateway restart called after rebuild
|
||||
gw_calls_path = os.path.expanduser('~/.openclaw/gateway_calls')
|
||||
if not os.path.exists(gw_calls_path):
|
||||
raise SystemExit('gateway restart not recorded')
|
||||
with open(gw_calls_path, 'r', encoding='utf-8') as fh:
|
||||
gw_calls = fh.read()
|
||||
if 'gateway restart' not in gw_calls:
|
||||
raise SystemExit('gateway restart not called after rebuild')
|
||||
# ensure warning emitted
|
||||
with open(os.path.join(os.environ['WORKDIR'], 'out_missing.txt'), 'r', encoding='utf-8') as fh:
|
||||
out = fh.read()
|
||||
if 'Store 原始值' not in out:
|
||||
raise SystemExit('missing store warning not found')
|
||||
if '索引已重建并自动重启网关' not in out:
|
||||
raise SystemExit('auto restart message not found')
|
||||
print('SMOKE_OK')
|
||||
PY
|
||||
150
tests/openclaw/tests_openclaw_memory_multi_agent_smoke.sh
Normal file
150
tests/openclaw/tests_openclaw_memory_multi_agent_smoke.sh
Normal file
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
SCRIPT="$REPO_ROOT/kejilion.sh"
|
||||
WORKDIR="${TMPDIR:-/tmp}/openclaw-memory-multi-agent-test-$$"
|
||||
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw/workspace/memory"
|
||||
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
|
||||
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
break_end() { return 0; }
|
||||
send_stats() { return 0; }
|
||||
EOF_INNER
|
||||
|
||||
awk 'BEGIN{p=0} /openclaw_memory_config_file\(\) \{/{p=1} /openclaw_permission_config_file\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
|
||||
chmod +x "$WORKDIR/harness.sh"
|
||||
|
||||
cat > "$WORKDIR/bin/openclaw" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cmd="$*"
|
||||
if [[ "$cmd" == "agents list --json" ]]; then
|
||||
cat <<'JSON'
|
||||
[
|
||||
{"id":"main","workspace":"~/.openclaw/workspace"},
|
||||
{"id":"work","workspace":"~/.openclaw/workspace-work"}
|
||||
]
|
||||
JSON
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "config get"* ]]; then
|
||||
key="$3"
|
||||
case "$key" in
|
||||
memory.backend) echo "qmd" ;;
|
||||
memory.qmd.command) echo "qmd" ;;
|
||||
agents.defaults.memorySearch.provider) echo "local" ;;
|
||||
agents.defaults.memorySearch.local.modelPath) echo "$HOME/.openclaw/models/embedding/model.gguf" ;;
|
||||
*) echo "" ;;
|
||||
esac
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "config set"* || "$cmd" == "config unset"* ]]; then
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$1" == "memory" && "$2" == "status" ]]; then
|
||||
agent="main"
|
||||
while [ $# -gt 0 ]; do
|
||||
if [ "$1" = "--agent" ] && [ $# -ge 2 ]; then
|
||||
agent="$2"
|
||||
shift 2
|
||||
continue
|
||||
fi
|
||||
shift
|
||||
done
|
||||
if [ "$agent" = "work" ]; then
|
||||
cat <<TXT
|
||||
Provider: qmd (requested: qmd)
|
||||
Vector: ready
|
||||
Indexed: 3/2 files
|
||||
Workspace: $HOME/.openclaw/workspace-work
|
||||
Store: $HOME/.openclaw/workspace-work/memory/index.sqlite
|
||||
TXT
|
||||
else
|
||||
cat <<TXT
|
||||
Provider: qmd (requested: qmd)
|
||||
Vector: ready
|
||||
Indexed: 5/4 files
|
||||
Workspace: $HOME/.openclaw/workspace
|
||||
Store: $HOME/.openclaw/workspace/memory/index.sqlite
|
||||
TXT
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$1" == "memory" && "$2" == "index" ]]; then
|
||||
echo "$cmd" >> "$HOME/.openclaw/index_calls"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$1" == "gateway" && "$2" == "restart" ]]; then
|
||||
echo "$cmd" >> "$HOME/.openclaw/gateway_calls"
|
||||
exit 0
|
||||
fi
|
||||
echo "mock openclaw $*"
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/openclaw"
|
||||
|
||||
export HOME="$WORKDIR/home"
|
||||
export PATH="$WORKDIR/bin:$PATH"
|
||||
export TERM=xterm
|
||||
|
||||
mkdir -p "$HOME/.openclaw/workspace/memory" "$HOME/.openclaw/workspace-work/memory"
|
||||
touch "$HOME/.openclaw/workspace/memory/index.sqlite" "$HOME/.openclaw/workspace-work/memory/index.sqlite"
|
||||
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
|
||||
{
|
||||
"agents": {
|
||||
"list": [
|
||||
{"id": "work", "workspace": "~/.openclaw/workspace-work"}
|
||||
]
|
||||
}
|
||||
}
|
||||
JSON
|
||||
cat > "$HOME/.openclaw/workspace/MEMORY.md" <<'TXT'
|
||||
# main memory
|
||||
TXT
|
||||
cat > "$HOME/.openclaw/workspace-work/MEMORY.md" <<'TXT'
|
||||
# work memory
|
||||
TXT
|
||||
cat > "$HOME/.openclaw/workspace-work/memory/2026-03-23.md" <<'TXT'
|
||||
# work note
|
||||
TXT
|
||||
|
||||
source "$WORKDIR/harness.sh"
|
||||
|
||||
openclaw_memory_render_status > "$WORKDIR/out_status.txt"
|
||||
openclaw_memory_file_render_list > "$WORKDIR/out_files.txt"
|
||||
openclaw_memory_prepare_workspace_all > "$WORKDIR/out_prepare.txt"
|
||||
openclaw_memory_rebuild_index_all > "$WORKDIR/out_rebuild.txt"
|
||||
|
||||
export WORKDIR
|
||||
python3 - <<'PY'
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
workdir = Path(os.environ['WORKDIR'])
|
||||
home = workdir / 'home'
|
||||
status_out = (workdir / 'out_status.txt').read_text(encoding='utf-8')
|
||||
files_out = (workdir / 'out_files.txt').read_text(encoding='utf-8')
|
||||
prepare_out = (workdir / 'out_prepare.txt').read_text(encoding='utf-8')
|
||||
rebuild_out = (workdir / 'out_rebuild.txt').read_text(encoding='utf-8')
|
||||
index_calls = (home / '.openclaw' / 'index_calls').read_text(encoding='utf-8')
|
||||
gateway_calls = (home / '.openclaw' / 'gateway_calls').read_text(encoding='utf-8')
|
||||
|
||||
if 'Agent: main' not in status_out or 'Agent: work' not in status_out:
|
||||
raise SystemExit('multi-agent status missing agent sections')
|
||||
if 'main/MEMORY.md' not in files_out or 'work/MEMORY.md' not in files_out:
|
||||
raise SystemExit('multi-agent file list missing memory files')
|
||||
if 'work/memory/2026-03-23.md' not in files_out:
|
||||
raise SystemExit('work daily memory file missing')
|
||||
if '检查并准备 2 个智能体工作区' not in prepare_out:
|
||||
raise SystemExit('prepare summary missing agent count')
|
||||
if '--agent main --force' not in index_calls or '--agent work --force' not in index_calls:
|
||||
raise SystemExit('force index not called for all agents')
|
||||
if 'gateway restart' not in gateway_calls:
|
||||
raise SystemExit('gateway restart not called after multi-agent rebuild')
|
||||
if '已为 2 个智能体重建索引' not in rebuild_out:
|
||||
raise SystemExit('multi-agent rebuild summary missing')
|
||||
print('SMOKE_OK')
|
||||
PY
|
||||
132
tests/openclaw/tests_openclaw_plugin_skill_menu_smoke.sh
Executable file
132
tests/openclaw/tests_openclaw_plugin_skill_menu_smoke.sh
Executable file
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
SCRIPT="$REPO_ROOT/kejilion.sh"
|
||||
WORKDIR="${TMPDIR:-/tmp}/openclaw-plugin-skill-menu-test-$$"
|
||||
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw/workspace/skills" "$WORKDIR/home/.openclaw"
|
||||
KEEP_WORKDIR=${KEEP_WORKDIR:-false}
|
||||
trap '[ "$KEEP_WORKDIR" = "true" ] || rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
cat > "$WORKDIR/harness.sh" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
break_end() { return 0; }
|
||||
send_stats() { return 0; }
|
||||
start_gateway() { openclaw gateway stop; openclaw gateway start; }
|
||||
EOF_INNER
|
||||
|
||||
# 抽取插件/技能管理实现
|
||||
awk 'BEGIN{p=0} /resolve_openclaw_plugin_id\(\) \{/{p=1} /openclaw_json_get_bool\(\) \{/{p=0} p{print}' "$SCRIPT" >> "$WORKDIR/harness.sh"
|
||||
chmod +x "$WORKDIR/harness.sh"
|
||||
|
||||
# stub: openclaw
|
||||
cat > "$WORKDIR/bin/openclaw" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
cmd="$*"
|
||||
if [[ "$cmd" == "plugins list" ]]; then
|
||||
cat <<TXT
|
||||
feishu disabled
|
||||
telegram enabled
|
||||
TXT
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "plugins enable"* ]]; then
|
||||
echo "enabled $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "plugins install"* ]]; then
|
||||
echo "install $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "plugins disable"* ]]; then
|
||||
echo "disable $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "plugins uninstall"* ]]; then
|
||||
# 模拟预装卸载失败
|
||||
if [[ "$cmd" == *"feishu"* ]]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "uninstall $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "skills list" ]]; then
|
||||
echo "skill1"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "gateway stop" ]]; then
|
||||
echo "gateway stop" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$cmd" == "gateway start" ]]; then
|
||||
echo "gateway start" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
|
||||
exit 0
|
||||
fi
|
||||
echo "mock openclaw $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/openclaw"
|
||||
|
||||
# stub: npx
|
||||
cat > "$WORKDIR/bin/npx" <<'EOF_INNER'
|
||||
#!/usr/bin/env bash
|
||||
if [[ "$*" == *"clawhub uninstall"* ]]; then
|
||||
echo "uninstall $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$*" == *"clawhub install"* ]]; then
|
||||
echo "install $*" >> "${TEST_LOG:-/tmp/openclaw-plugin.log}"
|
||||
exit 0
|
||||
fi
|
||||
exit 0
|
||||
EOF_INNER
|
||||
chmod +x "$WORKDIR/bin/npx"
|
||||
|
||||
export HOME="$WORKDIR/home"
|
||||
export PATH="$WORKDIR/bin:$PATH"
|
||||
export TERM=xterm
|
||||
export TEST_LOG="$WORKDIR/test.log"
|
||||
|
||||
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
|
||||
{"plugins":{"allow":["telegram","feishu"]}}
|
||||
JSON
|
||||
|
||||
# 模拟用户技能目录
|
||||
mkdir -p "$HOME/.openclaw/workspace/skills/skillA"
|
||||
|
||||
source "$WORKDIR/harness.sh"
|
||||
|
||||
# 插件安装:多输入,确保只重启一次
|
||||
printf "1\nfeishu telegram\n0\n" | install_plugin > "$WORKDIR/plugin_install.out"
|
||||
|
||||
# 插件删除:多输入,确保删除流程与 allowlist 移除
|
||||
printf "2\nfeishu telegram\n0\n" | install_plugin > "$WORKDIR/plugin_delete.out"
|
||||
|
||||
# 技能删除:二次确认 + 多输入,删除目录
|
||||
printf "2\nskillA skillB\ny\n0\n" | install_skill > "$WORKDIR/skill_delete.out"
|
||||
|
||||
# 断言:start_gateway 只调用一次/每次菜单
|
||||
start_count=$(grep -c "gateway start" "$TEST_LOG" || true)
|
||||
if [ "$start_count" -ne 3 ]; then
|
||||
echo "Expected 3 gateway starts (plugin install/delete + skill delete), got $start_count"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 断言:用户技能目录已删除
|
||||
if [ -d "$HOME/.openclaw/workspace/skills/skillA" ]; then
|
||||
echo "Skill directory was not removed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 断言:plugins.allow 移除
|
||||
if grep -q '"feishu"' "$HOME/.openclaw/openclaw.json"; then
|
||||
echo "feishu still in plugins.allow"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 断言:输出汇总
|
||||
grep -q "操作汇总" "$WORKDIR/plugin_install.out"
|
||||
grep -q "操作汇总" "$WORKDIR/plugin_delete.out"
|
||||
grep -q "操作汇总" "$WORKDIR/skill_delete.out"
|
||||
|
||||
echo "SMOKE_OK"
|
||||
66
tests/openclaw/tests_openclaw_sync_sessions_model_smoke.sh
Executable file
66
tests/openclaw/tests_openclaw_sync_sessions_model_smoke.sh
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root=$(cd "$(dirname "$0")/../.." && pwd)
|
||||
script="$repo_root/kejilion.sh"
|
||||
|
||||
workdir=$(mktemp -d)
|
||||
trap 'rm -rf "$workdir"' EXIT
|
||||
export HOME="$workdir/home"
|
||||
mkdir -p "$HOME/.openclaw/agents/main/sessions"
|
||||
mkdir -p "$HOME/.openclaw/agents/work/sessions"
|
||||
|
||||
funcs_file="$workdir/funcs.sh"
|
||||
{
|
||||
awk '/^[[:space:]]*openclaw_get_agents_dir\(\)/,/^[[:space:]]*\}$/' "$script"
|
||||
echo
|
||||
awk '/^[[:space:]]*openclaw_sync_sessions_model\(\)/,/^[[:space:]]*\}$/' "$script"
|
||||
} > "$funcs_file"
|
||||
|
||||
cat > "$HOME/.openclaw/agents/main/sessions/sessions.json" <<'JSON'
|
||||
{
|
||||
"agent:main:telegram:direct:123": {
|
||||
"sessionId": "abc-123",
|
||||
"modelOverride": "gpt-4",
|
||||
"providerOverride": "openai"
|
||||
},
|
||||
"agent:main:main": {
|
||||
"sessionId": "def-456"
|
||||
}
|
||||
}
|
||||
JSON
|
||||
|
||||
cat > "$HOME/.openclaw/agents/work/sessions/sessions.json" <<'JSON'
|
||||
{
|
||||
"agent:work:slack:channel:C789": {
|
||||
"sessionId": "ghi-789",
|
||||
"modelOverride": "claude-3",
|
||||
"providerOverride": "anthropic"
|
||||
}
|
||||
}
|
||||
JSON
|
||||
|
||||
source "$funcs_file"
|
||||
openclaw_sync_sessions_model "cli-api/gpt-5-pro"
|
||||
|
||||
echo "=== VERIFICATION ==="
|
||||
|
||||
main_file="$HOME/.openclaw/agents/main/sessions/sessions.json"
|
||||
work_file="$HOME/.openclaw/agents/work/sessions/sessions.json"
|
||||
|
||||
main_model=$(jq -r '."agent:main:telegram:direct:123".modelOverride' "$main_file")
|
||||
main_provider=$(jq -r '."agent:main:telegram:direct:123".providerOverride' "$main_file")
|
||||
main2_model=$(jq -r '."agent:main:main".modelOverride' "$main_file")
|
||||
main2_provider=$(jq -r '."agent:main:main".providerOverride // "null"' "$main_file")
|
||||
|
||||
work_model=$(jq -r '."agent:work:slack:channel:C789".modelOverride' "$work_file")
|
||||
work_provider=$(jq -r '."agent:work:slack:channel:C789".providerOverride' "$work_file")
|
||||
|
||||
[ "$main_model" = "gpt-5-pro" ] || { echo "FAIL: main model (got: $main_model)"; exit 1; }
|
||||
[ "$main_provider" = "cli-api" ] || { echo "FAIL: main provider (got: $main_provider)"; exit 1; }
|
||||
[ "$main2_model" = "gpt-5-pro" ] || { echo "FAIL: main2 model (got: $main2_model)"; exit 1; }
|
||||
[ "$main2_provider" = "cli-api" ] || { echo "FAIL: main2 provider (got: $main2_provider)"; exit 1; }
|
||||
[ "$work_model" = "gpt-5-pro" ] || { echo "FAIL: work model (got: $work_model)"; exit 1; }
|
||||
[ "$work_provider" = "cli-api" ] || { echo "FAIL: work provider (got: $work_provider)"; exit 1; }
|
||||
|
||||
echo "PASS: openclaw_sync_sessions_model smoke"
|
||||
77
tests/tests_openclaw_config_path_resolution_smoke.sh
Executable file
77
tests/tests_openclaw_config_path_resolution_smoke.sh
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root=$(cd "$(dirname "$0")/.." && pwd)
|
||||
script="$repo_root/kejilion.sh"
|
||||
|
||||
workdir=$(mktemp -d)
|
||||
trap 'rm -rf "$workdir"' EXIT
|
||||
export HOME="$workdir/home"
|
||||
mkdir -p "$HOME/.openclaw"
|
||||
|
||||
python_extract() {
|
||||
python3 - "$script" <<'PY'
|
||||
import re, sys
|
||||
text = open(sys.argv[1], encoding='utf-8').read()
|
||||
patterns = [
|
||||
r'openclaw_get_config_file\(\) \{.*?\n\t\t\}',
|
||||
r'openclaw_memory_config_file\(\) \{.*?\n\t\}',
|
||||
r'openclaw_permission_config_file\(\) \{.*?\n\t\}',
|
||||
]
|
||||
for pat in patterns:
|
||||
m = re.search(pat, text, re.S)
|
||||
if not m:
|
||||
raise SystemExit(f'missing pattern: {pat}')
|
||||
print(m.group(0))
|
||||
print()
|
||||
PY
|
||||
}
|
||||
|
||||
snippet="$workdir/snippet.sh"
|
||||
{
|
||||
echo '#!/usr/bin/env bash'
|
||||
echo 'set -euo pipefail'
|
||||
python_extract
|
||||
cat <<'SH'
|
||||
main() {
|
||||
echo "CFG=$(openclaw_get_config_file)"
|
||||
echo "MEM=$(openclaw_memory_config_file)"
|
||||
echo "PERM=$(openclaw_permission_config_file)"
|
||||
}
|
||||
main "$@"
|
||||
SH
|
||||
} > "$snippet"
|
||||
chmod +x "$snippet"
|
||||
|
||||
assert_eq() {
|
||||
local got="$1" expected="$2" msg="$3"
|
||||
if [ "$got" != "$expected" ]; then
|
||||
echo "ASSERT FAIL: $msg"
|
||||
echo "got: $got"
|
||||
echo "exp: $expected"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
user_cfg="$HOME/.openclaw/openclaw.json"
|
||||
root_cfg="$workdir/root/.openclaw/openclaw.json"
|
||||
mkdir -p "$(dirname "$root_cfg")"
|
||||
|
||||
out=$("$snippet")
|
||||
assert_eq "$(printf '%s\n' "$out" | sed -n '1p')" "CFG=$user_cfg" 'default config path should be user path'
|
||||
assert_eq "$(printf '%s\n' "$out" | sed -n '2p')" "MEM=$user_cfg" 'memory config path should follow helper'
|
||||
assert_eq "$(printf '%s\n' "$out" | sed -n '3p')" "PERM=$user_cfg" 'permission config path should follow helper'
|
||||
|
||||
printf '{}\n' > "$root_cfg"
|
||||
rm -f "$user_cfg"
|
||||
out=$("$snippet")
|
||||
assert_eq "$(printf '%s\n' "$out" | sed -n '1p')" "CFG=$user_cfg" 'non-root should not silently fallback to root config'
|
||||
assert_eq "$(printf '%s\n' "$out" | sed -n '2p')" "MEM=$user_cfg" 'memory config should stay in user path for non-root'
|
||||
assert_eq "$(printf '%s\n' "$out" | sed -n '3p')" "PERM=$user_cfg" 'permission config should stay in user path for non-root'
|
||||
|
||||
mkdir -p "$(dirname "$user_cfg")"
|
||||
printf '{"user":true}\n' > "$user_cfg"
|
||||
out=$("$snippet")
|
||||
assert_eq "$(printf '%s\n' "$out" | sed -n '1p')" "CFG=$user_cfg" 'user config should take precedence'
|
||||
|
||||
echo 'PASS: openclaw config path resolution smoke'
|
||||
163
tests_openclaw_manager_smoke.sh
Executable file
163
tests_openclaw_manager_smoke.sh
Executable file
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SCRIPT="$REPO_DIR/kejilion.sh"
|
||||
WORKDIR="${TMPDIR:-/tmp}/openclaw-manager-test-$$"
|
||||
mkdir -p "$WORKDIR/bin" "$WORKDIR/home/.openclaw"
|
||||
trap 'rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
cat > "$WORKDIR/harness.sh" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
install() { return 0; }
|
||||
break_end() { :; }
|
||||
send_stats() { :; }
|
||||
EOF
|
||||
sed -n '10597,10766p' "$SCRIPT" >> "$WORKDIR/harness.sh"
|
||||
printf '\n' >> "$WORKDIR/harness.sh"
|
||||
sed -n '12815,13534p' "$SCRIPT" >> "$WORKDIR/harness.sh"
|
||||
printf '\n' >> "$WORKDIR/harness.sh"
|
||||
sed -n '13781,13825p' "$SCRIPT" >> "$WORKDIR/harness.sh"
|
||||
printf '\n' >> "$WORKDIR/harness.sh"
|
||||
chmod +x "$WORKDIR/harness.sh"
|
||||
|
||||
cat > "$WORKDIR/bin/curl" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" = "-I" ]; then
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
if [[ "$*" == *"/models"* ]]; then
|
||||
cat <<JSON
|
||||
{"data":[{"id":"gpt-5.4"},{"id":"gpt-5.3-codex"},{"id":"claude-opus-4-6-thinking"}]}
|
||||
JSON
|
||||
else
|
||||
echo "US"
|
||||
fi
|
||||
EOF
|
||||
chmod +x "$WORKDIR/bin/curl"
|
||||
|
||||
cat > "$WORKDIR/bin/openclaw" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cmd="${1:-}"
|
||||
shift || true
|
||||
log_file="$HOME/.openclaw/mock_openclaw.log"
|
||||
echo "openclaw $cmd $*" >> "$log_file"
|
||||
case "$cmd" in
|
||||
dashboard)
|
||||
echo "Dashboard: http://127.0.0.1:18789/#token=deadbeef"
|
||||
;;
|
||||
config)
|
||||
sub="${1:-}"
|
||||
shift || true
|
||||
config_file="$HOME/.openclaw/mock_config.env"
|
||||
touch "$config_file"
|
||||
case "$sub" in
|
||||
set)
|
||||
key="$1"
|
||||
shift || true
|
||||
value="$*"
|
||||
grep -v "^${key}=" "$config_file" > "${config_file}.tmp" || true
|
||||
echo "${key}=${value}" >> "${config_file}.tmp"
|
||||
mv "${config_file}.tmp" "$config_file"
|
||||
;;
|
||||
get)
|
||||
key="$1"
|
||||
if grep -q "^${key}=" "$config_file"; then
|
||||
awk -F'=' -v k="$key" '$1==k {print substr($0, index($0, "=")+1); exit}' "$config_file"
|
||||
fi
|
||||
;;
|
||||
unset)
|
||||
key="$1"
|
||||
grep -v "^${key}=" "$config_file" > "${config_file}.tmp" || true
|
||||
mv "${config_file}.tmp" "$config_file"
|
||||
;;
|
||||
*)
|
||||
echo "mock openclaw config $sub $*"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
memory)
|
||||
sub="${1:-}"
|
||||
shift || true
|
||||
case "$sub" in
|
||||
status)
|
||||
echo "Provider: builtin"
|
||||
echo "Vector: ok"
|
||||
echo "Indexed: 0/0"
|
||||
echo "Workspace: $HOME/.openclaw/workspace"
|
||||
;;
|
||||
index)
|
||||
echo "mock memory index $*"
|
||||
;;
|
||||
*)
|
||||
echo "mock openclaw memory $sub $*"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
gateway)
|
||||
sub="${1:-}"
|
||||
shift || true
|
||||
if [ "$sub" = "restart" ]; then
|
||||
echo "mock gateway restart"
|
||||
else
|
||||
echo "mock openclaw gateway $sub $*"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "mock openclaw $cmd $*"
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "$WORKDIR/bin/openclaw"
|
||||
|
||||
export HOME="$WORKDIR/home"
|
||||
export PATH="$WORKDIR/bin:$PATH"
|
||||
cat > "$HOME/.openclaw/openclaw.json" <<'JSON'
|
||||
{"models":{"mode":"merge","providers":{}}}
|
||||
JSON
|
||||
|
||||
mkdir -p /home/web/conf.d
|
||||
cat > /home/web/conf.d/test-openclaw.conf <<'EOF'
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name claw.example.com;
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:18789;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
source "$WORKDIR/harness.sh"
|
||||
|
||||
echo '[TEST] add-all-models-from-provider'
|
||||
add-all-models-from-provider "cli-api" "https://example.com/v1" "dummy-token" >/tmp/add-models.out
|
||||
jq -e '.models.providers["cli-api"].models | length == 3' "$HOME/.openclaw/openclaw.json" >/dev/null
|
||||
jq -r '.models.providers["cli-api"].models[].id' "$HOME/.openclaw/openclaw.json"
|
||||
|
||||
echo '[TEST] openclaw_find_webui_domain'
|
||||
openclaw_find_webui_domain
|
||||
|
||||
echo '[TEST] openclaw_show_webui_addr'
|
||||
openclaw_show_webui_addr
|
||||
|
||||
echo '[TEST] openclaw_memory_auto_setup_run local'
|
||||
mkdir -p "$HOME/.openclaw/models/embedding"
|
||||
touch "$HOME/.openclaw/models/embedding/embeddinggemma-300M-Q8_0.gguf"
|
||||
echo "memory.local=legacy" > "$HOME/.openclaw/mock_config.env"
|
||||
printf "yes\n" | openclaw_memory_auto_setup_run "local" >/tmp/memory-auto.out
|
||||
|
||||
grep -q '^memory.backend=builtin' "$HOME/.openclaw/mock_config.env"
|
||||
grep -q '^agents.defaults.memorySearch.provider=local' "$HOME/.openclaw/mock_config.env"
|
||||
if grep -q '^memory.local=' "$HOME/.openclaw/mock_config.env"; then
|
||||
echo "memory.local should be removed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
grep -q 'openclaw memory index --force' "$HOME/.openclaw/mock_openclaw.log"
|
||||
grep -q 'openclaw gateway restart' "$HOME/.openclaw/mock_openclaw.log"
|
||||
|
||||
echo 'SMOKE_OK'
|
||||
92
translate.py
Normal file
92
translate.py
Normal file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from deep_translator import GoogleTranslator
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
|
||||
def is_chinese(text):
|
||||
return bool(re.search(r'[\u4e00-\u9fff]', text))
|
||||
|
||||
def translate_text(text, target_lang):
|
||||
if not text.strip() or not is_chinese(text):
|
||||
return text
|
||||
try:
|
||||
# 过滤掉一些不该翻译的特殊符号
|
||||
clean_text = text.strip()
|
||||
result = GoogleTranslator(source='zh-CN', target=target_lang).translate(clean_text)
|
||||
return result
|
||||
except Exception as e:
|
||||
print(f"\n[Error] {e}")
|
||||
return text
|
||||
|
||||
def process_content_with_vars(content, target_lang):
|
||||
"""
|
||||
核心逻辑:保护 ${var} 和 $var,翻译其中的中文部分
|
||||
"""
|
||||
# 匹配 ${var} 或 $var (字母数字下划线)
|
||||
parts = re.split(r'(\$\{\w+\}|\$\w+)', content)
|
||||
translated_parts = []
|
||||
for p in parts:
|
||||
if p.startswith('$'): # 变量部分,保持原样
|
||||
translated_parts.append(p)
|
||||
elif is_chinese(p): # 中文部分,翻译
|
||||
translated_parts.append(translate_text(p, target_lang))
|
||||
else: # 其他英文/符号,保持原样
|
||||
translated_parts.append(p)
|
||||
return "".join(translated_parts)
|
||||
|
||||
def universal_translator(line, target_lang):
|
||||
"""
|
||||
通用翻译引擎:识别行内所有引号内容并翻译
|
||||
"""
|
||||
# 1. 保护注释行
|
||||
leading_space = re.match(r'^(\s*)', line).group(1)
|
||||
stripped = line.strip()
|
||||
if stripped.startswith('#'):
|
||||
comment_content = stripped[1:].strip()
|
||||
if is_chinese(comment_content):
|
||||
return f"{leading_space}# {translate_text(comment_content, target_lang)}\n"
|
||||
return line
|
||||
|
||||
# 2. 识别所有引号内的内容 (双引号或单引号)
|
||||
# 使用正则匹配引号对,同时处理转义引号 \"
|
||||
def replacer(match):
|
||||
quote_type = match.group(1) # ' 或 "
|
||||
content = match.group(2) # 引号内的文本内容
|
||||
if is_chinese(content):
|
||||
# 翻译内容,但保护里面的变量
|
||||
translated = process_content_with_vars(content, target_lang)
|
||||
return f"{quote_type}{translated}{quote_type}"
|
||||
return match.group(0)
|
||||
|
||||
# 匹配 "content" 或 'content'
|
||||
new_line = re.sub(r'([\'"])(.*?)(?<!\\)\1', replacer, line)
|
||||
return new_line
|
||||
|
||||
def translate_file(input_file, output_file, target_lang):
|
||||
print(f"Translating to {target_lang}...")
|
||||
if not os.path.exists(input_file): return False
|
||||
|
||||
with open(input_file, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
total = len(lines)
|
||||
with open(output_file, 'w', encoding='utf-8') as f_out:
|
||||
for i, line in enumerate(lines):
|
||||
if (i + 1) % 10 == 0 or i + 1 == total:
|
||||
print(f"\rProgress: {(i+1)/total*100:.1f}%", end='')
|
||||
|
||||
# 只要行内有中文,就尝试用通用引擎翻译
|
||||
if is_chinese(line):
|
||||
f_out.write(universal_translator(line, target_lang))
|
||||
else:
|
||||
f_out.write(line)
|
||||
print(f"\n{target_lang} Success.")
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
input_file = 'kejilion.sh'
|
||||
langs = {'en': 'en', 'tw': 'zh-TW', 'kr': 'ko', 'jp': 'ja'}
|
||||
for dir_name, lang_code in langs.items():
|
||||
translate_file(input_file, f'{dir_name}/kejilion.sh', lang_code)
|
||||
22252
tw/kejilion.sh
Normal file
22252
tw/kejilion.sh
Normal file
File diff suppressed because it is too large
Load Diff
76
tw/to-tw.py
Normal file
76
tw/to-tw.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from deep_translator import GoogleTranslator
|
||||
import re
|
||||
import os
|
||||
|
||||
def is_chinese(text):
|
||||
return bool(re.search(r'[\u4e00-\u9fff]', text))
|
||||
|
||||
def translate_text(text):
|
||||
try:
|
||||
return GoogleTranslator(source='zh-CN', target='zh-TW').translate(text)
|
||||
except Exception as e:
|
||||
print(f"\nTranslation error: {e}")
|
||||
return text
|
||||
|
||||
def translate_line_preserving_variables(line):
|
||||
"""
|
||||
Translate only Chinese parts in echo/read/send_stats commands, excluding shell variables
|
||||
"""
|
||||
# Match double or single quoted strings
|
||||
def repl(match):
|
||||
full_string = match.group(0)
|
||||
quote = full_string[0]
|
||||
content = full_string[1:-1]
|
||||
|
||||
# Split by variable expressions
|
||||
parts = re.split(r'(\$\{?\w+\}?)', content)
|
||||
translated_parts = [
|
||||
translate_text(p) if is_chinese(p) else p
|
||||
for p in parts
|
||||
]
|
||||
return quote + ''.join(translated_parts) + quote
|
||||
|
||||
return re.sub(r'(?:\'[^\']*\'|"[^"]*")', repl, line)
|
||||
|
||||
def translate_file(input_file, output_file):
|
||||
total_lines = sum(1 for _ in open(input_file, 'r', encoding='utf-8'))
|
||||
processed_lines = 0
|
||||
|
||||
with open(input_file, 'r', encoding='utf-8') as f_in, \
|
||||
open(output_file, 'w', encoding='utf-8') as f_out:
|
||||
|
||||
for line in f_in:
|
||||
processed_lines += 1
|
||||
progress = processed_lines / total_lines * 100
|
||||
print(f"\rProcessing: {progress:.1f}% ({processed_lines}/{total_lines})", end='')
|
||||
|
||||
leading_space = re.match(r'^(\s*)', line).group(1)
|
||||
stripped = line.strip()
|
||||
|
||||
if stripped.startswith('#') and is_chinese(stripped):
|
||||
comment_mark = '#'
|
||||
comment_text = stripped[1:].strip()
|
||||
if comment_text:
|
||||
translated = translate_text(comment_text)
|
||||
f_out.write(f"{leading_space}{comment_mark} {translated}\n")
|
||||
else:
|
||||
f_out.write(line)
|
||||
|
||||
elif any(cmd in stripped for cmd in ['echo', 'read', 'send_stats']) and is_chinese(stripped):
|
||||
translated_line = translate_line_preserving_variables(line)
|
||||
f_out.write(translated_line)
|
||||
|
||||
else:
|
||||
f_out.write(line)
|
||||
|
||||
print("\nTranslation completed.")
|
||||
print(f"Original file size: {os.path.getsize(input_file)} bytes")
|
||||
print(f"Translated file size: {os.path.getsize(output_file)} bytes")
|
||||
|
||||
if __name__ == "__main__":
|
||||
input_file = 'kejilion.sh'
|
||||
output_file = 'kejilion_tw.sh'
|
||||
translate_file(input_file, output_file)
|
||||
423
update_log.sh
Normal file
423
update_log.sh
Normal file
@@ -0,0 +1,423 @@
|
||||
clear
|
||||
echo "脚本更新日志"
|
||||
echo "------------------------"
|
||||
echo "2023-8-13 v1.0.3"
|
||||
echo "1.甲骨文云的DD脚本,添加了Ubuntu 20.04的重装选项。"
|
||||
echo "2.LDNMP建站,开放了苹果CMS网站的搭建功能."
|
||||
echo "3.系统信息查询,增加了内核版本显示,美化了界面。"
|
||||
echo "4.甲骨文脚本中,添加了开启ROOT登录的选项。"
|
||||
echo "------------------------"
|
||||
echo "2023-8-13 v1.0.4"
|
||||
echo "1.LDNMP建站,开放了独角数卡网站的搭建功能."
|
||||
echo "2.LDNMP建站,优化了备份全站到远端服务器的稳定性."
|
||||
echo "3.Docker管理,全局状态信息,添加了所有docker卷的显示."
|
||||
echo "------------------------"
|
||||
echo "2023-8-14 v1.1"
|
||||
echo "Docker管理器全面升级,体验前所未有!"
|
||||
echo "-加入了docker容器管理面板"
|
||||
echo "-加入了docker镜像管理面板"
|
||||
echo "-加入了docker网络管理面板"
|
||||
echo "-加入了docker卷管理面板"
|
||||
echo "-删除docker时追加确认信息,拒绝误操作"
|
||||
echo "------------------------"
|
||||
echo "2023-8-14 v1.2"
|
||||
echo "1.新增了11选项,加入了常用面板工具合集!"
|
||||
echo "-支持安装各种面板,包括: 宝塔,宝塔国际版,1panel,Nginx Proxy Manager等等,满足更多人群的使用需求!"
|
||||
echo "2.优化了菜单效果"
|
||||
echo "------------------------"
|
||||
echo "2023-8-14 v1.3"
|
||||
echo "新增了12选项,我的工作区功能"
|
||||
echo "-将为你提供5个后台运行的工作区,用来执行后台任务。即使你断开SSH也不会中断,"
|
||||
echo "-非常有意思的功能,快去试试吧!"
|
||||
echo "------------------------"
|
||||
echo "2023-8-14 v1.3.2"
|
||||
echo "新增了13选项,系统工具"
|
||||
echo "科技lion一键脚本可以通过设置快捷键唤醒打开了,我设置的k作为脚本打开的快捷键!无需复制长命令了"
|
||||
echo "加入了ROOT密码修改,切换成ROOT登录模式"
|
||||
echo "系统设置中还有很多功能没开发,敬请期待!"
|
||||
echo "------------------------"
|
||||
echo "2023-8-15 v1.4"
|
||||
echo "全面适配Centos系统,实现Ubuntu,Debian,Centos三大主流系统的适配"
|
||||
echo "优化LDNMP中PHP输入数据最大时间,解决WordPress网站导入部分主题失败的问题"
|
||||
echo "------------------------"
|
||||
echo "2023-8-15 v1.4.1"
|
||||
echo "选项13,系统工具中,加入了安装Python最新版的选项,感谢群友春风得意马蹄疾的投稿!很好用!"
|
||||
echo "------------------------"
|
||||
echo "2023-8-15 v1.4.2"
|
||||
echo "docker管理中增加容器日志查看"
|
||||
echo "选项13,系统工具中,加入了留言板的选项,可以留下你的宝贵意见也可以在这里聊天,贼好玩!"
|
||||
echo "------------------------"
|
||||
echo "2023-8-15 v1.4.5"
|
||||
echo "优化了信息查询运行效率"
|
||||
echo "信息查询新增了地理位置显示"
|
||||
echo "优化了脚本内系统判断机制!"
|
||||
echo "------------------------"
|
||||
echo "2023-8-16 v1.4.6"
|
||||
echo "LDNMP建站中加入了删除站点删除数据库功能"
|
||||
echo "------------------------"
|
||||
echo "2023-8-16 v1.4.7"
|
||||
echo "选项11中,增加了一键搭建alist多存储文件列表工具的"
|
||||
echo "选项11中,增加了一键搭建网页版乌班图远程桌面"
|
||||
echo "选项13中,增加了开放所有端口功能"
|
||||
echo "------------------------"
|
||||
echo "2023-8-16 v1.4.8"
|
||||
echo "系统信息查询中,终于可以显示总流量消耗了!总接收和总发送两个信息"
|
||||
echo "------------------------"
|
||||
echo "2023-8-17 v1.4.9"
|
||||
echo "系统工具中新增SSH端口修改功能"
|
||||
echo "系统工具中新增优化DNS地址功能"
|
||||
echo "------------------------"
|
||||
echo "2023-8-18 v1.5"
|
||||
echo "系统性优化了代码,去除了无效的代码与空格"
|
||||
echo "系统信息查询添加了系统时间"
|
||||
echo "禁用ROOT账户,创建新的账户,更安全!"
|
||||
echo "------------------------"
|
||||
echo "2023-8-18 v1.5.1"
|
||||
echo "LDNMP加入了安装bingchatAI聊天网站"
|
||||
echo "面板工具中添加了哪吒探针脚本整合"
|
||||
echo "------------------------"
|
||||
echo "2023-8-18 v1.5.2"
|
||||
echo "LDNMP加入了更新LDNMP选项"
|
||||
echo "------------------------"
|
||||
echo "2023-8-19 v1.5.3"
|
||||
echo "面板工具添加安装QB离线BT磁力下载面板"
|
||||
echo "优化IP获取源"
|
||||
echo "------------------------"
|
||||
echo "2023-8-20 v1.5.4"
|
||||
echo "面板工具已安装的工具支持状态检测,可以进行删除了!"
|
||||
echo "------------------------"
|
||||
echo "2023-8-21 v1.5.5"
|
||||
echo "系统工具中添加优先ipv4/ipv6选项"
|
||||
echo "系统工具中添加查看端口占用状态选项"
|
||||
echo "------------------------"
|
||||
echo "2023-8-21 v1.5.6"
|
||||
echo "LDNMP建站添加了定时自动远程备份功能"
|
||||
echo "------------------------"
|
||||
echo "2023-8-22 v1.5.7"
|
||||
echo "面板工具增加了邮件服务器搭建,请确保服务器的25.80.443开放"
|
||||
echo "------------------------"
|
||||
echo "2023-8-23 v1.5.8"
|
||||
echo "面板工具增加了聊天系统搭建"
|
||||
echo "------------------------"
|
||||
echo "2023-8-24 v1.5.9"
|
||||
echo "面板工具增加了禅道项目管理软件搭建"
|
||||
echo "------------------------"
|
||||
echo "2023-8-24 v1.6"
|
||||
echo "面板工具增加了青龙面板搭建"
|
||||
echo "调整了面板工具列表的排版显示效果"
|
||||
echo "------------------------"
|
||||
echo "2023-8-27 v1.6.1"
|
||||
echo "LDNMP大幅优化安装体验,添加安装进度条和百分比显示,太刁了!"
|
||||
echo "------------------------"
|
||||
echo "2023-8-28 v1.6.2"
|
||||
echo "docker管理可以显示容器所属网络,并且可以加入网络和退出网络了"
|
||||
echo "------------------------"
|
||||
echo "2023-8-28 v1.6.3"
|
||||
echo "系统工具中增加修改虚拟内存大小的选项"
|
||||
echo "系统信息查询中显示虚拟内存占用"
|
||||
echo "------------------------"
|
||||
echo "2023-8-29 v1.6.4"
|
||||
echo "面板工具加入cloudreve网盘的搭建"
|
||||
echo "面板工具加入简单图床程序搭建"
|
||||
echo "------------------------"
|
||||
echo "2023-8-29 v1.6.5"
|
||||
echo "LDNMP加入了高逼格的flarum论坛搭建"
|
||||
echo "面板工具加入简单图床程序搭建"
|
||||
echo "------------------------"
|
||||
echo "2023-9-1 v1.6.6"
|
||||
echo "LDNMP环境安装时用户密码将随机生成,提升安全性,安装环境更简单!"
|
||||
echo "LDNMP环境安装时如果安装过docker将自动跳过,节省安装时间"
|
||||
echo "LDNMP环境更新WordPress到6.3.1版本"
|
||||
echo "------------------------"
|
||||
echo "2023-9-1 v1.6.7"
|
||||
echo "添加了账户管理功能,查看当前账户列表,添加删除账户,账号权限管理等"
|
||||
echo "------------------------"
|
||||
echo "2023-9-4 v1.6.8"
|
||||
echo "独角数卡登录时报错,显示解决办法"
|
||||
echo "------------------------"
|
||||
echo "2023-9-6 v1.6.9"
|
||||
echo "系统工具中添加随机用户密码生成器,方便懒得想用户名和密码的小伙伴"
|
||||
echo "优化了所有搭建网站与面板后的信息复制体验"
|
||||
echo "------------------------"
|
||||
echo "2023-9-11 v1.7"
|
||||
echo "面板工具中添加emby多媒体管理系统的搭建"
|
||||
echo "------------------------"
|
||||
echo "2023-9-15 v1.7.1"
|
||||
echo "LDNMP建站中可以搭建Bitwarden密码管理平台了"
|
||||
echo "------------------------"
|
||||
echo "2023-9-18 v1.7.2"
|
||||
echo "LDNMP建站将站点信息查询和站点管理合并"
|
||||
echo "LDNMP站点管理中添加证书重新申请和站点更换域名的功能"
|
||||
echo "------------------------"
|
||||
echo "2023-9-25 v1.8"
|
||||
echo "LDNMP建站增加了服务器与网站防护功能,防御暴力破解,防御网站被攻击"
|
||||
echo "------------------------"
|
||||
echo "2023-9-28 v1.8.2"
|
||||
echo "LDNMP建站优化了运行速度和安全性,增加了频率限制"
|
||||
echo "LDNMP建站优化了防御程序的高可用性"
|
||||
echo "------------------------"
|
||||
echo "2023-10-3 v1.8.3"
|
||||
echo "系统工具增加系统时区切换功能"
|
||||
echo "------------------------"
|
||||
echo "2023-10-7 v1.8.4"
|
||||
echo "LDNMP建站添加halo博客网站搭建"
|
||||
echo "------------------------"
|
||||
echo "2023-10-12 v1.8.5"
|
||||
echo "LDNMP建站添加优化LDNMP环境选项,可以开启高性能模式,大幅提升网站性能,应对高并发!"
|
||||
echo "------------------------"
|
||||
echo "2023-10-14 v1.8.6"
|
||||
echo "面板工具增加了测速流量监控面板的安装"
|
||||
echo "------------------------"
|
||||
echo "2023-10-16 v1.8.7"
|
||||
echo "系统工具中添加开启BBR3加速功能"
|
||||
echo "------------------------"
|
||||
echo "2023-10-18 v1.8.8"
|
||||
echo "系统工具中优化BBR3加速安装流程,可根据CPU型号自行安装适合的内核版本"
|
||||
echo "------------------------"
|
||||
echo "2023-10-19 v1.8.9"
|
||||
echo "系统工具中BBRv3功能增加了更新内核和卸载内核功能"
|
||||
echo "------------------------"
|
||||
echo "2023-10-21 v1.9"
|
||||
echo "开放端口相关优化"
|
||||
echo "解决部分系统SSH端口切换后重启失联的问题"
|
||||
echo "------------------------"
|
||||
echo "2023-10-26 v1.9.1"
|
||||
echo "LNMP建站管理中添加了站点缓存清理功能"
|
||||
echo "面板工具中卸载对应应用时添加了应用目录一并删除,删除更彻底!"
|
||||
echo "------------------------"
|
||||
echo "2023-10-28 v1.9.2"
|
||||
echo "系统工具中修复了虚拟内存大小重启后还原的问题"
|
||||
echo "------------------------"
|
||||
echo "2023-11-07 v1.9.3"
|
||||
echo "面板工具中增加AdGuardHome去广告软件安装和管理"
|
||||
echo "------------------------"
|
||||
echo "2023-11-08 v1.9.4"
|
||||
echo "系统工具添加了防火墙高级管理功能,可以开关端口,可以IP黑白名单"
|
||||
echo "未来会上线地域黑白名单等高级功能"
|
||||
echo "------------------------"
|
||||
echo "2023-11-09 v1.9.5"
|
||||
echo "系统工具中防火墙添加udp控制"
|
||||
echo "------------------------"
|
||||
echo "2023-11-10 v1.9.6"
|
||||
echo "测试脚本合集增加了缝合怪一条龙测试"
|
||||
echo "系统信息查询中添加了系统运行时长显示"
|
||||
echo "------------------------"
|
||||
echo "2023-11-10 v1.9.7"
|
||||
echo "LDNMP建站增加typecho轻量博客的搭建"
|
||||
echo "------------------------"
|
||||
echo "2023-11-16 v1.9.8"
|
||||
echo "面板工具中增加了在线office办公软件安装"
|
||||
echo "------------------------"
|
||||
echo "2023-11-21 v1.9.9"
|
||||
echo "面板工具中增加了雷池WAF防火墙程序安装"
|
||||
echo "------------------------"
|
||||
echo "2023-11-28 v2.0"
|
||||
echo "LDNMP建站中增加仅安装nginx的选项专门服务于站点重定向和站点反向代理"
|
||||
echo "精简无用的代码,优化执行效率"
|
||||
echo "------------------------"
|
||||
echo "2023-11-29 v2.0.1"
|
||||
echo "LDNMP建站改用cerbot申请证书,更稳定更快速。弃用acme"
|
||||
echo "------------------------"
|
||||
echo "2023-11-30 v2.0.2"
|
||||
echo "面板工具修复QB无法登录问题"
|
||||
echo "面板工具修复RocketChat进入后无限加载问题"
|
||||
echo "系统工具中添加修改主机名功能"
|
||||
echo "系统工具中添加服务器重启功能"
|
||||
echo "------------------------"
|
||||
echo "2023-12-04 v2.0.3"
|
||||
echo "LDNMP建站过程中增加了nginx自我检测修复功能"
|
||||
echo "系统工具添加更新源切换功能,请先在测试环境使用"
|
||||
echo "LDNMP建站增加自定义上传静态html界面功能"
|
||||
echo "------------------------"
|
||||
echo "2023-12-05 v2.0.4"
|
||||
echo "LDNMP建站中仅安装nginx功能添加安装成功提示,更优雅直观"
|
||||
echo "LDNMP建站中仅安装nginx功能支持自动更新nginx版本"
|
||||
echo "优化代码细节,定义调用函数,脚本执行更简洁,提升效率"
|
||||
echo "------------------------"
|
||||
echo "2023-12-07 v2.0.5"
|
||||
echo "LDNMP在站点数据管理中增加查看站点分析报告功能,可以对网站流量进行监控与分析"
|
||||
echo "主菜单添加手动更新脚本功能"
|
||||
echo "------------------------"
|
||||
echo "2023-12-08 v2.0.6"
|
||||
echo "主菜单中更新日志和脚本更新合并,更新时可以看到更新日志,更直觉"
|
||||
echo "面板工具中新增了docker管理面板portainer的安装"
|
||||
echo "面板工具中新增了VScode网页版的安装"
|
||||
echo "------------------------"
|
||||
echo "2023-12-15 v2.0.7"
|
||||
echo "系统工具中添加了定时任务的管理功能"
|
||||
echo "------------------------"
|
||||
echo "2023-12-16 v2.0.8"
|
||||
echo "大量安装软件的代码整合更简单快速安装各类软件包"
|
||||
echo "优化选项4中的常用工具安装及使用体验,已安装可以提示使用方法"
|
||||
echo "选项4中新增多款实用工具,如btop现代化监控工具,安装即用,q退出"
|
||||
echo "------------------------"
|
||||
echo "2023-12-18 v2.0.9"
|
||||
echo "优化安装代码,全局调用,安装智能检测,安装软件更快"
|
||||
echo "------------------------"
|
||||
echo "2023-12-19 v2.1"
|
||||
echo "选项4中,新增自定义安装卸载指定的工具"
|
||||
echo "优化了清理逻辑,清理系统更快更干净"
|
||||
echo "优化了卸载软件包的逻辑,根据系统执行卸载,更智能"
|
||||
echo "优化主菜单到二级菜单的交互,二级菜单更沉浸,二级菜单箭头引导调整"
|
||||
echo "------------------------"
|
||||
echo "2023-12-20 v2.1.1"
|
||||
echo "史诗级代码精简,屎山大扫除,脚本运行更快速,脚本大小缩减20%"
|
||||
echo "LDNMP安装环境时增加端口检测功能,端口被占用会无法安装"
|
||||
echo "面板工具中添加Uptime Kuma监控工具的安装"
|
||||
echo "面板工具中添加Memos网页备忘录的安装"
|
||||
echo "------------------------"
|
||||
echo "2023-12-23 v2.1.2"
|
||||
echo "面板工具中添加潘多拉GPT镜像站安装"
|
||||
echo "------------------------"
|
||||
echo "2023-12-26 v2.1.3"
|
||||
echo "选项4常用工具中添加跑火车屏保,俄罗斯方块,贪吃蛇,太空入侵者三款小游戏"
|
||||
echo "------------------------"
|
||||
echo "2023-12-30 v2.1.4"
|
||||
echo "LDNMP增加了防止源站IP泄露机制,保护源站IP与证书潜在安全隐患"
|
||||
echo "------------------------"
|
||||
echo "2024-01-04 v2.1.5"
|
||||
echo "脚本添加了启动快捷键,命令行输入k可以快速启动科技lion脚本工具"
|
||||
echo "------------------------"
|
||||
echo "2024-01-04 v2.1.6"
|
||||
echo "脚本添加了启动快捷键,命令行输入k可以快速启动科技lion脚本工具"
|
||||
echo "面板工具1panel增加了已安装状态,支持查看面板信息修改用户密码,支持卸载面板"
|
||||
echo "------------------------"
|
||||
echo "2024-01-06 v2.1.7"
|
||||
echo "面板工具增加了nextcloud网盘的搭建"
|
||||
echo "------------------------"
|
||||
echo "2024-01-09 v2.1.8"
|
||||
echo "LDNMP建站增加对ipv6的建站支持,解析v6地址建站据说提升站点安全性,性能也有提升!"
|
||||
echo "------------------------"
|
||||
echo "2024-01-10 v2.1.9"
|
||||
echo "面板工具增加QD-Today定时任务管理框架的安装"
|
||||
echo "------------------------"
|
||||
echo "2024-01-12 v2.2"
|
||||
echo "面板工具增加了Dockge容器堆栈管理面板的安装"
|
||||
echo "面板工具增加了LibreSpeed轻量级测速工具的安装"
|
||||
echo "优化了脚本快捷启动,输入k快速启动脚本支持任何目录下使用"
|
||||
echo "------------------------"
|
||||
echo "2024-01-16 v2.2.1"
|
||||
echo "主菜单添加14选项,VPS集群控制系统,可以一键操控所有VPS执行任务。"
|
||||
echo "VPS集群控制属于测试版本,请用闲置机器开始体验,有任何问题欢迎留言反馈"
|
||||
echo "------------------------"
|
||||
echo "2024-01-17 v2.2.2"
|
||||
echo "面板工具增加了搜索聚合网站的安装"
|
||||
echo "优化了集群控制体验,集群环境备份还原卸载等功能上线"
|
||||
echo "------------------------"
|
||||
echo "2024-01-18 v2.2.3"
|
||||
echo "面板工具增加了私有相册系统的安装"
|
||||
echo "------------------------"
|
||||
echo "2024-01-21 v2.2.4"
|
||||
echo "面板工具增加了PDF工具大全应用的安装"
|
||||
echo "------------------------"
|
||||
echo "2024-01-23 v2.2.5"
|
||||
echo "优化了LDNMP建站配置时进度条体验,让读条不至于卡在一个地方很久。拆分配置环节让体验更加顺畅"
|
||||
echo "------------------------"
|
||||
echo "2024-01-25 v2.2.6"
|
||||
echo "精简了LDNMP建站镜像大小,采用官方alpine精简镜像包,更快,更轻,更安全"
|
||||
echo "脚本适配alpine系统"
|
||||
echo "系统工具中重装系统选项升级,增加了Debian11 Debian10 ubuntu22.04 centos7 alpine3.19 windows11的安装选项"
|
||||
echo "------------------------"
|
||||
echo "2024-01-30 v2.2.7"
|
||||
echo "LDNMP建站更新halo2的安装版本到2.11"
|
||||
echo "修复alpine系统下虚拟内存重启后失效的问题"
|
||||
echo "优化alpine系统下docker安装体验,安装docker应用时自动识别安装docker环境"
|
||||
echo "修复alpine系统下CPU占用显示异常的问题"
|
||||
echo "------------------------"
|
||||
echo "2024-02-1 v2.2.8"
|
||||
echo "主菜单临时增加p选项,与幻兽帕鲁开服脚本联动"
|
||||
echo "------------------------"
|
||||
echo "2024-02-5 v2.2.9"
|
||||
echo "修改主机名支持alpine系统"
|
||||
echo "------------------------"
|
||||
echo "2024-02-8 v2.3"
|
||||
echo "面板工具增加了drawio在线绘图工具的安装"
|
||||
echo "------------------------"
|
||||
echo "2024-02-21 v2.3.1"
|
||||
echo "主菜单选项12我的工作区增加至10个,更利于多线程后台任务"
|
||||
echo "------------------------"
|
||||
echo "2024-02-26 v2.3.2"
|
||||
echo "系统工具中的选项8中一键重装系统的体验进行优化,重装时展示系统重装后的用户名密码和端口号"
|
||||
echo "一键重装系统增加了更多Windows版本重装 11 10 2019 2022"
|
||||
echo "一键重装系统增加了更多版本重装"
|
||||
echo "一键重装Windows系统默认为中文版了"
|
||||
echo "主菜单选项5的BBR管理适配了alpine的新界面"
|
||||
echo "------------------------"
|
||||
echo "2024-03-06 v2.3.3"
|
||||
echo "系统工具中新增了host解析设置功能"
|
||||
echo "优化了alpine系统的主机名修改逻辑"
|
||||
echo "------------------------"
|
||||
echo "2024-03-11 v2.3.4"
|
||||
echo "系统工具中新增fail2banSSH防御程序,防止你的SSH被暴力破解"
|
||||
echo "------------------------"
|
||||
echo "2024-03-20 v2.3.5"
|
||||
echo "面板工具加入了PVE开小鸡的面板,感谢oneclickvirt大佬的一键安装脚本"
|
||||
echo "------------------------"
|
||||
echo "2024-03-29 v2.3.6"
|
||||
echo "LDNMP安装环境时提前设置1G虚拟内存,提升建站环境安装速度和稳定性"
|
||||
echo "------------------------"
|
||||
echo "2024-04-01 v2.3.7"
|
||||
echo "LDNMP改进了防御能力,可以拦截404攻击,守护网站安全。脚本进入选择10再选择35站点防御,防御原来这么简单!"
|
||||
echo "------------------------"
|
||||
echo "2024-04-02 v2.3.8"
|
||||
echo "LDNMP站点防御接入cf,添加了cloudflare模式,添加了参数配置选项"
|
||||
echo "------------------------"
|
||||
echo "2024-04-07 v2.3.9"
|
||||
echo "LDNMP申请域名证书,解决证书链不完整的问题,谢谢wuying2021分支提供思路,已整合至主线版本"
|
||||
echo "------------------------"
|
||||
echo "2024-04-12 v2.4"
|
||||
echo "面板工具中添加了24选项 webtop远程桌面程序,alpine中文可视化桌面系统,很好用!"
|
||||
echo "------------------------"
|
||||
echo "2024-04-14 v2.4.1"
|
||||
echo "面板工具中添加Sun-Panel导航面板的安装"
|
||||
echo "LDNMP建站中halo镜像版本更新到最新版本"
|
||||
echo "测试脚本合集中追加了两项,nxtrace快速回程测试脚本 nxtrace指定IP回程测试脚本"
|
||||
echo "测试脚本合集中追加了两项,ludashi2020的三网线路测试"
|
||||
echo "测试脚本合集界面重构,分类更清晰,方便未来扩展使用"
|
||||
echo "------------------------"
|
||||
echo "2024-04-18 v2.4.2"
|
||||
echo "使用docker容器方式部署fail2ban防暴力破解程序,ssh和nginx都能防御"
|
||||
echo "优化了重启服务器的逻辑,增加了确认提示。"
|
||||
echo "增加了dnf包管理的适配"
|
||||
echo "安装BBRV3,cpu测速时将自动分配1024M的虚拟内存。"
|
||||
echo "------------------------"
|
||||
echo "2024-04-23 v2.4.3"
|
||||
echo "面板工具中添加了34选项,一个文件共享平台,可以传文件传图片,做分享链接用"
|
||||
echo "------------------------"
|
||||
echo "2024-04-26 v2.4.4"
|
||||
echo "面板工具中添加了33选项,一个极简朋友圈网页程序,高仿微信朋友圈!"
|
||||
echo "------------------------"
|
||||
echo "2024-04-29 v2.4.5"
|
||||
echo "系统工具中添加了限流关机功能,到达限定流量后自动关机,针对小流量怕反撸的机型而设计!"
|
||||
echo "------------------------"
|
||||
echo "2024-04-30 v2.4.6"
|
||||
echo "LDNMP建站分类调整,将不需要安装PHP就能搭建的应用移至nginx区块中,"
|
||||
echo "LDNMP建站如果没装环境直接部署网站会弹出检测提示,要求先装环境再建站"
|
||||
echo "缩小脚本体积,降了5kb,整合了一些老大难代码,模块化更顺畅小巧"
|
||||
echo "------------------------"
|
||||
echo "2024-05-04 v2.4.7"
|
||||
echo "面板工具新增36选项,加入了对AI聚合聊天网站的安装"
|
||||
echo "------------------------"
|
||||
echo "2024-05-05 v2.4.8"
|
||||
echo "LDNMP增加了自定义PHP动态站点功能,你可以上传自己的PHP项目"
|
||||
echo "LDNMP站点管理中,增加了编辑查看全局和站点配置"
|
||||
echo "------------------------"
|
||||
echo "2024-05-09 v2.4.9"
|
||||
echo "LDNMP静态站点动态站点自定义搭建优化,支持远程下载源码,手动上传源码"
|
||||
echo "修改主机名大小写Y的支持"
|
||||
echo "检测脚本添加了xykt大佬的IP质量体检脚本,非常美观实用的脚本"
|
||||
echo "------------------------"
|
||||
echo "2024-05-09 v2.4.10"
|
||||
echo "更新脚本逻辑进行优化,可选择是否更新,并且显示当前和最新的版本号,更智能!"
|
||||
echo "------------------------"
|
||||
echo "2024-05-09 v2.5"
|
||||
echo "重构并定义脚本中出现的红绿蓝黄灰白文字颜色,创建未来统一灵活调用文字颜色"
|
||||
echo "面板工具中添加了MYIP工具箱面板,可以查看当前使用的IP信息与状态"
|
||||
echo "------------------------"
|
||||
echo "2024-05-11 v2.5.1"
|
||||
echo "对docker环境检测进行优化,应用部署将会更稳定"
|
||||
echo "对一些提示的文字颜色进行了优化,对一些警示文字进行红色黄色标注"
|
||||
echo "openclaw的备份还原功能新增"
|
||||
echo "------------------------"
|
||||
|
||||
|
||||
164
upgrade_openssh9.8p1.sh
Normal file
164
upgrade_openssh9.8p1.sh
Normal file
@@ -0,0 +1,164 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 设置OpenSSH的版本号
|
||||
OPENSSH_VERSION=$(curl -s https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/ | grep -oP 'openssh-\K[0-9]+\.[0-9]+p[0-9]+' | sort -V | tail -n 1)
|
||||
|
||||
|
||||
# 检测系统类型
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
OS=$ID
|
||||
else
|
||||
echo "无法检测操作系统类型。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 等待并检查锁文件
|
||||
wait_for_lock() {
|
||||
while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do
|
||||
echo "等待dpkg锁释放..."
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
# 修复dpkg中断问题
|
||||
fix_dpkg() {
|
||||
DEBIAN_FRONTEND=noninteractive dpkg --configure -a
|
||||
}
|
||||
|
||||
# 安装依赖包
|
||||
install_dependencies() {
|
||||
case $OS in
|
||||
ubuntu|debian)
|
||||
wait_for_lock
|
||||
fix_dpkg
|
||||
DEBIAN_FRONTEND=noninteractive apt update
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y build-essential zlib1g-dev libssl-dev libpam0g-dev wget ntpdate -o Dpkg::Options::="--force-confnew"
|
||||
;;
|
||||
centos|rhel|almalinux|rocky|fedora)
|
||||
yum install -y epel-release
|
||||
yum groupinstall -y "Development Tools"
|
||||
yum install -y zlib-devel openssl-devel pam-devel wget ntpdate
|
||||
;;
|
||||
alpine)
|
||||
apk add build-base zlib-dev openssl-dev pam-dev wget ntpdate
|
||||
;;
|
||||
*)
|
||||
echo "不支持的操作系统:$OS"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
# 下载、编译和安装OpenSSH
|
||||
install_openssh() {
|
||||
wget --no-check-certificate https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-${OPENSSH_VERSION}.tar.gz
|
||||
|
||||
# 解压最新的 .tar.gz 文件
|
||||
tar -xzf openssh-*.tar.gz
|
||||
|
||||
# 获取解压出来的目录名并进入(自动适配)
|
||||
DIR_NAME=$(tar -tzf openssh-*.tar.gz | head -1 | cut -f1 -d"/")
|
||||
cd "$DIR_NAME"
|
||||
|
||||
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
}
|
||||
|
||||
# 重启SSH服务
|
||||
restart_ssh() {
|
||||
mv /usr/bin/ssh /usr/bin/ssh.bak
|
||||
ln -s /usr/local/bin/ssh /usr/bin/ssh
|
||||
case $OS in
|
||||
ubuntu|debian)
|
||||
systemctl restart ssh
|
||||
;;
|
||||
centos|rhel|almalinux|rocky|fedora)
|
||||
systemctl restart sshd
|
||||
;;
|
||||
alpine)
|
||||
rc-service sshd restart
|
||||
;;
|
||||
*)
|
||||
echo "不支持的操作系统:$OS"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 设置路径优先级
|
||||
set_path_priority() {
|
||||
NEW_SSH_PATH=$(which sshd) # 假设新版本的sshd和ssh在同一个目录
|
||||
NEW_SSH_DIR=$(dirname "$NEW_SSH_PATH")
|
||||
|
||||
if [[ ":$PATH:" != *":$NEW_SSH_DIR:"* ]]; then
|
||||
export PATH="$NEW_SSH_DIR:$PATH"
|
||||
echo "export PATH=\"$NEW_SSH_DIR:\$PATH\"" >> ~/.bashrc
|
||||
fi
|
||||
}
|
||||
|
||||
# 验证更新
|
||||
verify_installation() {
|
||||
echo "SSH版本信息:"
|
||||
ssh -V
|
||||
sshd -V
|
||||
}
|
||||
|
||||
# 清理下载的文件
|
||||
clean_up() {
|
||||
cd ..
|
||||
rm -rf openssh-${OPENSSH_VERSION}*
|
||||
}
|
||||
|
||||
|
||||
# 标题
|
||||
check_openssh_test() {
|
||||
echo "SSH高危漏洞修复工具"
|
||||
echo "视频介绍: https://www.bilibili.com/video/BV1dm421G7dy?t=0.1"
|
||||
echo "--------------------------"
|
||||
}
|
||||
|
||||
# 检查OpenSSH版本
|
||||
check_openssh_version() {
|
||||
current_version=$(ssh -V 2>&1 | awk '{print $1}' | cut -d_ -f2 | cut -d'p' -f1)
|
||||
|
||||
# 版本范围
|
||||
min_version=8.5
|
||||
max_version=9.8
|
||||
|
||||
if awk -v ver="$current_version" -v min="$min_version" -v max="$max_version" 'BEGIN{if(ver>=min && ver<=max) exit 0; else exit 1}'; then
|
||||
check_openssh_test
|
||||
echo "SSH版本: $current_version 在8.5到9.8之间,需要修复。"
|
||||
read -p "确定继续吗?(Y/N): " choice
|
||||
case "$choice" in
|
||||
[Yy])
|
||||
install_dependencies
|
||||
install_openssh
|
||||
restart_ssh
|
||||
set_path_priority
|
||||
verify_installation
|
||||
clean_up
|
||||
|
||||
;;
|
||||
[Nn])
|
||||
echo "已取消"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
echo "无效的选择,请输入 Y 或 N。"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
else
|
||||
check_openssh_test
|
||||
echo "SSH版本: $current_version 不在8.5到9.8之间,无需修复。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
|
||||
check_openssh_version
|
||||
31
valkey.conf
Normal file
31
valkey.conf
Normal file
@@ -0,0 +1,31 @@
|
||||
# 基础设置
|
||||
bind 0.0.0.0
|
||||
protected-mode yes
|
||||
port 6379
|
||||
timeout 300
|
||||
tcp-keepalive 300
|
||||
|
||||
# 性能优化
|
||||
io-threads 4
|
||||
io-threads-do-reads yes
|
||||
databases 1
|
||||
maxclients 10000
|
||||
|
||||
# 内存管理
|
||||
maxmemory 2gb
|
||||
maxmemory-policy allkeys-lru
|
||||
|
||||
# 禁用持久化
|
||||
save ""
|
||||
appendonly no
|
||||
rdbcompression no
|
||||
rdbchecksum no
|
||||
|
||||
# 日志设置
|
||||
loglevel warning
|
||||
logfile ""
|
||||
|
||||
# 异步删除优化
|
||||
lazyfree-lazy-eviction yes
|
||||
lazyfree-lazy-expire yes
|
||||
lazyfree-lazy-server-del yes
|
||||
490
www-1.conf
Normal file
490
www-1.conf
Normal file
@@ -0,0 +1,490 @@
|
||||
; Start a new pool named 'www'.
|
||||
; the variable $pool can be used in any directive and will be replaced by the
|
||||
; pool name ('www' here)
|
||||
[www]
|
||||
|
||||
; Per pool prefix
|
||||
; It only applies on the following directives:
|
||||
; - 'access.log'
|
||||
; - 'slowlog'
|
||||
; - 'listen' (unixsocket)
|
||||
; - 'chroot'
|
||||
; - 'chdir'
|
||||
; - 'php_values'
|
||||
; - 'php_admin_values'
|
||||
; When not set, the global prefix (or NONE) applies instead.
|
||||
; Note: This directive can also be relative to the global prefix.
|
||||
; Default Value: none
|
||||
;prefix = /path/to/pools/$pool
|
||||
|
||||
; Unix user/group of the child processes. This can be used only if the master
|
||||
; process running user is root. It is set after the child process is created.
|
||||
; The user and group can be specified either by their name or by their numeric
|
||||
; IDs.
|
||||
; Note: If the user is root, the executable needs to be started with
|
||||
; --allow-to-run-as-root option to work.
|
||||
; Default Values: The user is set to master process running user by default.
|
||||
; If the group is not set, the user's group is used.
|
||||
user = www-data
|
||||
group = www-data
|
||||
|
||||
; The address on which to accept FastCGI requests.
|
||||
; Valid syntaxes are:
|
||||
; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on
|
||||
; a specific port;
|
||||
; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
|
||||
; a specific port;
|
||||
; 'port' - to listen on a TCP socket to all addresses
|
||||
; (IPv6 and IPv4-mapped) on a specific port;
|
||||
; '/path/to/unix/socket' - to listen on a unix socket.
|
||||
; Note: This value is mandatory.
|
||||
listen = 127.0.0.1:9000
|
||||
|
||||
; Set listen(2) backlog.
|
||||
; Default Value: 511 (-1 on Linux, FreeBSD and OpenBSD)
|
||||
;listen.backlog = 511
|
||||
|
||||
; Set permissions for unix socket, if one is used. In Linux, read/write
|
||||
; permissions must be set in order to allow connections from a web server. Many
|
||||
; BSD-derived systems allow connections regardless of permissions. The owner
|
||||
; and group can be specified either by name or by their numeric IDs.
|
||||
; Default Values: Owner is set to the master process running user. If the group
|
||||
; is not set, the owner's group is used. Mode is set to 0660.
|
||||
;listen.owner = www-data
|
||||
;listen.group = www-data
|
||||
;listen.mode = 0660
|
||||
|
||||
; When POSIX Access Control Lists are supported you can set them using
|
||||
; these options, value is a comma separated list of user/group names.
|
||||
; When set, listen.owner and listen.group are ignored
|
||||
;listen.acl_users =
|
||||
;listen.acl_groups =
|
||||
|
||||
; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect.
|
||||
; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
|
||||
; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
|
||||
; must be separated by a comma. If this value is left blank, connections will be
|
||||
; accepted from any ip address.
|
||||
; Default Value: any
|
||||
;listen.allowed_clients = 127.0.0.1
|
||||
|
||||
; Set the associated the route table (FIB). FreeBSD only
|
||||
; Default Value: -1
|
||||
;listen.setfib = 1
|
||||
|
||||
; Specify the nice(2) priority to apply to the pool processes (only if set)
|
||||
; The value can vary from -19 (highest priority) to 20 (lower priority)
|
||||
; Note: - It will only work if the FPM master process is launched as root
|
||||
; - The pool processes will inherit the master process priority
|
||||
; unless it specified otherwise
|
||||
; Default Value: no set
|
||||
; process.priority = -19
|
||||
|
||||
; Set the process dumpable flag (PR_SET_DUMPABLE prctl for Linux or
|
||||
; PROC_TRACE_CTL procctl for FreeBSD) even if the process user
|
||||
; or group is different than the master process user. It allows to create process
|
||||
; core dump and ptrace the process for the pool user.
|
||||
; Default Value: no
|
||||
; process.dumpable = yes
|
||||
|
||||
; Choose how the process manager will control the number of child processes.
|
||||
; Possible Values:
|
||||
; static - a fixed number (pm.max_children) of child processes;
|
||||
; dynamic - the number of child processes are set dynamically based on the
|
||||
; following directives. With this process management, there will be
|
||||
; always at least 1 children.
|
||||
; pm.max_children - the maximum number of children that can
|
||||
; be alive at the same time.
|
||||
; pm.start_servers - the number of children created on startup.
|
||||
; pm.min_spare_servers - the minimum number of children in 'idle'
|
||||
; state (waiting to process). If the number
|
||||
; of 'idle' processes is less than this
|
||||
; number then some children will be created.
|
||||
; pm.max_spare_servers - the maximum number of children in 'idle'
|
||||
; state (waiting to process). If the number
|
||||
; of 'idle' processes is greater than this
|
||||
; number then some children will be killed.
|
||||
; pm.max_spawn_rate - the maximum number of rate to spawn child
|
||||
; processes at once.
|
||||
; ondemand - no children are created at startup. Children will be forked when
|
||||
; new requests will connect. The following parameter are used:
|
||||
; pm.max_children - the maximum number of children that
|
||||
; can be alive at the same time.
|
||||
; pm.process_idle_timeout - The number of seconds after which
|
||||
; an idle process will be killed.
|
||||
; Note: This value is mandatory.
|
||||
pm = dynamic
|
||||
|
||||
; The number of child processes to be created when pm is set to 'static' and the
|
||||
; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
|
||||
; This value sets the limit on the number of simultaneous requests that will be
|
||||
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
|
||||
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
|
||||
; CGI. The below defaults are based on a server without much resources. Don't
|
||||
; forget to tweak pm.* to fit your needs.
|
||||
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
|
||||
; Note: This value is mandatory.
|
||||
pm.max_children = 10
|
||||
|
||||
; The number of child processes created on startup.
|
||||
; Note: Used only when pm is set to 'dynamic'
|
||||
; Default Value: (min_spare_servers + max_spare_servers) / 2
|
||||
pm.start_servers = 3
|
||||
|
||||
; The desired minimum number of idle server processes.
|
||||
; Note: Used only when pm is set to 'dynamic'
|
||||
; Note: Mandatory when pm is set to 'dynamic'
|
||||
pm.min_spare_servers = 1
|
||||
|
||||
; The desired maximum number of idle server processes.
|
||||
; Note: Used only when pm is set to 'dynamic'
|
||||
; Note: Mandatory when pm is set to 'dynamic'
|
||||
pm.max_spare_servers = 3
|
||||
|
||||
; The number of rate to spawn child processes at once.
|
||||
; Note: Used only when pm is set to 'dynamic'
|
||||
; Note: Mandatory when pm is set to 'dynamic'
|
||||
; Default Value: 32
|
||||
;pm.max_spawn_rate = 32
|
||||
|
||||
; The number of seconds after which an idle process will be killed.
|
||||
; Note: Used only when pm is set to 'ondemand'
|
||||
; Default Value: 10s
|
||||
;pm.process_idle_timeout = 10s;
|
||||
|
||||
; The number of requests each child process should execute before respawning.
|
||||
; This can be useful to work around memory leaks in 3rd party libraries. For
|
||||
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
|
||||
; Default Value: 0
|
||||
pm.max_requests = 500
|
||||
|
||||
; The URI to view the FPM status page. If this value is not set, no URI will be
|
||||
; recognized as a status page. It shows the following information:
|
||||
; pool - the name of the pool;
|
||||
; process manager - static, dynamic or ondemand;
|
||||
; start time - the date and time FPM has started;
|
||||
; start since - number of seconds since FPM has started;
|
||||
; accepted conn - the number of request accepted by the pool;
|
||||
; listen queue - the number of request in the queue of pending
|
||||
; connections (see backlog in listen(2));
|
||||
; max listen queue - the maximum number of requests in the queue
|
||||
; of pending connections since FPM has started;
|
||||
; listen queue len - the size of the socket queue of pending connections;
|
||||
; idle processes - the number of idle processes;
|
||||
; active processes - the number of active processes;
|
||||
; total processes - the number of idle + active processes;
|
||||
; max active processes - the maximum number of active processes since FPM
|
||||
; has started;
|
||||
; max children reached - number of times, the process limit has been reached,
|
||||
; when pm tries to start more children (works only for
|
||||
; pm 'dynamic' and 'ondemand');
|
||||
; Value are updated in real time.
|
||||
; Example output:
|
||||
; pool: www
|
||||
; process manager: static
|
||||
; start time: 01/Jul/2011:17:53:49 +0200
|
||||
; start since: 62636
|
||||
; accepted conn: 190460
|
||||
; listen queue: 0
|
||||
; max listen queue: 1
|
||||
; listen queue len: 42
|
||||
; idle processes: 4
|
||||
; active processes: 11
|
||||
; total processes: 15
|
||||
; max active processes: 12
|
||||
; max children reached: 0
|
||||
;
|
||||
; By default the status page output is formatted as text/plain. Passing either
|
||||
; 'html', 'xml' or 'json' in the query string will return the corresponding
|
||||
; output syntax. Example:
|
||||
; http://www.foo.bar/status
|
||||
; http://www.foo.bar/status?json
|
||||
; http://www.foo.bar/status?html
|
||||
; http://www.foo.bar/status?xml
|
||||
;
|
||||
; By default the status page only outputs short status. Passing 'full' in the
|
||||
; query string will also return status for each pool process.
|
||||
; Example:
|
||||
; http://www.foo.bar/status?full
|
||||
; http://www.foo.bar/status?json&full
|
||||
; http://www.foo.bar/status?html&full
|
||||
; http://www.foo.bar/status?xml&full
|
||||
; The Full status returns for each process:
|
||||
; pid - the PID of the process;
|
||||
; state - the state of the process (Idle, Running, ...);
|
||||
; start time - the date and time the process has started;
|
||||
; start since - the number of seconds since the process has started;
|
||||
; requests - the number of requests the process has served;
|
||||
; request duration - the duration in µs of the requests;
|
||||
; request method - the request method (GET, POST, ...);
|
||||
; request URI - the request URI with the query string;
|
||||
; content length - the content length of the request (only with POST);
|
||||
; user - the user (PHP_AUTH_USER) (or '-' if not set);
|
||||
; script - the main script called (or '-' if not set);
|
||||
; last request cpu - the %cpu the last request consumed
|
||||
; it's always 0 if the process is not in Idle state
|
||||
; because CPU calculation is done when the request
|
||||
; processing has terminated;
|
||||
; last request memory - the max amount of memory the last request consumed
|
||||
; it's always 0 if the process is not in Idle state
|
||||
; because memory calculation is done when the request
|
||||
; processing has terminated;
|
||||
; If the process is in Idle state, then informations are related to the
|
||||
; last request the process has served. Otherwise informations are related to
|
||||
; the current request being served.
|
||||
; Example output:
|
||||
; ************************
|
||||
; pid: 31330
|
||||
; state: Running
|
||||
; start time: 01/Jul/2011:17:53:49 +0200
|
||||
; start since: 63087
|
||||
; requests: 12808
|
||||
; request duration: 1250261
|
||||
; request method: GET
|
||||
; request URI: /test_mem.php?N=10000
|
||||
; content length: 0
|
||||
; user: -
|
||||
; script: /home/fat/web/docs/php/test_mem.php
|
||||
; last request cpu: 0.00
|
||||
; last request memory: 0
|
||||
;
|
||||
; Note: There is a real-time FPM status monitoring sample web page available
|
||||
; It's available in: /usr/local/share/php/fpm/status.html
|
||||
;
|
||||
; Note: The value must start with a leading slash (/). The value can be
|
||||
; anything, but it may not be a good idea to use the .php extension or it
|
||||
; may conflict with a real PHP file.
|
||||
; Default Value: not set
|
||||
;pm.status_path = /status
|
||||
|
||||
; The address on which to accept FastCGI status request. This creates a new
|
||||
; invisible pool that can handle requests independently. This is useful
|
||||
; if the main pool is busy with long running requests because it is still possible
|
||||
; to get the status before finishing the long running requests.
|
||||
;
|
||||
; Valid syntaxes are:
|
||||
; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on
|
||||
; a specific port;
|
||||
; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
|
||||
; a specific port;
|
||||
; 'port' - to listen on a TCP socket to all addresses
|
||||
; (IPv6 and IPv4-mapped) on a specific port;
|
||||
; '/path/to/unix/socket' - to listen on a unix socket.
|
||||
; Default Value: value of the listen option
|
||||
;pm.status_listen = 127.0.0.1:9001
|
||||
|
||||
; The ping URI to call the monitoring page of FPM. If this value is not set, no
|
||||
; URI will be recognized as a ping page. This could be used to test from outside
|
||||
; that FPM is alive and responding, or to
|
||||
; - create a graph of FPM availability (rrd or such);
|
||||
; - remove a server from a group if it is not responding (load balancing);
|
||||
; - trigger alerts for the operating team (24/7).
|
||||
; Note: The value must start with a leading slash (/). The value can be
|
||||
; anything, but it may not be a good idea to use the .php extension or it
|
||||
; may conflict with a real PHP file.
|
||||
; Default Value: not set
|
||||
;ping.path = /ping
|
||||
|
||||
; This directive may be used to customize the response of a ping request. The
|
||||
; response is formatted as text/plain with a 200 response code.
|
||||
; Default Value: pong
|
||||
;ping.response = pong
|
||||
|
||||
; The access log file
|
||||
; Default: not set
|
||||
;access.log = log/$pool.access.log
|
||||
|
||||
; The access log format.
|
||||
; The following syntax is allowed
|
||||
; %%: the '%' character
|
||||
; %C: %CPU used by the request
|
||||
; it can accept the following format:
|
||||
; - %{user}C for user CPU only
|
||||
; - %{system}C for system CPU only
|
||||
; - %{total}C for user + system CPU (default)
|
||||
; %d: time taken to serve the request
|
||||
; it can accept the following format:
|
||||
; - %{seconds}d (default)
|
||||
; - %{milliseconds}d
|
||||
; - %{milli}d
|
||||
; - %{microseconds}d
|
||||
; - %{micro}d
|
||||
; %e: an environment variable (same as $_ENV or $_SERVER)
|
||||
; it must be associated with embraces to specify the name of the env
|
||||
; variable. Some examples:
|
||||
; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e
|
||||
; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e
|
||||
; %f: script filename
|
||||
; %l: content-length of the request (for POST request only)
|
||||
; %m: request method
|
||||
; %M: peak of memory allocated by PHP
|
||||
; it can accept the following format:
|
||||
; - %{bytes}M (default)
|
||||
; - %{kilobytes}M
|
||||
; - %{kilo}M
|
||||
; - %{megabytes}M
|
||||
; - %{mega}M
|
||||
; %n: pool name
|
||||
; %o: output header
|
||||
; it must be associated with embraces to specify the name of the header:
|
||||
; - %{Content-Type}o
|
||||
; - %{X-Powered-By}o
|
||||
; - %{Transfert-Encoding}o
|
||||
; - ....
|
||||
; %p: PID of the child that serviced the request
|
||||
; %P: PID of the parent of the child that serviced the request
|
||||
; %q: the query string
|
||||
; %Q: the '?' character if query string exists
|
||||
; %r: the request URI (without the query string, see %q and %Q)
|
||||
; %R: remote IP address
|
||||
; %s: status (response code)
|
||||
; %t: server time the request was received
|
||||
; it can accept a strftime(3) format:
|
||||
; %d/%b/%Y:%H:%M:%S %z (default)
|
||||
; The strftime(3) format must be encapsulated in a %{<strftime_format>}t tag
|
||||
; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
|
||||
; %T: time the log has been written (the request has finished)
|
||||
; it can accept a strftime(3) format:
|
||||
; %d/%b/%Y:%H:%M:%S %z (default)
|
||||
; The strftime(3) format must be encapsulated in a %{<strftime_format>}t tag
|
||||
; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
|
||||
; %u: remote user
|
||||
;
|
||||
; Default: "%R - %u %t \"%m %r\" %s"
|
||||
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{milli}d %{kilo}M %C%%"
|
||||
|
||||
; A list of request_uri values which should be filtered from the access log.
|
||||
;
|
||||
; As a security precuation, this setting will be ignored if:
|
||||
; - the request method is not GET or HEAD; or
|
||||
; - there is a request body; or
|
||||
; - there are query parameters; or
|
||||
; - the response code is outwith the successful range of 200 to 299
|
||||
;
|
||||
; Note: The paths are matched against the output of the access.format tag "%r".
|
||||
; On common configurations, this may look more like SCRIPT_NAME than the
|
||||
; expected pre-rewrite URI.
|
||||
;
|
||||
; Default Value: not set
|
||||
;access.suppress_path[] = /ping
|
||||
;access.suppress_path[] = /health_check.php
|
||||
|
||||
; The log file for slow requests
|
||||
; Default Value: not set
|
||||
; Note: slowlog is mandatory if request_slowlog_timeout is set
|
||||
;slowlog = log/$pool.log.slow
|
||||
|
||||
; The timeout for serving a single request after which a PHP backtrace will be
|
||||
; dumped to the 'slowlog' file. A value of '0s' means 'off'.
|
||||
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
|
||||
; Default Value: 0
|
||||
;request_slowlog_timeout = 0
|
||||
|
||||
; Depth of slow log stack trace.
|
||||
; Default Value: 20
|
||||
;request_slowlog_trace_depth = 20
|
||||
|
||||
; The timeout for serving a single request after which the worker process will
|
||||
; be killed. This option should be used when the 'max_execution_time' ini option
|
||||
; does not stop script execution for some reason. A value of '0' means 'off'.
|
||||
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
|
||||
; Default Value: 0
|
||||
request_terminate_timeout = 1600
|
||||
|
||||
; The timeout set by 'request_terminate_timeout' ini option is not engaged after
|
||||
; application calls 'fastcgi_finish_request' or when application has finished and
|
||||
; shutdown functions are being called (registered via register_shutdown_function).
|
||||
; This option will enable timeout limit to be applied unconditionally
|
||||
; even in such cases.
|
||||
; Default Value: no
|
||||
;request_terminate_timeout_track_finished = no
|
||||
|
||||
; Set open file descriptor rlimit.
|
||||
; Default Value: system defined value
|
||||
;rlimit_files = 1024
|
||||
|
||||
; Set max core size rlimit.
|
||||
; Possible Values: 'unlimited' or an integer greater or equal to 0
|
||||
; Default Value: system defined value
|
||||
;rlimit_core = 0
|
||||
|
||||
; Chroot to this directory at the start. This value must be defined as an
|
||||
; absolute path. When this value is not set, chroot is not used.
|
||||
; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
|
||||
; of its subdirectories. If the pool prefix is not set, the global prefix
|
||||
; will be used instead.
|
||||
; Note: chrooting is a great security feature and should be used whenever
|
||||
; possible. However, all PHP paths will be relative to the chroot
|
||||
; (error_log, sessions.save_path, ...).
|
||||
; Default Value: not set
|
||||
;chroot =
|
||||
|
||||
; Chdir to this directory at the start.
|
||||
; Note: relative path can be used.
|
||||
; Default Value: current directory or / when chroot
|
||||
;chdir = /var/www
|
||||
|
||||
; Redirect worker stdout and stderr into main error log. If not set, stdout and
|
||||
; stderr will be redirected to /dev/null according to FastCGI specs.
|
||||
; Note: on highloaded environment, this can cause some delay in the page
|
||||
; process time (several ms).
|
||||
; Default Value: no
|
||||
;catch_workers_output = yes
|
||||
|
||||
; Decorate worker output with prefix and suffix containing information about
|
||||
; the child that writes to the log and if stdout or stderr is used as well as
|
||||
; log level and time. This options is used only if catch_workers_output is yes.
|
||||
; Settings to "no" will output data as written to the stdout or stderr.
|
||||
; Default value: yes
|
||||
;decorate_workers_output = no
|
||||
|
||||
; Clear environment in FPM workers
|
||||
; Prevents arbitrary environment variables from reaching FPM worker processes
|
||||
; by clearing the environment in workers before env vars specified in this
|
||||
; pool configuration are added.
|
||||
; Setting to "no" will make all environment variables available to PHP code
|
||||
; via getenv(), $_ENV and $_SERVER.
|
||||
; Default Value: yes
|
||||
;clear_env = no
|
||||
|
||||
; Limits the extensions of the main script FPM will allow to parse. This can
|
||||
; prevent configuration mistakes on the web server side. You should only limit
|
||||
; FPM to .php extensions to prevent malicious users to use other extensions to
|
||||
; execute php code.
|
||||
; Note: set an empty value to allow all extensions.
|
||||
; Default Value: .php
|
||||
;security.limit_extensions = .php .php3 .php4 .php5 .php7
|
||||
|
||||
; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
|
||||
; the current environment.
|
||||
; Default Value: clean env
|
||||
;env[HOSTNAME] = $HOSTNAME
|
||||
;env[PATH] = /usr/local/bin:/usr/bin:/bin
|
||||
;env[TMP] = /tmp
|
||||
;env[TMPDIR] = /tmp
|
||||
;env[TEMP] = /tmp
|
||||
|
||||
; Additional php.ini defines, specific to this pool of workers. These settings
|
||||
; overwrite the values previously defined in the php.ini. The directives are the
|
||||
; same as the PHP SAPI:
|
||||
; php_value/php_flag - you can set classic ini defines which can
|
||||
; be overwritten from PHP call 'ini_set'.
|
||||
; php_admin_value/php_admin_flag - these directives won't be overwritten by
|
||||
; PHP call 'ini_set'
|
||||
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
|
||||
|
||||
; Defining 'extension' will load the corresponding shared extension from
|
||||
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
|
||||
; overwrite previously defined php.ini values, but will append the new value
|
||||
; instead.
|
||||
|
||||
; Note: path INI options can be relative and will be expanded with the prefix
|
||||
; (pool, global or /usr/local)
|
||||
|
||||
; Default Value: nothing is defined by default except the values in php.ini and
|
||||
; specified at startup with the -d argument
|
||||
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
|
||||
php_flag[display_errors] = off
|
||||
php_admin_value[error_log] = /var/log/fpm-php.www.log
|
||||
php_admin_flag[log_errors] = on
|
||||
;php_admin_value[memory_limit] = 32M
|
||||
490
www.conf
Normal file
490
www.conf
Normal file
@@ -0,0 +1,490 @@
|
||||
; Start a new pool named 'www'.
|
||||
; the variable $pool can be used in any directive and will be replaced by the
|
||||
; pool name ('www' here)
|
||||
[www]
|
||||
|
||||
; Per pool prefix
|
||||
; It only applies on the following directives:
|
||||
; - 'access.log'
|
||||
; - 'slowlog'
|
||||
; - 'listen' (unixsocket)
|
||||
; - 'chroot'
|
||||
; - 'chdir'
|
||||
; - 'php_values'
|
||||
; - 'php_admin_values'
|
||||
; When not set, the global prefix (or NONE) applies instead.
|
||||
; Note: This directive can also be relative to the global prefix.
|
||||
; Default Value: none
|
||||
;prefix = /path/to/pools/$pool
|
||||
|
||||
; Unix user/group of the child processes. This can be used only if the master
|
||||
; process running user is root. It is set after the child process is created.
|
||||
; The user and group can be specified either by their name or by their numeric
|
||||
; IDs.
|
||||
; Note: If the user is root, the executable needs to be started with
|
||||
; --allow-to-run-as-root option to work.
|
||||
; Default Values: The user is set to master process running user by default.
|
||||
; If the group is not set, the user's group is used.
|
||||
user = www-data
|
||||
group = www-data
|
||||
|
||||
; The address on which to accept FastCGI requests.
|
||||
; Valid syntaxes are:
|
||||
; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on
|
||||
; a specific port;
|
||||
; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
|
||||
; a specific port;
|
||||
; 'port' - to listen on a TCP socket to all addresses
|
||||
; (IPv6 and IPv4-mapped) on a specific port;
|
||||
; '/path/to/unix/socket' - to listen on a unix socket.
|
||||
; Note: This value is mandatory.
|
||||
listen = 127.0.0.1:9000
|
||||
|
||||
; Set listen(2) backlog.
|
||||
; Default Value: 511 (-1 on Linux, FreeBSD and OpenBSD)
|
||||
;listen.backlog = 511
|
||||
|
||||
; Set permissions for unix socket, if one is used. In Linux, read/write
|
||||
; permissions must be set in order to allow connections from a web server. Many
|
||||
; BSD-derived systems allow connections regardless of permissions. The owner
|
||||
; and group can be specified either by name or by their numeric IDs.
|
||||
; Default Values: Owner is set to the master process running user. If the group
|
||||
; is not set, the owner's group is used. Mode is set to 0660.
|
||||
;listen.owner = www-data
|
||||
;listen.group = www-data
|
||||
;listen.mode = 0660
|
||||
|
||||
; When POSIX Access Control Lists are supported you can set them using
|
||||
; these options, value is a comma separated list of user/group names.
|
||||
; When set, listen.owner and listen.group are ignored
|
||||
;listen.acl_users =
|
||||
;listen.acl_groups =
|
||||
|
||||
; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect.
|
||||
; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
|
||||
; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
|
||||
; must be separated by a comma. If this value is left blank, connections will be
|
||||
; accepted from any ip address.
|
||||
; Default Value: any
|
||||
;listen.allowed_clients = 127.0.0.1
|
||||
|
||||
; Set the associated the route table (FIB). FreeBSD only
|
||||
; Default Value: -1
|
||||
;listen.setfib = 1
|
||||
|
||||
; Specify the nice(2) priority to apply to the pool processes (only if set)
|
||||
; The value can vary from -19 (highest priority) to 20 (lower priority)
|
||||
; Note: - It will only work if the FPM master process is launched as root
|
||||
; - The pool processes will inherit the master process priority
|
||||
; unless it specified otherwise
|
||||
; Default Value: no set
|
||||
; process.priority = -19
|
||||
|
||||
; Set the process dumpable flag (PR_SET_DUMPABLE prctl for Linux or
|
||||
; PROC_TRACE_CTL procctl for FreeBSD) even if the process user
|
||||
; or group is different than the master process user. It allows to create process
|
||||
; core dump and ptrace the process for the pool user.
|
||||
; Default Value: no
|
||||
; process.dumpable = yes
|
||||
|
||||
; Choose how the process manager will control the number of child processes.
|
||||
; Possible Values:
|
||||
; static - a fixed number (pm.max_children) of child processes;
|
||||
; dynamic - the number of child processes are set dynamically based on the
|
||||
; following directives. With this process management, there will be
|
||||
; always at least 1 children.
|
||||
; pm.max_children - the maximum number of children that can
|
||||
; be alive at the same time.
|
||||
; pm.start_servers - the number of children created on startup.
|
||||
; pm.min_spare_servers - the minimum number of children in 'idle'
|
||||
; state (waiting to process). If the number
|
||||
; of 'idle' processes is less than this
|
||||
; number then some children will be created.
|
||||
; pm.max_spare_servers - the maximum number of children in 'idle'
|
||||
; state (waiting to process). If the number
|
||||
; of 'idle' processes is greater than this
|
||||
; number then some children will be killed.
|
||||
; pm.max_spawn_rate - the maximum number of rate to spawn child
|
||||
; processes at once.
|
||||
; ondemand - no children are created at startup. Children will be forked when
|
||||
; new requests will connect. The following parameter are used:
|
||||
; pm.max_children - the maximum number of children that
|
||||
; can be alive at the same time.
|
||||
; pm.process_idle_timeout - The number of seconds after which
|
||||
; an idle process will be killed.
|
||||
; Note: This value is mandatory.
|
||||
pm = dynamic
|
||||
|
||||
; The number of child processes to be created when pm is set to 'static' and the
|
||||
; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
|
||||
; This value sets the limit on the number of simultaneous requests that will be
|
||||
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
|
||||
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
|
||||
; CGI. The below defaults are based on a server without much resources. Don't
|
||||
; forget to tweak pm.* to fit your needs.
|
||||
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
|
||||
; Note: This value is mandatory.
|
||||
pm.max_children = 20
|
||||
|
||||
; The number of child processes created on startup.
|
||||
; Note: Used only when pm is set to 'dynamic'
|
||||
; Default Value: (min_spare_servers + max_spare_servers) / 2
|
||||
pm.start_servers = 5
|
||||
|
||||
; The desired minimum number of idle server processes.
|
||||
; Note: Used only when pm is set to 'dynamic'
|
||||
; Note: Mandatory when pm is set to 'dynamic'
|
||||
pm.min_spare_servers = 1
|
||||
|
||||
; The desired maximum number of idle server processes.
|
||||
; Note: Used only when pm is set to 'dynamic'
|
||||
; Note: Mandatory when pm is set to 'dynamic'
|
||||
pm.max_spare_servers = 5
|
||||
|
||||
; The number of rate to spawn child processes at once.
|
||||
; Note: Used only when pm is set to 'dynamic'
|
||||
; Note: Mandatory when pm is set to 'dynamic'
|
||||
; Default Value: 32
|
||||
;pm.max_spawn_rate = 32
|
||||
|
||||
; The number of seconds after which an idle process will be killed.
|
||||
; Note: Used only when pm is set to 'ondemand'
|
||||
; Default Value: 10s
|
||||
;pm.process_idle_timeout = 10s;
|
||||
|
||||
; The number of requests each child process should execute before respawning.
|
||||
; This can be useful to work around memory leaks in 3rd party libraries. For
|
||||
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
|
||||
; Default Value: 0
|
||||
pm.max_requests = 500
|
||||
|
||||
; The URI to view the FPM status page. If this value is not set, no URI will be
|
||||
; recognized as a status page. It shows the following information:
|
||||
; pool - the name of the pool;
|
||||
; process manager - static, dynamic or ondemand;
|
||||
; start time - the date and time FPM has started;
|
||||
; start since - number of seconds since FPM has started;
|
||||
; accepted conn - the number of request accepted by the pool;
|
||||
; listen queue - the number of request in the queue of pending
|
||||
; connections (see backlog in listen(2));
|
||||
; max listen queue - the maximum number of requests in the queue
|
||||
; of pending connections since FPM has started;
|
||||
; listen queue len - the size of the socket queue of pending connections;
|
||||
; idle processes - the number of idle processes;
|
||||
; active processes - the number of active processes;
|
||||
; total processes - the number of idle + active processes;
|
||||
; max active processes - the maximum number of active processes since FPM
|
||||
; has started;
|
||||
; max children reached - number of times, the process limit has been reached,
|
||||
; when pm tries to start more children (works only for
|
||||
; pm 'dynamic' and 'ondemand');
|
||||
; Value are updated in real time.
|
||||
; Example output:
|
||||
; pool: www
|
||||
; process manager: static
|
||||
; start time: 01/Jul/2011:17:53:49 +0200
|
||||
; start since: 62636
|
||||
; accepted conn: 190460
|
||||
; listen queue: 0
|
||||
; max listen queue: 1
|
||||
; listen queue len: 42
|
||||
; idle processes: 4
|
||||
; active processes: 11
|
||||
; total processes: 15
|
||||
; max active processes: 12
|
||||
; max children reached: 0
|
||||
;
|
||||
; By default the status page output is formatted as text/plain. Passing either
|
||||
; 'html', 'xml' or 'json' in the query string will return the corresponding
|
||||
; output syntax. Example:
|
||||
; http://www.foo.bar/status
|
||||
; http://www.foo.bar/status?json
|
||||
; http://www.foo.bar/status?html
|
||||
; http://www.foo.bar/status?xml
|
||||
;
|
||||
; By default the status page only outputs short status. Passing 'full' in the
|
||||
; query string will also return status for each pool process.
|
||||
; Example:
|
||||
; http://www.foo.bar/status?full
|
||||
; http://www.foo.bar/status?json&full
|
||||
; http://www.foo.bar/status?html&full
|
||||
; http://www.foo.bar/status?xml&full
|
||||
; The Full status returns for each process:
|
||||
; pid - the PID of the process;
|
||||
; state - the state of the process (Idle, Running, ...);
|
||||
; start time - the date and time the process has started;
|
||||
; start since - the number of seconds since the process has started;
|
||||
; requests - the number of requests the process has served;
|
||||
; request duration - the duration in µs of the requests;
|
||||
; request method - the request method (GET, POST, ...);
|
||||
; request URI - the request URI with the query string;
|
||||
; content length - the content length of the request (only with POST);
|
||||
; user - the user (PHP_AUTH_USER) (or '-' if not set);
|
||||
; script - the main script called (or '-' if not set);
|
||||
; last request cpu - the %cpu the last request consumed
|
||||
; it's always 0 if the process is not in Idle state
|
||||
; because CPU calculation is done when the request
|
||||
; processing has terminated;
|
||||
; last request memory - the max amount of memory the last request consumed
|
||||
; it's always 0 if the process is not in Idle state
|
||||
; because memory calculation is done when the request
|
||||
; processing has terminated;
|
||||
; If the process is in Idle state, then informations are related to the
|
||||
; last request the process has served. Otherwise informations are related to
|
||||
; the current request being served.
|
||||
; Example output:
|
||||
; ************************
|
||||
; pid: 31330
|
||||
; state: Running
|
||||
; start time: 01/Jul/2011:17:53:49 +0200
|
||||
; start since: 63087
|
||||
; requests: 12808
|
||||
; request duration: 1250261
|
||||
; request method: GET
|
||||
; request URI: /test_mem.php?N=10000
|
||||
; content length: 0
|
||||
; user: -
|
||||
; script: /home/fat/web/docs/php/test_mem.php
|
||||
; last request cpu: 0.00
|
||||
; last request memory: 0
|
||||
;
|
||||
; Note: There is a real-time FPM status monitoring sample web page available
|
||||
; It's available in: /usr/local/share/php/fpm/status.html
|
||||
;
|
||||
; Note: The value must start with a leading slash (/). The value can be
|
||||
; anything, but it may not be a good idea to use the .php extension or it
|
||||
; may conflict with a real PHP file.
|
||||
; Default Value: not set
|
||||
;pm.status_path = /status
|
||||
|
||||
; The address on which to accept FastCGI status request. This creates a new
|
||||
; invisible pool that can handle requests independently. This is useful
|
||||
; if the main pool is busy with long running requests because it is still possible
|
||||
; to get the status before finishing the long running requests.
|
||||
;
|
||||
; Valid syntaxes are:
|
||||
; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on
|
||||
; a specific port;
|
||||
; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
|
||||
; a specific port;
|
||||
; 'port' - to listen on a TCP socket to all addresses
|
||||
; (IPv6 and IPv4-mapped) on a specific port;
|
||||
; '/path/to/unix/socket' - to listen on a unix socket.
|
||||
; Default Value: value of the listen option
|
||||
;pm.status_listen = 127.0.0.1:9001
|
||||
|
||||
; The ping URI to call the monitoring page of FPM. If this value is not set, no
|
||||
; URI will be recognized as a ping page. This could be used to test from outside
|
||||
; that FPM is alive and responding, or to
|
||||
; - create a graph of FPM availability (rrd or such);
|
||||
; - remove a server from a group if it is not responding (load balancing);
|
||||
; - trigger alerts for the operating team (24/7).
|
||||
; Note: The value must start with a leading slash (/). The value can be
|
||||
; anything, but it may not be a good idea to use the .php extension or it
|
||||
; may conflict with a real PHP file.
|
||||
; Default Value: not set
|
||||
;ping.path = /ping
|
||||
|
||||
; This directive may be used to customize the response of a ping request. The
|
||||
; response is formatted as text/plain with a 200 response code.
|
||||
; Default Value: pong
|
||||
;ping.response = pong
|
||||
|
||||
; The access log file
|
||||
; Default: not set
|
||||
;access.log = log/$pool.access.log
|
||||
|
||||
; The access log format.
|
||||
; The following syntax is allowed
|
||||
; %%: the '%' character
|
||||
; %C: %CPU used by the request
|
||||
; it can accept the following format:
|
||||
; - %{user}C for user CPU only
|
||||
; - %{system}C for system CPU only
|
||||
; - %{total}C for user + system CPU (default)
|
||||
; %d: time taken to serve the request
|
||||
; it can accept the following format:
|
||||
; - %{seconds}d (default)
|
||||
; - %{milliseconds}d
|
||||
; - %{milli}d
|
||||
; - %{microseconds}d
|
||||
; - %{micro}d
|
||||
; %e: an environment variable (same as $_ENV or $_SERVER)
|
||||
; it must be associated with embraces to specify the name of the env
|
||||
; variable. Some examples:
|
||||
; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e
|
||||
; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e
|
||||
; %f: script filename
|
||||
; %l: content-length of the request (for POST request only)
|
||||
; %m: request method
|
||||
; %M: peak of memory allocated by PHP
|
||||
; it can accept the following format:
|
||||
; - %{bytes}M (default)
|
||||
; - %{kilobytes}M
|
||||
; - %{kilo}M
|
||||
; - %{megabytes}M
|
||||
; - %{mega}M
|
||||
; %n: pool name
|
||||
; %o: output header
|
||||
; it must be associated with embraces to specify the name of the header:
|
||||
; - %{Content-Type}o
|
||||
; - %{X-Powered-By}o
|
||||
; - %{Transfert-Encoding}o
|
||||
; - ....
|
||||
; %p: PID of the child that serviced the request
|
||||
; %P: PID of the parent of the child that serviced the request
|
||||
; %q: the query string
|
||||
; %Q: the '?' character if query string exists
|
||||
; %r: the request URI (without the query string, see %q and %Q)
|
||||
; %R: remote IP address
|
||||
; %s: status (response code)
|
||||
; %t: server time the request was received
|
||||
; it can accept a strftime(3) format:
|
||||
; %d/%b/%Y:%H:%M:%S %z (default)
|
||||
; The strftime(3) format must be encapsulated in a %{<strftime_format>}t tag
|
||||
; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
|
||||
; %T: time the log has been written (the request has finished)
|
||||
; it can accept a strftime(3) format:
|
||||
; %d/%b/%Y:%H:%M:%S %z (default)
|
||||
; The strftime(3) format must be encapsulated in a %{<strftime_format>}t tag
|
||||
; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
|
||||
; %u: remote user
|
||||
;
|
||||
; Default: "%R - %u %t \"%m %r\" %s"
|
||||
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{milli}d %{kilo}M %C%%"
|
||||
|
||||
; A list of request_uri values which should be filtered from the access log.
|
||||
;
|
||||
; As a security precuation, this setting will be ignored if:
|
||||
; - the request method is not GET or HEAD; or
|
||||
; - there is a request body; or
|
||||
; - there are query parameters; or
|
||||
; - the response code is outwith the successful range of 200 to 299
|
||||
;
|
||||
; Note: The paths are matched against the output of the access.format tag "%r".
|
||||
; On common configurations, this may look more like SCRIPT_NAME than the
|
||||
; expected pre-rewrite URI.
|
||||
;
|
||||
; Default Value: not set
|
||||
;access.suppress_path[] = /ping
|
||||
;access.suppress_path[] = /health_check.php
|
||||
|
||||
; The log file for slow requests
|
||||
; Default Value: not set
|
||||
; Note: slowlog is mandatory if request_slowlog_timeout is set
|
||||
;slowlog = log/$pool.log.slow
|
||||
|
||||
; The timeout for serving a single request after which a PHP backtrace will be
|
||||
; dumped to the 'slowlog' file. A value of '0s' means 'off'.
|
||||
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
|
||||
; Default Value: 0
|
||||
;request_slowlog_timeout = 0
|
||||
|
||||
; Depth of slow log stack trace.
|
||||
; Default Value: 20
|
||||
;request_slowlog_trace_depth = 20
|
||||
|
||||
; The timeout for serving a single request after which the worker process will
|
||||
; be killed. This option should be used when the 'max_execution_time' ini option
|
||||
; does not stop script execution for some reason. A value of '0' means 'off'.
|
||||
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
|
||||
; Default Value: 0
|
||||
request_terminate_timeout = 1600
|
||||
|
||||
; The timeout set by 'request_terminate_timeout' ini option is not engaged after
|
||||
; application calls 'fastcgi_finish_request' or when application has finished and
|
||||
; shutdown functions are being called (registered via register_shutdown_function).
|
||||
; This option will enable timeout limit to be applied unconditionally
|
||||
; even in such cases.
|
||||
; Default Value: no
|
||||
;request_terminate_timeout_track_finished = no
|
||||
|
||||
; Set open file descriptor rlimit.
|
||||
; Default Value: system defined value
|
||||
;rlimit_files = 1024
|
||||
|
||||
; Set max core size rlimit.
|
||||
; Possible Values: 'unlimited' or an integer greater or equal to 0
|
||||
; Default Value: system defined value
|
||||
;rlimit_core = 0
|
||||
|
||||
; Chroot to this directory at the start. This value must be defined as an
|
||||
; absolute path. When this value is not set, chroot is not used.
|
||||
; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
|
||||
; of its subdirectories. If the pool prefix is not set, the global prefix
|
||||
; will be used instead.
|
||||
; Note: chrooting is a great security feature and should be used whenever
|
||||
; possible. However, all PHP paths will be relative to the chroot
|
||||
; (error_log, sessions.save_path, ...).
|
||||
; Default Value: not set
|
||||
;chroot =
|
||||
|
||||
; Chdir to this directory at the start.
|
||||
; Note: relative path can be used.
|
||||
; Default Value: current directory or / when chroot
|
||||
;chdir = /var/www
|
||||
|
||||
; Redirect worker stdout and stderr into main error log. If not set, stdout and
|
||||
; stderr will be redirected to /dev/null according to FastCGI specs.
|
||||
; Note: on highloaded environment, this can cause some delay in the page
|
||||
; process time (several ms).
|
||||
; Default Value: no
|
||||
;catch_workers_output = yes
|
||||
|
||||
; Decorate worker output with prefix and suffix containing information about
|
||||
; the child that writes to the log and if stdout or stderr is used as well as
|
||||
; log level and time. This options is used only if catch_workers_output is yes.
|
||||
; Settings to "no" will output data as written to the stdout or stderr.
|
||||
; Default value: yes
|
||||
;decorate_workers_output = no
|
||||
|
||||
; Clear environment in FPM workers
|
||||
; Prevents arbitrary environment variables from reaching FPM worker processes
|
||||
; by clearing the environment in workers before env vars specified in this
|
||||
; pool configuration are added.
|
||||
; Setting to "no" will make all environment variables available to PHP code
|
||||
; via getenv(), $_ENV and $_SERVER.
|
||||
; Default Value: yes
|
||||
;clear_env = no
|
||||
|
||||
; Limits the extensions of the main script FPM will allow to parse. This can
|
||||
; prevent configuration mistakes on the web server side. You should only limit
|
||||
; FPM to .php extensions to prevent malicious users to use other extensions to
|
||||
; execute php code.
|
||||
; Note: set an empty value to allow all extensions.
|
||||
; Default Value: .php
|
||||
;security.limit_extensions = .php .php3 .php4 .php5 .php7
|
||||
|
||||
; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
|
||||
; the current environment.
|
||||
; Default Value: clean env
|
||||
;env[HOSTNAME] = $HOSTNAME
|
||||
;env[PATH] = /usr/local/bin:/usr/bin:/bin
|
||||
;env[TMP] = /tmp
|
||||
;env[TMPDIR] = /tmp
|
||||
;env[TEMP] = /tmp
|
||||
|
||||
; Additional php.ini defines, specific to this pool of workers. These settings
|
||||
; overwrite the values previously defined in the php.ini. The directives are the
|
||||
; same as the PHP SAPI:
|
||||
; php_value/php_flag - you can set classic ini defines which can
|
||||
; be overwritten from PHP call 'ini_set'.
|
||||
; php_admin_value/php_admin_flag - these directives won't be overwritten by
|
||||
; PHP call 'ini_set'
|
||||
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
|
||||
|
||||
; Defining 'extension' will load the corresponding shared extension from
|
||||
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
|
||||
; overwrite previously defined php.ini values, but will append the new value
|
||||
; instead.
|
||||
|
||||
; Note: path INI options can be relative and will be expanded with the prefix
|
||||
; (pool, global or /usr/local)
|
||||
|
||||
; Default Value: nothing is defined by default except the values in php.ini and
|
||||
; specified at startup with the -d argument
|
||||
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
|
||||
php_flag[display_errors] = off
|
||||
php_admin_value[error_log] = /var/log/fpm-php.www.log
|
||||
php_admin_flag[log_errors] = on
|
||||
;php_admin_value[memory_limit] = 32M
|
||||
Reference in New Issue
Block a user