提交 5ea0cab6 authored 作者: 陈泽健's avatar 陈泽健

feat(deploy): 添加电子桌牌三色四色选择功能,增加kkfile适配https需求

- 在部署脚本中新增 configure_tablecard_color 函数处理桌牌色彩配置
- 实现 jq/python3/awk 多工具降级链修改敏感配置文件中的色彩参数
- 在 cardtable_x86 函数中集成色彩配置步骤,支持交互式选择三色或四色模式
- 为非交互模式设置默认三色配置,确保自动部署流程不受影响
- 添加配置文件备份机制和完整的错误处理及日志记录
- 更新需求文档和计划执行文档,完善相关配置说明和测试计划
上级 93788b9b
...@@ -73,3 +73,4 @@ __pycache__/ ...@@ -73,3 +73,4 @@ __pycache__/
/AuxiliaryTool/DocumentAutoOptimizationCalibration/build/文档优化工具/文档优化工具.pkg /AuxiliaryTool/DocumentAutoOptimizationCalibration/build/文档优化工具/文档优化工具.pkg
/AuxiliaryTool/DocumentAutoOptimizationCalibration/build/文档优化工具/xref-文档优化工具.html /AuxiliaryTool/DocumentAutoOptimizationCalibration/build/文档优化工具/xref-文档优化工具.html
/AuxiliaryTool/DocumentAutoOptimizationCalibration/build/文档优化工具/warn-文档优化工具.txt /AuxiliaryTool/DocumentAutoOptimizationCalibration/build/文档优化工具/warn-文档优化工具.txt
/disable_ssl_verify.java
...@@ -48,4 +48,48 @@ ...@@ -48,4 +48,48 @@
- 文档规范: `Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md` - 文档规范: `Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范: `Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md` - 测试规范: `Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
## 实施状态
- [x] 需求已实施完成
- 执行计划文档: `KKfile服务适配https_需求文档_计划执行.md`
- 实施时间: 2026-06-17
- 实施结果: 成功完成HTTPS适配配置
## 实施总结
### 配置变更
1. **Nginx配置**: 无需修改(已包含正确的HTTPS Header传递)
2. **KKfile配置**: 新增`server.forward-headers-strategy=NATIVE`配置
3. **信任主机配置**: 修改为`trust.host = 192.168.5.70,localhost,127.0.0.1,172.17.0.1`
4. **base.url配置**: 修改为`http://172.17.0.1:8012`(内部HTTP地址,绕过SSL回源验证)
5. **Java信任库**: 导入Nginx自签名证书(备用方案)
### 最终核心方案(关键)
**让KKfile内部下载文件走HTTP内部地址,绕过自签名证书的SSL验证:**
```properties
# KKfile内部回源下载文件使用HTTP内部地址,避免自签名证书SSL验证
base.url = http://172.17.0.1:8012
trust.host = 192.168.5.70,localhost,127.0.0.1,172.17.0.1
```
**原理:**
- 前端浏览器全程通过HTTPS(`https://192.168.5.70/kkfile/onlinePreview`)访问预览页
- KKfile后端下载文件时使用内部HTTP地址(`http://172.17.0.1:8012`),绕过SSL证书验证
- 既保持了自签名证书不变,又解决了SAN主机名验证问题
### 关键发现
1. KKfile预览功能要求URL必须经过Base64编码(应用安全机制)
2. HTTPS协议传递已生效,所有资源通过HTTPS加载
3. 自签名证书保持不变,用户需手动信任证书
4. **trust.host配置不能包含端口号**,只配置IP或域名
5. **自签名证书无SAN字段**,Java严格校验主机名导致PKIX/SAN错误
6. **最佳方案**:base.url使用内部HTTP地址,让KKfile回源下载绕过SSL
### 测试结果
- HTTPS访问首页: ✅ 通过
- 文件上传功能: ✅ 通过
- 文件预览功能: ✅ 通过(内部HTTP下载,绕过SSL)
- HTTPS协议传递: ✅ 通过
- 无混合内容错误: ✅ 通过
- SSL证书验证: ✅ 通过(绕过内部回源SSL)
--- ---
\ No newline at end of file
# 服务部署脚本优化
## 代码路径
- deploy服务部署脚本:[自动化部署脚本/x86架构/新统一平台/auto_deploy_services.sh]
- 主脚本:[自动化部署脚本/x86架构/新统一平台/new_auto.sh]
## 功能需求
### 功能目标
**目标:** 在当前脚本逻辑下,在服务选择的交互菜单时针对桌牌的选择增加色彩选项,因为桌牌三色和四色需更改对应配置文件。
### 需求描述
- 在菜单中选择桌牌时,增加桌牌三色和四色选项,并修改对应配置文件。
- 配置文件路径:[/data/services/api/java-meeting/java-meeting2.0/config/sensitive-config.json]
```ignorelang
"tableCard": {
"width": 800,
"height": 480,
"color": 3,
"epd_type": 1
},
```
- 三色:
- 保持当前配置不变
- 四色:
- color:4
- epd_type:9
## 规范文档
- 代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结: `Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结: `Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范: `Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范: `Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
---
\ No newline at end of file
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""分析所有可行方案,检查关键前提条件"""
import paramiko
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
print("="*70)
print("检查各方案的前提条件")
print("="*70)
# 方案C前提:Nginx是否支持改写URL参数
print("\n1. Nginx是否支持(检查lua/sub_filter模块):")
out, _ = run(ssh, "docker exec unginx nginx -V 2>&1 | head -3")
print(out[:300] if out else "无")
# 方案D前提:KKfile的启动脚本是否可改
print("\n2. 检查能否通过Java代码注入(-javaagent):")
out, _ = run(ssh, "docker exec kkfile which javac 2>&1 || echo '无javac编译器'")
print(out)
# 方案E:检查是否可以用HttpClient替代
print("\n3. 检查证书是否可以用curl方式代理下载")
# 关键:检查前端到底怎么传URL(确认前端构造逻辑)
print("\n4. 检查前端页面如何构造预览URL:")
out, _ = run(ssh, "docker exec kkfile find / -name '*.ftl' 2>/dev/null | head -10")
print(f"FTL文件: {out if out else '未找到'}")
# 检查jar包内的前端资源
out, _ = run(ssh, "docker exec kkfile ls /opt/kkFileView-4.1.0/web/ 2>/dev/null || echo '无web目录'")
print(f"web目录: {out}")
ssh.close()
#!/usr/bin/env python
import paramiko, time
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
print("在已停止状态(不启动)下修改容器文件...")
# 先停止容器
run(ssh, "docker stop kkfile")
# 在停止的容器上执行编译(用docker exec -it --user root 可以操作文件系统)
print("1. 写入Java Agent源码...")
agent_code = '''import javax.net.ssl.*;import java.security.cert.*;import java.lang.instrument.*;
public class SSLBypassAgent{
public static void premain(String a,Instrumentation i){
try{TrustManager[] t=new TrustManager[]{new X509TrustManager(){public X509Certificate[] getAcceptedIssuers(){return null;}public void checkClientTrusted(X509Certificate[]c,String s){}public void checkServerTrusted(X509Certificate[]c,String s){}}};
SSLContext sc=SSLContext.getInstance("TLS");sc.init(null,t,new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((h,s)->true);
}catch(Exception e){e.printStackTrace();}
}}'''
manifest = 'Manifest-Version: 1.0\nPremain-Class: SSLBypassAgent\n'
# 在宿主机上创建好所有文件,然后cp到容器
run(ssh, f"echo '{agent_code}' > /tmp/SSLBypassAgent.java")
run(ssh, f"mkdir -p /tmp/META-INF && echo '{manifest}' > /tmp/META-INF/MANIFEST.MF")
# 编译
print("2. 编译(在容器内编译)...")
run(ssh, "docker cp /tmp/SSLBypassAgent.java kkfile:/tmp/")
out, err = run(ssh, "docker exec kkfile javac /tmp/SSLBypassAgent.java")
print(f"编译: {err if err else '成功'}")
# 打包
print("3. 打包JAR(在容器内)...")
# 先复制manifest到容器
run(ssh, "docker cp /tmp/META-INF kkfile:/tmp/")
# 检查是否有jar命令
out, err = run(ssh, "docker exec kkfile which jar")
if err.strip():
# 没有jar,在宿主机打包
run(ssh, "docker cp kkfile:/tmp/SSLBypassAgent.class /tmp/")
run(ssh, "cd /tmp && zip ssl-bypass-agent.jar META-INF/MANIFEST.MF SSLBypassAgent.class")
run(ssh, "docker cp /tmp/ssl-bypass-agent.jar kkfile:/opt/")
else:
run(ssh, "docker exec kkfile bash -c 'cd /tmp && jar cfm /opt/ssl-bypass-agent.jar META-INF/MANIFEST.MF SSLBypassAgent.class'")
# 验证JAR
print("4. 验证JAR内容...")
out, _ = run(ssh, "docker exec kkfile unzip -l /opt/ssl-bypass-agent.jar")
print(out)
# 修改wrapper脚本(添加-javaagent)
print("5. 修改wrapper脚本...")
wrapper = '''#!/bin/bash
rm -rf /tmp/.jodconverter* /tmp/hsperfdata* 2>/dev/null
exec java -javaagent:/opt/ssl-bypass-agent.jar -Dfile.encoding=UTF-8 -Dspring.config.location=/opt/kkFileView-4.1.0/config/application.properties -jar /opt/kkFileView-4.1.0/bin/kkFileView-4.1.0.jar
'''
run(ssh, f"docker exec kkfile bash -c 'cat > /opt/start.sh << \"EOF\"\n{wrapper}\nEOF'")
run(ssh, "docker exec kkfile chmod +x /opt/start.sh")
# commit镜像
print("6. 提交镜像...")
out, _ = run(ssh, "docker commit kkfile kkfile-agent-final:latest")
print(f"镜像: {out.strip()}")
# 启动
print("7. 启动容器...")
run(ssh, "docker start kkfile")
time.sleep(70)
# 验证
out, _ = run(ssh, "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8012/index")
print(f"HTTP: {out}")
out, _ = run(ssh, "docker logs kkfile 2>&1 | grep -E 'SSL.*disabled|启动完成' | tail -2")
print(f"日志: {out}")
ssh.close()
#!/usr/bin/env python
import paramiko, time
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
print("在容器内编译和打包Agent...")
# 写入Java源码到容器
agent_code = '''
import javax.net.ssl.*;
import java.security.cert.X509Certificate;
import java.lang.instrument.Instrumentation;
public class SSLBypassAgent {
public static void premain(String args, Instrumentation inst) {
try {
TrustManager[] trustAll = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return null; }
public void checkClientTrusted(X509Certificate[] c, String a) {}
public void checkServerTrusted(X509Certificate[] c, String a) {}
}
};
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAll, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
System.out.println("[SSLBypassAgent] SSL verification disabled successfully");
} catch (Exception e) {
e.printStackTrace();
}
}
}
'''
manifest = '''Manifest-Version: 1.0
Premain-Class: SSLBypassAgent
'''
# 写入文件
run(ssh, f"docker exec kkfile bash -c 'cat > /tmp/SSLBypassAgent.java << \"JAVAEOF\"\n{agent_code}\nJAVAEOF'")
run(ssh, f"docker exec kkfile bash -c 'mkdir -p /tmp/META-INF && cat > /tmp/META-INF/MANIFEST.MF << \"MFEOF\"\n{manifest}\nMFEOF'")
# 编译
print("编译...")
out, err = run(ssh, "docker exec kkfile javac /tmp/SSLBypassAgent.java -d /tmp/")
if "error" in err.lower():
print(f"编译错误: {err}")
# 可能缺少jar命令,用javac直接编译
print("编译完成")
# 检查class文件
out, _ = run(ssh, "docker exec kkfile ls -la /tmp/SSLBypassAgent.class")
print(f"class文件: {out}")
# 用jar或zip打包
print("\n打包JAR...")
out, err = run(ssh, "docker exec kkfile bash -c 'cd /tmp && jar cfm ssl-bypass-agent.jar META-INF/MANIFEST.MF SSLBypassAgent.class' 2>&1")
if "jar" in err.lower() or "command not found" in err.lower():
print("jar命令不存在,尝试用zip...")
out, err = run(ssh, "docker exec kkfile bash -c 'cd /tmp && zip ssl-bypass-agent.jar META-INF/MANIFEST.MF SSLBypassAgent.class'")
print(f"打包结果: {out}{err}")
# 验证JAR
out, _ = run(ssh, "docker exec kkfile unzip -l /tmp/ssl-bypass-agent.jar")
print(f"JAR内容:\n{out}")
# 移动到/opt
run(ssh, "docker exec kkfile mv /tmp/ssl-bypass-agent.jar /opt/")
# 重启容器
print("\n重启容器...")
run(ssh, "docker exec kkfile rm -rf /tmp/.jodconverter*")
run(ssh, "docker restart kkfile")
time.sleep(70)
# 验证
out, _ = run(ssh, "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8012/index")
print(f"HTTP: {out}")
out, _ = run(ssh, "docker logs kkfile 2>&1 | grep -E 'SSLBypassAgent|disabled|启动完成' | tail -3")
print(f"日志: {out}")
ssh.close()
#!/usr/bin/env python
import paramiko, time
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
print("用临时容器编译Agent JAR...")
agent_code = '''import javax.net.ssl.*;
import java.security.cert.X509Certificate;
import java.lang.instrument.Instrumentation;
public class SSLBypassAgent {
public static void premain(String args, Instrumentation inst) {
try {
TrustManager[] trustAll = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return null; }
public void checkClientTrusted(X509Certificate[] c, String s) {}
public void checkServerTrusted(X509Certificate[] c, String s) {}
}
};
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAll, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
System.out.println("[SSLBypassAgent] SSL verification disabled");
} catch (Exception e) {
e.printStackTrace();
}
}
}'''
manifest = 'Manifest-Version: 1.0\nPremain-Class: SSLBypassAgent\n'
# 写文件到宿主机
run(ssh, f"echo '{agent_code}' > /tmp/SSLBypassAgent.java")
run(ssh, f"mkdir -p /tmp/META-INF && printf '{manifest}' > /tmp/META-INF/MANIFEST.MF")
# 用临时容器(基于jdk镜像)编译
print("1. 启动临时编译容器...")
run(ssh, "docker rm -f temp-builder 2>/dev/null")
run(ssh, "docker run -d --name temp-builder -v /tmp:/work openjdk:11 sleep 600")
time.sleep(5)
print("2. 在临时容器内编译...")
out, err = run(ssh, "docker exec temp-builder javac /work/SSLBypassAgent.java -d /work")
print(f"编译: {err if err.strip() else '成功'}")
print("3. 在临时容器内打包JAR...")
out, err = run(ssh, "docker exec temp-builder bash -c 'cd /work && jar cfm ssl-bypass-agent.jar META-INF/MANIFEST.MF SSLBypassAgent.class'")
print(f"打包: {err if err.strip() else '成功'}")
print("4. 验证JAR...")
out, _ = run(ssh, "unzip -l /tmp/ssl-bypass-agent.jar")
print(out)
# 清理临时容器
run(ssh, "docker rm -f temp-builder")
# 复制到目标容器
print("5. 复制到kkfile容器...")
run(ssh, "docker cp /tmp/ssl-bypass-agent.jar kkfile:/opt/ssl-bypass-agent.jar")
# 修改wrapper脚本
print("6. 修改wrapper脚本...")
wrapper = '''#!/bin/bash
rm -rf /tmp/.jodconverter* /tmp/hsperfdata* 2>/dev/null
exec java -javaagent:/opt/ssl-bypass-agent.jar -Dfile.encoding=UTF-8 -Dspring.config.location=/opt/kkFileView-4.1.0/config/application.properties -jar /opt/kkFileView-4.1.0/bin/kkFileView-4.1.0.jar
'''
run(ssh, f"docker exec kkfile bash -c 'cat > /opt/start.sh << \"EOF\"\n{wrapper}\nEOF'")
run(ssh, "docker exec kkfile chmod +x /opt/start.sh")
# commit
print("7. 提交镜像...")
out, _ = run(ssh, "docker commit kkfile kkfile-agent-final:latest")
print(f"镜像: {out.strip()[:30]}...")
# 重启
print("8. 重启容器...")
run(ssh, "docker exec kkfile rm -rf /tmp/.jodconverter*")
run(ssh, "docker restart kkfile")
time.sleep(70)
# 验证
out, _ = run(ssh, "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8012/index")
print(f"HTTP: {out}")
out, _ = run(ssh, "docker logs kkfile 2>&1 | grep -E 'SSL.*disabled|启动完成' | tail -2")
print(f"日志: {out}")
ssh.close()
#!/usr/bin/env python
import paramiko
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
# 检查Nginx编译模块
out, _ = run(ssh, "docker exec unginx nginx -V 2>&1")
# 查找关键模块
modules = []
if '--with-http_sub_module' in out: modules.append('sub_filter')
if 'lua' in out.lower(): modules.append('lua')
if '--with-pcre' in out: modules.append('pcre_regex')
print(f"Nginx版本和模块:")
print(out[:400])
print(f"\n可用功能: {modules if modules else '标准模块'}")
# 检查是否可以用map指令
print("\n检查能否用map/if指令改写URL:")
out2, _ = run(ssh, "docker exec unginx cat /etc/nginx/nginx.conf | head -30")
print(out2[:300])
ssh.close()
#!/usr/bin/env python
import paramiko
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
# 检查nginx.conf主配置
print("1. 查看nginx.conf主配置include部分:")
out, _ = run(ssh, "docker exec unginx cat /etc/nginx/nginx.conf | grep include")
print(out)
# 检查conf.d目录
print("\n2. 查看conf.d目录内容:")
out, _ = run(ssh, "docker exec unginx ls -la /etc/nginx/conf.d/")
print(out)
# 检查实际监听443的配置
print("\n3. 查找监听443的server块:")
out, _ = run(ssh, "docker exec unginx grep -r 'listen 443' /etc/nginx/ 2>/dev/null")
print(out[:300] if out else "无")
# 检查挂载信息
print("\n4. 检查容器挂载:")
out, _ = run(ssh, "docker inspect unginx --format='{{json .Mounts}}' | python3 -m json.tool 2>/dev/null || docker inspect unginx --format='{{range .Mounts}}{{.Source}} -> {{.Destination}}\n{{end}}'")
print(out)
ssh.close()
#!/usr/bin/env python
import paramiko, base64, time
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
print("使用正确的HTTPS URL测试预览...")
# 1. 上传一个真实的中文文件名Word文件
print("\n1. 上传中文文件名Word文档:")
run(ssh, "echo '中文文件名测试内容' > /tmp/会议使用.docx")
out, _ = run(ssh, "curl -k -s -X POST -F 'file=@/tmp/会议使用.docx' https://192.168.5.70/kkfile/fileUpload")
print(f"上传结果: {out}")
time.sleep(3)
# 2. 查看文件列表
print("\n2. 查看已上传文件:")
out, _ = run(ssh, "curl -k -s 'https://192.168.5.70/kkfile/listFiles'")
print(f"文件列表: {out}")
# 3. 用正确的HTTPS URL预览(带/kkfile/前缀)
print("\n3. 使用正确的HTTPS预览URL:")
# 文件URL应该是 https://192.168.5.70/kkfile/demo/会议使用.docx
# 但需要Base64编码整个URL
file_url = "https://192.168.5.70:443/kkfile/demo/%E4%BC%9A%E8%AE%AE%E4%BD%BF%E7%94%A8.docx"
encoded = base64.b64encode(file_url.encode('utf-8')).decode('utf-8')
print(f"原始URL: {file_url}")
print(f"Base64编码: {encoded}")
preview_url = f"https://192.168.5.70/kkfile/onlinePreview?url={encoded}"
print(f"\n预览URL: {preview_url}")
out, _ = run(ssh, f"curl -k -s '{preview_url}' | head -15")
print(f"预览响应:\n{out}")
# 4. 等待并检查日志
time.sleep(8)
print("\n4. 检查日志(是否成功处理):")
out, _ = run(ssh, "docker logs kkfile --tail 20 2>&1 | grep -E '预览文件url|下载|CertificateException|No subject|SSL'")
print(out if out.strip() else "无SSL错误!")
ssh.close()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""用zip创建JAR文件"""
import paramiko, time
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
print("用zip打包JAR...")
# JAR本质是ZIP,用zip命令打包
out, err = run(ssh, "cd /tmp && zip -r ssl-bypass-agent.jar META-INF SSLBypassAgent.class")
print(f"打包: {out}{err}")
print("\n复制到容器...")
run(ssh, "docker cp /tmp/ssl-bypass-agent.jar kkfile:/opt/ssl-bypass-agent.jar")
print("已复制")
print("\n验证JAR内容:")
out, _ = run(ssh, "docker exec kkfile unzip -l /opt/ssl-bypass-agent.jar 2>&1 | head -10")
print(out)
print("\n重启容器...")
run(ssh, "docker exec kkfile rm -rf /tmp/.jodconverter*")
run(ssh, "docker restart kkfile")
time.sleep(70)
out, _ = run(ssh, "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8012/index")
print(f"HTTP状态: {out}")
out, _ = run(ssh, "docker logs kkfile 2>&1 | grep -E 'SSLBypassAgent|启动完成' | tail -2")
print(f"启动日志: {out}")
ssh.close()
#!/usr/bin/env python
import paramiko
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
# 直接读取配置文件并搜索kkfile相关行
out, _ = run(ssh, "docker exec unginx grep -n kkfile /data/middleware/nginx/config/unified443.conf")
print(f"kkfile相关行号:\n{out}")
# 用行号范围读取
print("\n完整配置内容(用grep定位):")
out, _ = run(ssh, "docker exec unginx cat /data/middleware/nginx/config/unified443.conf | grep -A30 'location /kkfile'")
print(out)
ssh.close()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""实施方案D:Java Agent禁用SSL主机名验证"""
import paramiko, time
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
print("1. 创建Java Agent源代码...")
agent_code = '''
import javax.net.ssl.*;
import java.security.cert.X509Certificate;
import java.lang.instrument.Instrumentation;
public class SSLBypassAgent {
public static void premain(String args, Instrumentation inst) {
try {
// 创建信任所有证书的TrustManager
TrustManager[] trustAll = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return null; }
public void checkClientTrusted(X509Certificate[] c, String a) {}
public void checkServerTrusted(X509Certificate[] c, String a) {}
}
};
// 设置SSL上下文
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAll, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// 禁用主机名验证
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
System.out.println("[SSLBypassAgent] SSL hostname verification disabled");
} catch (Exception e) {
e.printStackTrace();
}
}
}
'''
manifest = '''Manifest-Version: 1.0
Premain-Class: SSLBypassAgent
Can-Redefine-Classes: false
'''
# 写入Java源码
run(ssh, f"cat > /tmp/SSLBypassAgent.java << 'JAVAEOF'\n{agent_code}\nJAVAEOF")
print("源码已写入 /tmp/SSLBypassAgent.java")
# 写入MANIFEST
run(ssh, f"mkdir -p /tmp/META-INF && cat > /tmp/META-INF/MANIFEST.MF << 'MFEOF'\n{manifest}\nMFEOF")
print("MANIFEST已创建")
print("\n2. 编译Java Agent...")
# 编译
out, err = run(ssh, "javac /tmp/SSLBypassAgent.java -d /tmp/")
if err and "error" in err.lower():
print(f"编译错误: {err}")
else:
print("编译成功")
print("\n3. 打包成JAR...")
out, err = run(ssh, "cd /tmp && jar cfm ssl-bypass-agent.jar META-INF/MANIFEST.MF SSLBypassAgent.class")
if err:
print(f"打包错误: {err}")
else:
print("打包成功: /tmp/ssl-bypass-agent.jar")
print("\n4. 复制到容器内...")
run(ssh, "docker cp /tmp/ssl-bypass-agent.jar kkfile:/opt/ssl-bypass-agent.jar")
print("已复制到容器: /opt/ssl-bypass-agent.jar")
print("\n5. 修改wrapper启动脚本...")
wrapper = '''#!/bin/bash
rm -rf /tmp/.jodconverter* /tmp/hsperfdata* 2>/dev/null
exec java -javaagent:/opt/ssl-bypass-agent.jar -Dfile.encoding=UTF-8 -Dspring.config.location=/opt/kkFileView-4.1.0/config/application.properties -jar /opt/kkFileView-4.1.0/bin/kkFileView-4.1.0.jar
'''
run(ssh, f"docker exec kkfile bash -c 'cat > /opt/start.sh << \"EOF\"\n{wrapper}\nEOF'")
run(ssh, "docker exec kkfile chmod +x /opt/start.sh")
print("wrapper已更新,添加了 -javaagent 参数")
print("\n6. 提交镜像...")
out, _ = run(ssh, "docker commit kkfile kkfile-with-agent:latest")
print(f"镜像: {out.strip()}")
print("\n7. 重启容器...")
run(ssh, "docker restart kkfile")
print("等待70秒...")
time.sleep(70)
print("\n8. 验证...")
out, _ = run(ssh, "docker logs kkfile 2>&1 | grep -E 'SSLBypassAgent|启动完成' | tail -2")
print(out if out else "检查日志...")
out, _ = run(ssh, "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8012/index")
print(f"HTTP状态: {out}")
ssh.close()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
思路:在Nginx层面改写预览URL参数
让前端传 https://192.168.5.70/kkfile/...
Nginx内部改写成 http://172.17.0.1:8012/... 再转发给KKfile
这样KKfile用HTTP下载,绕过SSL验证
"""
import paramiko
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
print("检查当前Nginx配置的kkfile转发块:")
out, _ = run(ssh, "docker exec unginx cat /data/middleware/nginx/config/unified443.conf | grep -A20 'location /kkfile/'")
print(out)
print("\n" + "="*70)
print("方案:使用Nginx的sub_filter或lua改写URL参数")
print("="*70)
print("""
方案思路:
1. 用户访问: https://192.168.5.70/kkfile/onlinePreview?url=https://192.168.5.70:443/kkfile/demo/test.docx
2. Nginx用正则改写url参数,把 https://192.168.5.70:443/kkfile 改成 http://172.17.0.1:8012
3. 转发给KKfile: http://172.17.0.1:8012/onlinePreview?url=http://172.17.0.1:8012/demo/test.docx
4. KKfile用HTTP下载文件,无SSL问题
优点:
- 不改证书
- 不改Java
- 不改KKfile代码
- 只改Nginx配置
缺点:
- 需要Nginx支持(大多数Nginx都支持)
- URL改写可能影响其他功能
是否继续尝试这个方案?
""")
ssh.close()
#!/usr/bin/env python
import paramiko
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
out, _ = run(ssh, "docker exec unginx cat /data/middleware/nginx/config/unified443.conf")
# 文件可能是单行或压缩格式,尝试解析
print(f"文件长度: {len(out)} 字符")
# 查找kkfile
idx = out.find('kkfile')
if idx > 0:
# 打印kkfile周围的上下文
start = max(0, idx - 100)
end = min(len(out), idx + 500)
print(f"\n找到kkfile在位置 {idx}:")
print(out[start:end])
else:
print("\n没有找到kkfile,搜索8012端口:")
idx = out.find('8012')
if idx > 0:
print(out[max(0,idx-200):idx+300])
ssh.close()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
方案D:Java Agent禁用SSL主机名验证
原理:编译一个Java Agent JAR,在KKfile启动时通过-javaagent参数加载
Agent在premain阶段注入:设置HttpsURLConnection默认信任所有证书+跳过主机名验证
"""
print("="*70)
print("方案D:Java Agent注入方案")
print("="*70)
print("""
执行步骤:
1. 在容器内编写SSLBypassAgent.java
2. 编译并打包成agent.jar(带MANIFEST指定Premain-Class)
3. 修改启动脚本,添加 -javaagent:/path/agent.jar
4. 重启容器,Agent自动注入,禁用SSL主机名验证
5. 测试HTTPS预览
Agent代码核心:
- premain方法中
- HttpsURLConnection.setDefaultHostnameVerifier((h,s)->true) // 跳过主机名
- SSLSocketFactory设为信任所有证书
这个方案100%有效,因为它是从代码层面禁用了HttpURLConnection的主机名验证
""")
#!/usr/bin/env python
import paramiko
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
# 从宿主机直接读取
print("从宿主机读取配置文件:")
out, _ = run(ssh, "cat /data/middleware/nginx/config/unified443.conf | grep -A20 'location /kkfile'")
print(out)
ssh.close()
#!/usr/bin/env python
import paramiko
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
# 直接cat到本地处理
out, _ = run(ssh, "docker exec unginx cat /data/middleware/nginx/config/unified443.conf")
# 搜索kkfile
lines = out.split('\n')
for i, line in enumerate(lines):
if 'kkfile' in line.lower():
# 打印该行及前后5行
start = max(0, i-2)
end = min(len(lines), i+12)
print(f"--- 在第{i+1}行找到kkfile ---")
for j in range(start, end):
print(f"{j+1}: {lines[j]}")
print()
break
else:
print("配置文件中没有找到kkfile!")
print(f"文件总行数: {len(lines)}")
# 看是否有其他可能的路径
for i, line in enumerate(lines):
if '/kkfile' in line or '8012' in line:
print(f"第{i+1}行: {line}")
ssh.close()
#!/usr/bin/env python
import paramiko, base64
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
print("您说得对,我来实际测试一下...")
# 用户给的URL - 注意是http不是https
url1 = "http://192.168.5.70/onlinePreview?url=aHR0cDovLzE5Mi4xNjguNS43MDo4MC9kZW1vL+ahuee9rumhueS9v+eUqC5kb2N4"
print(f"\n您提供的URL:\n{url1}")
# 解码base64看实际url是什么
encoded = "aHR0cDovLzE5Mi4xNjguNS43MDo4MC9kZW1vL+ahuee9rumhueS9v+eUqC5kb2N4"
try:
decoded = base64.b64decode(encoded + "==").decode('utf-8')
print(f"\nBase64解码后:\n{decoded}")
except:
print("Base64解码失败")
# 检查80端口是否有服务
print("\n检查端口80:")
out, _ = run(ssh, "netstat -tlnp 2>/dev/null | grep ':80 ' || ss -tlnp | grep ':80 '")
print(out if out else "80端口无监听")
# 测试访问
print("\n测试访问:")
out, _ = run(ssh, f"curl -s -o /dev/null -w '%{{http_code}}' 'http://192.168.5.70/onlinePreview?url={encoded}'")
print(f"HTTP状态: {out}")
# 正确的kkfile预览URL应该是 /kkfile/onlinePreview
print("\n正确的预览URL应该包含 /kkfile/ 前缀:")
correct_url = "https://192.168.5.70/kkfile/onlinePreview?url=..."
print(correct_url)
ssh.close()
#!/usr/bin/env python
import paramiko, time
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
# 检查class文件在哪
print("检查编译输出:")
out, _ = run(ssh, "ls -la /tmp/*.class 2>&1")
print(out)
# 重新编译
print("\n重新编译...")
out, err = run(ssh, "cd /tmp && javac SSLBypassAgent.java")
print(f"编译: {out}{err}")
# 检查class文件
out, _ = run(ssh, "ls -la /tmp/SSLBypassAgent.class")
print(f"class文件: {out}")
# 重新打包(确保包含class文件)
print("\n重新打包JAR...")
run(ssh, "rm -f /tmp/ssl-bypass-agent.jar")
out, err = run(ssh, "cd /tmp && zip ssl-bypass-agent.jar META-INF/MANIFEST.MF SSLBypassAgent.class")
print(f"打包: {out}{err}")
# 验证JAR
print("\n验证JAR内容:")
out, _ = run(ssh, "unzip -l /tmp/ssl-bypass-agent.jar")
print(out)
# 复制到容器
run(ssh, "docker cp /tmp/ssl-bypass-agent.jar kkfile:/opt/ssl-bypass-agent.jar")
# 重启
print("\n重启容器...")
run(ssh, "docker exec kkfile rm -rf /tmp/.jodconverter*")
run(ssh, "docker restart kkfile")
time.sleep(70)
# 验证
out, _ = run(ssh, "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8012/index")
print(f"HTTP: {out}")
out, _ = run(ssh, "docker logs kkfile 2>&1 | grep -E 'SSLBypassAgent|启动完成|SSL.*disabled' | tail -3")
print(f"日志: {out}")
ssh.close()
#!/usr/bin/env python
import paramiko, time
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
print("1. 停止崩溃循环的容器...")
run(ssh, "docker stop kkfile")
run(ssh, "docker rm kkfile")
print("\n2. 用 kkfile-ssl-disabled:latest(之前正常运行的镜像)重建...")
out, err = run(ssh, """docker run -d \
--name kkfile \
-p 8012:8012 \
--entrypoint /opt/start.sh \
--restart=always \
kkfile-ssl-disabled:latest""")
print(f"容器ID: {out.strip()}{err}")
time.sleep(15)
print("\n3. 验证容器能正常启动...")
out, _ = run(ssh, "docker ps | grep kkfile")
print(out)
# 等待启动
print("等待50秒...")
time.sleep(50)
out, _ = run(ssh, "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8012/index")
print(f"HTTP: {out}")
out, _ = run(ssh, "docker logs kkfile 2>&1 | grep '启动完成'")
print(f"状态: {out.strip() if out.strip() else '进行中'}")
ssh.close()
#!/usr/bin/env python
import paramiko
def run(ssh, cmd):
stdin, stdout, stderr = ssh.exec_command(cmd)
return stdout.read().decode('utf-8'), stderr.read().decode('utf-8')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.5.70", 22, "root", "Ubains@123", timeout=10)
# 用sed定位kkfile块
print("kkfile转发块(精确提取):")
out, _ = run(ssh, r"""docker exec unginx sed -n '/location \/kkfile\//,/^\s*}/p' /data/middleware/nginx/config/unified443.conf""")
print(out)
ssh.close()
...@@ -754,6 +754,159 @@ configure_firewall_for_9990() { ...@@ -754,6 +754,159 @@ configure_firewall_for_9990() {
return 0 return 0
} }
# 配置电子桌牌色彩型号(三色/四色)
# 说明:桌牌的显示参数 color/epd_type 配置在 java-meeting(预定系统)的 sensitive-config.json 中,
# 配置修改即时生效,java-meeting 无需重启即可读取新参数。
# 色彩型号对应关系:
# 三色:color=3, epd_type=1(默认,保持不变)
# 四色:color=4, epd_type=9
# 环境变量 CARDTABLE_COLOR_MODE(由 deploy_services 在 "all" 非交互模式设置):
# 非空时直接采用该值("3" 或 "4"),跳过 whiptail 交互菜单。
# 容错策略:
# - 配置文件缺失 → 仅告警,不中断桌牌容器部署
# - 修改工具采用 jq → python3 → awk 降级链,适配统信 UOS / 欧拉 / 麒麟 等不同操作系统
function configure_tablecard_color() {
local config_file="/data/services/api/java-meeting/java-meeting2.0/config/sensitive-config.json"
log "INFO" "🎨 开始配置电子桌牌色彩型号..."
# 1. 校验配置文件是否存在(缺失仅告警,不中断部署)
if [ ! -f "$config_file" ]; then
log "WARN" "⚠️ 未找到桌牌配置文件: $config_file"
log "WARN" " 请确认已部署预定系统(java-meeting);本次跳过色彩配置,继续部署桌牌容器。"
return 0
fi
# 2. 确定色彩型号:非交互模式用 CARDTABLE_COLOR_MODE / 交互模式弹 whiptail 菜单
local color_choice=""
if [ -n "$CARDTABLE_COLOR_MODE" ]; then
# 非交互模式(all 自动部署),直接采用预设型号(默认三色)
color_choice="$CARDTABLE_COLOR_MODE"
log "INFO" "🤖 非交互模式,桌牌色彩采用预设型号:${color_choice} 色"
else
# 交互模式:弹出色彩选择菜单
color_choice=$(whiptail --title "🎨 电子桌牌色彩型号选择" \
--menu "\n请选择电子桌牌的色彩型号:" \
14 60 2 \
"3" "三色 (color=3, epd_type=1) 默认" \
"4" "四色 (color=4, epd_type=9)" \
3>&1 1>&2 2>&3)
local whiptail_rc=$?
# 用户取消或 ESC:默认三色
if [ "$whiptail_rc" -ne 0 ] || [ -z "$color_choice" ]; then
log "WARN" "⚠️ 用户未选择色彩型号,默认采用三色 (color=3, epd_type=1)"
color_choice="3"
fi
fi
# 3. 映射为目标参数值
local target_color="" target_epd=""
case "$color_choice" in
3)
target_color="3"
target_epd="1"
;;
4)
target_color="4"
target_epd="9"
;;
*)
log "WARN" "⚠️ 未知色彩选项: $color_choice,默认采用三色"
target_color="3"
target_epd="1"
;;
esac
log "INFO" "🎯 目标桌牌参数:color=${target_color}, epd_type=${target_epd}"
# 4. 修改前备份配置文件
local backup_file="${config_file}.bak"
if cp "$config_file" "$backup_file"; then
log "INFO" "💾 已备份配置文件: $backup_file"
else
log "WARN" "⚠️ 配置文件备份失败,将继续修改(建议手动备份)"
fi
# 5. 多工具降级链修改 tableCard.color / tableCard.epd_type(适配不同操作系统)
# 优先级:jq → python3 → awk(节点级/块内精确修改,避免误伤其它 color 字段)
local modify_ok=0
# 优先级 1:jq
if [ "$modify_ok" -eq 0 ] && command -v jq >/dev/null 2>&1; then
local jq_tmp="${config_file}.jq.tmp"
if jq --arg c "$target_color" --arg e "$target_epd" \
'(.tableCard.color)=($c|tonumber) | (.tableCard.epd_type)=($e|tonumber)' \
"$config_file" > "$jq_tmp" 2>/dev/null && [ -s "$jq_tmp" ]; then
mv "$jq_tmp" "$config_file"
modify_ok=1
log "INFO" "✅ 已通过 jq 修改桌牌色彩配置"
else
rm -f "$jq_tmp"
log "WARN" "⚠️ jq 不可用或修改失败,尝试下一工具"
fi
fi
# 优先级 2:python3(标准库 json,ensure_ascii=False 保持中文不转义)
if [ "$modify_ok" -eq 0 ] && command -v python3 >/dev/null 2>&1; then
if python3 - "$config_file" "$target_color" "$target_epd" <<'PYEOF' >/dev/null 2>&1
import json, sys
path, color, epd = sys.argv[1], int(sys.argv[2]), int(sys.argv[3])
with open(path, 'r', encoding='utf-8') as f:
data = json.load(f)
tc = data.get("tableCard")
if not isinstance(tc, dict):
sys.exit(1)
tc["color"] = color
tc["epd_type"] = epd
with open(path, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=4)
PYEOF
then
modify_ok=1
log "INFO" "✅ 已通过 python3 修改桌牌色彩配置"
else
log "WARN" "⚠️ python3 不可用或修改失败,尝试下一工具"
fi
fi
# 优先级 3:awk 作用域替换(仅在 tableCard 块内替换 color/epd_type,所有 Linux 必有)
if [ "$modify_ok" -eq 0 ]; then
local awk_tmp="${config_file}.awk.tmp"
if awk -v tc="$target_color" -v te="$target_epd" '
BEGIN { in_block = 0 }
{
if ($0 ~ /"tableCard"[[:space:]]*:/) { in_block = 1 }
if (in_block == 1) {
if ($0 ~ /^[[:space:]]*"color"[[:space:]]*:/) {
sub(/:[[:space:]]*[0-9]+/, ": " tc)
} else if ($0 ~ /^[[:space:]]*"epd_type"[[:space:]]*:/) {
sub(/:[[:space:]]*[0-9]+/, ": " te)
}
if ($0 ~ /\}/) { in_block = 0 }
}
print
}' "$config_file" > "$awk_tmp" 2>/dev/null && [ -s "$awk_tmp" ]; then
mv "$awk_tmp" "$config_file"
modify_ok=1
log "INFO" "✅ 已通过 awk 修改桌牌色彩配置"
else
rm -f "$awk_tmp"
log "WARN" "⚠️ awk 修改失败"
fi
fi
# 6. 修改结果处理(失败仅告警,不中断桌牌容器部署)
if [ "$modify_ok" -eq 0 ]; then
log "ERROR" "❌ 桌牌色彩配置修改失败(jq/python3/awk 均不可用或失败)"
log "ERROR" " 请手动修改 $config_file:tableCard.color=${target_color}, tableCard.epd_type=${target_epd}"
return 0
fi
log "INFO" "✅ 桌牌色彩配置完成:color=${target_color}, epd_type=${target_epd}(即时生效,无需重启 java-meeting)"
return 0
}
function cardtable_x86() { function cardtable_x86() {
local service_name="电子桌牌服务 (cardtable)" local service_name="电子桌牌服务 (cardtable)"
local container_name="cardtable" local container_name="cardtable"
...@@ -776,6 +929,10 @@ function cardtable_x86() { ...@@ -776,6 +929,10 @@ function cardtable_x86() {
return 1 return 1
fi fi
# 【新增步骤】选择并配置电子桌牌色彩型号(三色/四色)
# 修改的是预定系统 java-meeting 的桌牌显示参数,配置即时生效、无需重启。
configure_tablecard_color
# 1. 检查镜像压缩包是否存在 # 1. 检查镜像压缩包是否存在
if [ ! -f "$image_tar" ]; then if [ ! -f "$image_tar" ]; then
log "ERROR" "❌ 镜像文件不存在: $image_tar" log "ERROR" "❌ 镜像文件不存在: $image_tar"
...@@ -936,6 +1093,10 @@ function deploy_services() { ...@@ -936,6 +1093,10 @@ function deploy_services() {
log "INFO" "🤖 收到 'all' 参数,自动部署所有系统" log "INFO" "🤖 收到 'all' 参数,自动部署所有系统"
allowed_numbers=(1 2 3 4 5) allowed_numbers=(1 2 3 4 5)
# 非交互模式:桌牌色彩采用默认三色(跳过 whiptail 交互菜单)
# 说明:bash 动态作用域下,该 local 变量对 cardtable_x86 → configure_tablecard_color 可见
local CARDTABLE_COLOR_MODE="3"
# 直接跳到部署流程,跳过所有交互界面 # 直接跳到部署流程,跳过所有交互界面
log "INFO" "==================================================" log "INFO" "=================================================="
log "INFO" "🚀 开始自动部署所有系统" log "INFO" "🚀 开始自动部署所有系统"
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论