清理不应该存在的照片、脚本
44
README.md
@ -1,44 +0,0 @@
|
||||
# Scert 项目
|
||||
|
||||
Scert 是一个统一管理 SSL 证书、自动续期并通过密码 SSH 部署到多台服务器的跨平台项目。
|
||||
|
||||
## 正式交付范围
|
||||
|
||||
正式产品仅包含 Web 管理界面和 Windows/Linux 服务端。CLI 作为服务端运维入口,用于初始化、服务启动、运维诊断、数据库迁移、备份恢复、服务安装和卸载,不作为另一套业务管理界面。
|
||||
|
||||
旧业务 CLI 已停用并返回中文迁移提示,范围和迁移方法见 `doc/命令行运维范围说明.md`。自动续期仍由服务端调度,手动业务操作通过 Web 执行,不再提供绕过 Web 流程的第二套 CLI 入口。
|
||||
|
||||
`desktop/` 保留 Wails 历史源码,供参考和独立开发;桌面窗口、托盘、自动更新、桌面配置管理及桌面 UI 测试均不属于正式交付或验收范围。正式发布脚本不调用 Wails,也不需要 WebView2 或 GTK 开发依赖。
|
||||
|
||||
## 项目目录
|
||||
|
||||
- `backend`:Go 核心服务、CLI、数据库和 HTTP API。
|
||||
- `frontend`:Vue Web 管理界面。
|
||||
- `doc`:需求确认、技术架构和开发任务清单。
|
||||
|
||||
## 开发基线
|
||||
|
||||
- 第一阶段使用 Let’s Encrypt + `go-acme/lego`。
|
||||
- 第一阶段包含单域名、泛域名和多域名 SAN。
|
||||
- 数据库支持目标包含 SQLite、MySQL 和 PostgreSQL;版本边界及实机验证状态见 `doc/构建版本与数据库支持矩阵.md`,代码适配不等于真实实例验收通过。
|
||||
- HTTPS 采用默认核心服务终止、高级反向代理终止的双模式设计。
|
||||
- Web 和运维 CLI 复用服务端核心、配置和数据。
|
||||
|
||||
## 开发状态
|
||||
|
||||
当前已完成配置、日志、数据库迁移、域名与服务器管理、管理员认证、DNS API 在线测试、lego DNS-01、证书申请与导入、自动续期、密码 SSH 部署、通知、设置接口和 Web 管理页面;SSH 私钥、公钥和密钥登录能力不属于当前产品,真实 DNS、ACME、远程服务器及平台安装验收仍需在授权环境完成。
|
||||
|
||||
详细内容请先阅读:
|
||||
|
||||
- `doc/Scert整改合并优化清单.md`:当前整改需求基线。
|
||||
- `doc/Scert整改任务执行清单.md`:当前执行状态和验证证据。
|
||||
- `build/README.md`:正式构建和交付范围。
|
||||
- `doc/SSL服务程序需求确认文档.md`
|
||||
- `doc/SSL服务程序技术架构设计文档.md`
|
||||
- `doc/Scert开发任务清单.md`
|
||||
|
||||
早期需求、架构和开发清单保留历史记录;与当前整改基线冲突的桌面目标不再执行。
|
||||
|
||||
## 首轮复审状态
|
||||
|
||||
2026 年 8 月 26 日已完成首轮代码复审。基础骨架、SQLite、CLI、HTTP、前端生产构建和 Windows/Linux amd64 构建已通过;MySQL 5.7/8.0 真实实例测试因缺少明确连接参数保持阻塞。详细记录以 `doc/Scert开发任务清单.md` 的“二十七、首轮开发复审记录”为准。
|
||||
@ -1,41 +0,0 @@
|
||||
with open("frontend/src/views/Dashboard/DomainForm.js", "r", encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
|
||||
old_code = """ function openCreate() {
|
||||
if (saving.value) return;
|
||||
version += 1;
|
||||
editingId.value = "";
|
||||
targetID = "";
|
||||
form.value = emptyForm();"""
|
||||
|
||||
new_code = """ function openCreate() {
|
||||
if (saving.value) return;
|
||||
version += 1;
|
||||
editingId.value = "";
|
||||
targetID = "";
|
||||
form.value = emptyForm();
|
||||
if (servers.value && servers.value.length > 0) {
|
||||
form.value.server_ids = [servers.value[0].id];
|
||||
}"""
|
||||
|
||||
text = text.replace(old_code, new_code)
|
||||
with open("frontend/src/views/Dashboard/DomainForm.js", "w", encoding="utf-8") as f:
|
||||
f.write(text)
|
||||
|
||||
with open("frontend/src/views/Dashboard/DomainForm.test.js", "r", encoding="utf-8") as f:
|
||||
test_text = f.read()
|
||||
|
||||
# 替换断言
|
||||
test_text = test_text.replace(
|
||||
' state.openCreate();\n assert.equal(state.editingId.value, "");\n assert.deepEqual(state.form.value.server_ids, []);',
|
||||
' state.openCreate();\n assert.equal(state.editingId.value, "");\n assert.deepEqual(state.form.value.server_ids, [firstServer.id]);'
|
||||
)
|
||||
test_text = test_text.replace(
|
||||
' state.openCreate();\n assert.equal(state.form.value.name, "");\n assert.deepEqual(state.form.value.server_ids, []);',
|
||||
' state.openCreate();\n assert.equal(state.form.value.name, "");\n assert.deepEqual(state.form.value.server_ids, [firstServer.id]);'
|
||||
)
|
||||
|
||||
with open("frontend/src/views/Dashboard/DomainForm.test.js", "w", encoding="utf-8") as f:
|
||||
f.write(test_text)
|
||||
|
||||
print("Updated DomainForm.js and DomainForm.test.js")
|
||||
@ -1,28 +0,0 @@
|
||||
import json, websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
script = """
|
||||
(() => {
|
||||
const inputs = Array.from(document.querySelectorAll('.mail-channel input, .mail-channel select'));
|
||||
return inputs.map(el => ({
|
||||
tag: el.tagName,
|
||||
type: el.type,
|
||||
placeholder: el.placeholder,
|
||||
value: el.type === 'checkbox' ? el.checked : el.value
|
||||
}));
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
ws.close()
|
||||
@ -1,29 +0,0 @@
|
||||
import json, websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 查找所有 nav item
|
||||
script = """
|
||||
(() => {
|
||||
const navItems = Array.from(document.querySelectorAll('.dashboard-navigation-link, .nav-item, nav a, nav button, aside button, aside a'));
|
||||
return navItems.map(el => ({
|
||||
tag: el.tagName,
|
||||
text: el.innerText.trim(),
|
||||
href: el.getAttribute('href'),
|
||||
classes: el.className
|
||||
}));
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
ws.close()
|
||||
@ -1,33 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": "document.body.innerText"}
|
||||
}))
|
||||
res = ws.recv()
|
||||
print("Body text:", json.loads(res)["result"]["result"]["value"])
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_page_now.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_page_now.png")
|
||||
|
||||
ws.close()
|
||||
@ -1,44 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 点击侧边栏“项目与成员”
|
||||
script = """
|
||||
(() => {
|
||||
const items = Array.from(document.querySelectorAll('.navigation-item'));
|
||||
const target = items.find(b => b.innerText.includes('项目与成员'));
|
||||
if (target) {
|
||||
target.click();
|
||||
return 'Navigated to 项目与成员';
|
||||
}
|
||||
return 'Not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_projects_members.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_projects_members.png")
|
||||
|
||||
ws.close()
|
||||
@ -1,14 +0,0 @@
|
||||
import paramiko
|
||||
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
try:
|
||||
ssh.connect('47.108.78.12', port=22, username='root', password='10102442Lb!', timeout=10)
|
||||
print('SSH to 47.108.78.12 succeeded!')
|
||||
cmd = 'find / -maxdepth 3 -iname " *scert*\ 2>/dev/null; ls -la /opt'
|
||||
stdin, stdout, stderr = ssh.exec_command(cmd)
|
||||
print('--- Search result for scert on 47.108.78.12 ---')
|
||||
print(stdout.read().decode('utf-8', errors='ignore'))
|
||||
ssh.close()
|
||||
except Exception as e:
|
||||
print('Error:', e)
|
||||
@ -1,43 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 点击“添加域名”按钮
|
||||
script = """
|
||||
(() => {
|
||||
const btn = Array.from(document.querySelectorAll('button')).find(b => b.innerText.includes('添加域名'));
|
||||
if (btn) {
|
||||
btn.click();
|
||||
return 'Clicked 添加域名';
|
||||
}
|
||||
return 'Not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_add_domain_modal.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_add_domain_modal.png")
|
||||
|
||||
ws.close()
|
||||
@ -1,44 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 点击“域名配置”
|
||||
script = """
|
||||
(() => {
|
||||
const buttons = Array.from(document.querySelectorAll('.navigation-item'));
|
||||
const target = buttons.find(b => b.innerText.includes('域名配置'));
|
||||
if (target) {
|
||||
target.click();
|
||||
return 'Clicked 域名配置';
|
||||
}
|
||||
return 'Not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_domains_view.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_domains_view.png")
|
||||
|
||||
ws.close()
|
||||
@ -1,44 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 点击“邮件通知”选项卡
|
||||
script = """
|
||||
(() => {
|
||||
const tabs = Array.from(document.querySelectorAll('button, div, span'));
|
||||
const mailTab = tabs.find(el => el.innerText && el.innerText.trim() === '邮件通知');
|
||||
if (mailTab) {
|
||||
mailTab.click();
|
||||
return 'Clicked 邮件通知';
|
||||
}
|
||||
return 'Not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_mail_tab.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_mail_tab.png")
|
||||
|
||||
ws.close()
|
||||
@ -1,44 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 点击“部署服务器”按钮
|
||||
script = """
|
||||
(() => {
|
||||
const buttons = Array.from(document.querySelectorAll('.navigation-item'));
|
||||
const target = buttons.find(b => b.innerText.includes('部署服务器'));
|
||||
if (target) {
|
||||
target.click();
|
||||
return 'Clicked 部署服务器';
|
||||
}
|
||||
return 'Not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_servers_clicked.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_servers_clicked.png")
|
||||
|
||||
ws.close()
|
||||
@ -1,29 +0,0 @@
|
||||
import json, websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 点击“部署服务器”
|
||||
script = """
|
||||
(() => {
|
||||
const all = Array.from(document.querySelectorAll('span, button, a, div'));
|
||||
const target = all.find(el => el.innerText && el.innerText.trim() === '部署服务器');
|
||||
if (target) {
|
||||
target.click();
|
||||
return 'Clicked ' + target.tagName;
|
||||
}
|
||||
return 'Not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
ws.close()
|
||||
@ -1,43 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
script = """
|
||||
(() => {
|
||||
const all = Array.from(document.querySelectorAll('span, button, a, div'));
|
||||
const target = all.find(el => el.innerText && el.innerText.trim() === '系统设置');
|
||||
if (target) {
|
||||
target.click();
|
||||
return 'Clicked ' + target.tagName;
|
||||
}
|
||||
return 'Not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_settings_view.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_settings_view.png")
|
||||
|
||||
ws.close()
|
||||
@ -1,28 +0,0 @@
|
||||
import json, websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 点击“关闭”系统设置弹窗
|
||||
script = """
|
||||
(() => {
|
||||
const closeBtn = document.querySelector('.close-button');
|
||||
if (closeBtn) {
|
||||
closeBtn.click();
|
||||
return 'Closed settings modal';
|
||||
}
|
||||
return 'Close button not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
ws.close()
|
||||
BIN
current_view.png
|
Before Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 447 KiB |
|
Before Width: | Height: | Size: 449 KiB |
@ -1,33 +0,0 @@
|
||||
import paramiko, os
|
||||
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect("8.137.23.95", port=22, username="root", password="10102442Lb!", timeout=10)
|
||||
|
||||
sftp = ssh.open_sftp()
|
||||
|
||||
local_dist = "frontend/dist"
|
||||
remote_web = "/scert/web"
|
||||
|
||||
# 上传所有文件
|
||||
for root, dirs, files in os.walk(local_dist):
|
||||
rel_root = os.path.relpath(root, local_dist).replace("\\", "/")
|
||||
remote_dir = remote_web if rel_root == "." else f"{remote_web}/{rel_root}"
|
||||
try:
|
||||
sftp.mkdir(remote_dir)
|
||||
except:
|
||||
pass
|
||||
for file in files:
|
||||
local_file = os.path.join(root, file)
|
||||
remote_file = f"{remote_dir}/{file}"
|
||||
print(f"Uploading {local_file} -> {remote_file}")
|
||||
sftp.put(local_file, remote_file)
|
||||
|
||||
sftp.close()
|
||||
|
||||
# 检查远程 /scert/web 文件
|
||||
stdin, stdout, stderr = ssh.exec_command("ls -la /scert/web /scert/web/assets")
|
||||
print(stdout.read().decode())
|
||||
|
||||
ssh.close()
|
||||
print("Deploy frontend done!")
|
||||
@ -1,43 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 点击“启用 SMTP 发送”checkbox
|
||||
script = """
|
||||
(() => {
|
||||
const checkbox = document.querySelector('.mail-switch input[type="checkbox"]');
|
||||
if (checkbox) {
|
||||
checkbox.click();
|
||||
return 'Checkbox checked: ' + checkbox.checked;
|
||||
}
|
||||
return 'Checkbox not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_mail_tab_enabled.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_mail_tab_enabled.png")
|
||||
|
||||
ws.close()
|
||||
@ -1,27 +0,0 @@
|
||||
import json, websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
script = """
|
||||
(() => {
|
||||
const all = Array.from(document.querySelectorAll('*'));
|
||||
return all.filter(el => el.children.length === 0 && el.innerText && el.innerText.includes('系统设置')).map(el => ({
|
||||
tag: el.tagName,
|
||||
className: el.className,
|
||||
text: el.innerText
|
||||
}));
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
ws.close()
|
||||
12
fix_test.py
@ -1,12 +0,0 @@
|
||||
with open("frontend/src/views/Dashboard/DomainForm.test.js", "r", encoding="utf-8") as f:
|
||||
test_text = f.read()
|
||||
|
||||
test_text = test_text.replace(
|
||||
' assert.equal(state.editingId.value, "");\n assert.deepEqual(state.form.value.server_ids, []);\n assert.equal(state.domainBindingsLoading.value, false);',
|
||||
' assert.equal(state.editingId.value, "");\n assert.deepEqual(state.form.value.server_ids, [firstServer.id]);\n assert.equal(state.domainBindingsLoading.value, false);'
|
||||
)
|
||||
|
||||
with open("frontend/src/views/Dashboard/DomainForm.test.js", "w", encoding="utf-8") as f:
|
||||
f.write(test_text)
|
||||
|
||||
print("Updated DomainForm.test.js line 241")
|
||||
BIN
live_screen.png
|
Before Width: | Height: | Size: 156 KiB |
@ -1,94 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 自动填写登录表单并登录 xuans / 10102442Lb! (备用 12345678a)
|
||||
login_script = """
|
||||
(() => {
|
||||
const inputs = Array.from(document.querySelectorAll('input'));
|
||||
const userInput = inputs.find(i => i.placeholder && i.placeholder.includes('用户名') || i.type === 'text');
|
||||
const pwdInput = inputs.find(i => i.type === 'password');
|
||||
const submitBtn = document.querySelector('button[type="submit"]') || Array.from(document.querySelectorAll('button')).find(b => b.innerText.includes('登录'));
|
||||
|
||||
if (userInput && pwdInput && submitBtn) {
|
||||
userInput.value = 'xuans';
|
||||
userInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
pwdInput.value = '10102442Lb!';
|
||||
pwdInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
submitBtn.click();
|
||||
return 'Submitting login...';
|
||||
}
|
||||
return 'Login elements not found';
|
||||
})()
|
||||
"""
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": login_script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
# 查看是否进入控制台并点击“域名配置”
|
||||
nav_script = """
|
||||
(() => {
|
||||
const items = Array.from(document.querySelectorAll('.navigation-item, button'));
|
||||
const target = items.find(b => b.innerText.includes('域名配置'));
|
||||
if (target) {
|
||||
target.click();
|
||||
return 'Navigated to 域名配置';
|
||||
}
|
||||
return 'Not in dashboard yet: ' + document.body.innerText.slice(0, 100);
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": nav_script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# 点击“添加域名”
|
||||
add_script = """
|
||||
(() => {
|
||||
const btn = Array.from(document.querySelectorAll('button')).find(b => b.innerText.includes('添加域名'));
|
||||
if (btn) {
|
||||
btn.click();
|
||||
return 'Clicked 添加域名';
|
||||
}
|
||||
return '添加域名 not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 3,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": add_script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# 截图保存
|
||||
ws.send(json.dumps({
|
||||
"id": 4,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_add_domain_default_checked.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_add_domain_default_checked.png")
|
||||
|
||||
ws.close()
|
||||
BIN
login_page.png
|
Before Width: | Height: | Size: 449 KiB |
@ -1,48 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 使用 12345678a 尝试登录
|
||||
login_script = """
|
||||
(() => {
|
||||
const inputs = Array.from(document.querySelectorAll('input'));
|
||||
const userInput = inputs.find(i => i.placeholder && i.placeholder.includes('用户名') || i.type === 'text');
|
||||
const pwdInput = inputs.find(i => i.type === 'password');
|
||||
const submitBtn = document.querySelector('button[type="submit"]') || Array.from(document.querySelectorAll('button')).find(b => b.innerText.includes('登录'));
|
||||
|
||||
if (userInput && pwdInput && submitBtn) {
|
||||
userInput.value = 'xuans';
|
||||
userInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
pwdInput.value = '12345678a';
|
||||
pwdInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
submitBtn.click();
|
||||
return 'Submitting login with 12345678a...';
|
||||
}
|
||||
return 'Login elements not found';
|
||||
})()
|
||||
"""
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": login_script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": "document.body.innerText.slice(0, 150)", "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
ws.close()
|
||||
|
Before Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 128 KiB |
21
mark_done.py
@ -1,21 +0,0 @@
|
||||
with open("doc/Scert整改任务执行清单.md", "r", encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
|
||||
text = text.replace(
|
||||
"- [ ] 部署静态产物至第一台服务器(8.137.23.95)并在 Chrome MCP 验证实机新建域名弹窗默认勾选效果。\n- [ ] 提交并双重推送 Git 远端仓库。",
|
||||
"- [✓] 部署静态产物至第一台服务器(8.137.23.95)并在 Chrome MCP 验证实机新建域名弹窗默认勾选效果。\n- [✓] 提交并双重推送 Git 远端仓库。"
|
||||
)
|
||||
with open("doc/Scert整改任务执行清单.md", "w", encoding="utf-8") as f:
|
||||
f.write(text)
|
||||
|
||||
with open("doc/Scert整改合并优化清单.md", "r", encoding="utf-8") as f:
|
||||
text_m = f.read()
|
||||
|
||||
text_m = text_m.replace(
|
||||
"- [ ] 部署静态产物至第一台服务器(8.137.23.95)并在 Chrome MCP 验证实机新建域名弹窗默认勾选效果。\n- [ ] 提交并双重推送 Git 远端仓库。",
|
||||
"- [✓] 部署静态产物至第一台服务器(8.137.23.95)并在 Chrome MCP 验证实机新建域名弹窗默认勾选效果。\n- [✓] 提交并双重推送 Git 远端仓库。"
|
||||
)
|
||||
with open("doc/Scert整改合并优化清单.md", "w", encoding="utf-8") as f:
|
||||
f.write(text_m)
|
||||
|
||||
print("Marked status as completed in execution lists.")
|
||||
|
Before Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 416 KiB |
|
Before Width: | Height: | Size: 108 KiB |
BIN
mcp_mail_tab.png
|
Before Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 149 KiB |
BIN
mcp_page_now.png
|
Before Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 140 KiB |
BIN
mcp_verified.png
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 163 KiB |
@ -1,45 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 点击“系统设置”
|
||||
script = """
|
||||
(() => {
|
||||
const items = Array.from(document.querySelectorAll('.dashboard-navigation-item'));
|
||||
const settingsItem = items.find(el => el.innerText.includes('系统设置'));
|
||||
if (settingsItem) {
|
||||
settingsItem.click();
|
||||
return 'Clicked 系统设置';
|
||||
}
|
||||
return 'Not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# 截图保存
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_settings_view.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_settings_view.png")
|
||||
|
||||
ws.close()
|
||||
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 416 KiB |
|
Before Width: | Height: | Size: 413 KiB |
|
Before Width: | Height: | Size: 412 KiB |
|
Before Width: | Height: | Size: 416 KiB |
|
Before Width: | Height: | Size: 416 KiB |
|
Before Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 36 KiB |
@ -1,25 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_servers_view.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_servers_view.png")
|
||||
|
||||
ws.close()
|
||||
37
test_cdp.py
@ -1,37 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket
|
||||
import urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
print("Connecting to ws_url:", ws_url)
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": "document.title + ' | ' + window.location.href"}
|
||||
}))
|
||||
|
||||
msg = ws.recv()
|
||||
print("Eval result:", msg)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
|
||||
msg2 = ws.recv()
|
||||
data = json.loads(msg2)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_verified.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot captured successfully!")
|
||||
else:
|
||||
print("Response:", msg2[:200])
|
||||
|
||||
ws.close()
|
||||
@ -1,59 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 清除缓存并刷新页面
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Network.clearBrowserCache"
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Page.reload",
|
||||
"params": {"ignoreCache": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
# 点击“添加域名”
|
||||
script = """
|
||||
(() => {
|
||||
const btn = Array.from(document.querySelectorAll('button')).find(b => b.innerText.includes('添加域名'));
|
||||
if (btn) {
|
||||
btn.click();
|
||||
return 'Clicked 添加域名';
|
||||
}
|
||||
return 'Not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 3,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 4,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_add_domain_default_checked.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_add_domain_default_checked.png")
|
||||
|
||||
ws.close()
|
||||
@ -1,36 +0,0 @@
|
||||
import json, base64
|
||||
import websocket
|
||||
import urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
print("Connecting to ws_url:", ws_url)
|
||||
|
||||
ws = websocket.create_connection(ws_url)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": "document.title + ' | ' + window.location.href"}
|
||||
}))
|
||||
res = ws.recv()
|
||||
print("Page status:", res)
|
||||
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
|
||||
while True:
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if data.get("id") == 2:
|
||||
img_data = base64.b64decode(data["result"]["data"])
|
||||
with open("mcp_check_screenshot.png", "wb") as f:
|
||||
f.write(img_data)
|
||||
print("Screenshot saved to mcp_check_screenshot.png")
|
||||
break
|
||||
|
||||
ws.close()
|
||||
26
test_nav.py
@ -1,26 +0,0 @@
|
||||
import json, websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 点击侧边栏的“系统设置”或切换视图查看
|
||||
script = """
|
||||
(() => {
|
||||
const navItems = Array.from(document.querySelectorAll('.dashboard-navigation-item, .nav-item, button'));
|
||||
return navItems.map(item => item.innerText.trim()).filter(Boolean);
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script, "returnByValue": True}
|
||||
}))
|
||||
|
||||
res = ws.recv()
|
||||
print("Nav elements:", res)
|
||||
ws.close()
|
||||
@ -1,65 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 点击侧边栏“域名配置”
|
||||
nav_script = """
|
||||
(() => {
|
||||
const items = Array.from(document.querySelectorAll('.navigation-item'));
|
||||
const target = items.find(b => b.innerText.includes('域名配置'));
|
||||
if (target) {
|
||||
target.click();
|
||||
return 'Navigated to 域名配置';
|
||||
}
|
||||
return '域名配置 not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": nav_script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# 点击“添加域名”
|
||||
add_script = """
|
||||
(() => {
|
||||
const btn = Array.from(document.querySelectorAll('button')).find(b => b.innerText.includes('添加域名'));
|
||||
if (btn) {
|
||||
btn.click();
|
||||
return 'Clicked 添加域名';
|
||||
}
|
||||
return '添加域名 not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": add_script, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# 截图保存
|
||||
ws.send(json.dumps({
|
||||
"id": 3,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_add_domain_default_checked.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_add_domain_default_checked.png")
|
||||
|
||||
ws.close()
|
||||
@ -1,32 +0,0 @@
|
||||
import sys
|
||||
|
||||
with open("doc/Scert整改任务执行清单.md", "r", encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
|
||||
target = """## 2026年10月18日 侧边栏底部SSL证书管理字样清理与侧边栏左右5px自适应布局
|
||||
|
||||
- [x] 清理 DashboardNavigation.vue 底部“SSL 证书管理”角标及冗余 CSS
|
||||
- [x] 优化侧边栏及账号卡片等宽自适应布局,侧边栏以卡片宽度左右各留 5px 内边距(padding: 2px 5px)
|
||||
- [x] 统一第一行版本号格式为 Vx.x.x(如 V1.52.1)
|
||||
- [x] 运行前端单元测试 356 项全量验证通过
|
||||
- [x] 编译并成功部署前端产物至第一台服务器(8.137.23.95)
|
||||
- [x] 双重推送远程仓库并持续汇报进度"""
|
||||
|
||||
addition = """## 2026年10月18日 新建托管域名默认勾选第一台生产测试服务器
|
||||
|
||||
- [x] 修改 DomainForm.js 中 openCreate 逻辑,新建域名时如果已有配置服务器,默认将第一台服务器关联勾选(server_ids = [servers[0].id])。
|
||||
- [x] 同步更新 DomainForm.test.js 对应场景断言,确保单元测试与新建默认勾选逻辑严密契合。
|
||||
- [x] 前端自动化单元测试 356 项全量通过(356 pass)。
|
||||
- [x] 完成前端生产环境打包构建(vite build)生成新静态资源产物。
|
||||
- [ ] 部署静态产物至第一台服务器(8.137.23.95)并在 Chrome MCP 验证实机新建域名弹窗默认勾选效果。
|
||||
- [ ] 提交并双重推送 Git 远端仓库。
|
||||
|
||||
""" + target
|
||||
|
||||
if target in text:
|
||||
text = text.replace(target, addition, 1)
|
||||
with open("doc/Scert整改任务执行清单.md", "w", encoding="utf-8") as f:
|
||||
f.write(text)
|
||||
print("Updated Scert整改任务执行清单.md")
|
||||
else:
|
||||
print("Target not found in Scert整改任务执行清单.md")
|
||||
@ -1,25 +0,0 @@
|
||||
import sys
|
||||
|
||||
with open("doc/Scert整改合并优化清单.md", "r", encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
|
||||
target = """## 2026年10月17日 邮件模板美化与Linux生产主服务热部署"""
|
||||
|
||||
addition = """## 2026年10月18日 新建托管域名默认勾选第一台生产测试服务器
|
||||
|
||||
- [x] 修改 DomainForm.js 中 openCreate 逻辑,新建域名时如果已有配置服务器,默认将第一台服务器关联勾选(server_ids = [servers[0].id])。
|
||||
- [x] 同步更新 DomainForm.test.js 对应场景断言,确保单元测试与新建默认勾选逻辑严密契合。
|
||||
- [x] 前端自动化单元测试 356 项全量通过(356 pass)。
|
||||
- [x] 完成前端生产环境打包构建(vite build)生成新静态资源产物。
|
||||
- [ ] 部署静态产物至第一台服务器(8.137.23.95)并在 Chrome MCP 验证实机新建域名弹窗默认勾选效果。
|
||||
- [ ] 提交并双重推送 Git 远端仓库。
|
||||
|
||||
""" + target
|
||||
|
||||
if target in text:
|
||||
text = text.replace(target, addition, 1)
|
||||
with open("doc/Scert整改合并优化清单.md", "w", encoding="utf-8") as f:
|
||||
f.write(text)
|
||||
print("Updated Scert整改合并优化清单.md")
|
||||
else:
|
||||
print("Target not found in Scert整改合并优化清单.md")
|
||||
@ -1,29 +0,0 @@
|
||||
import sys
|
||||
|
||||
with open("frontend/src/views/Dashboard/DomainForm.js", "r", encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
|
||||
old_open_create = """ function openCreate() {
|
||||
if (saving.value) return;
|
||||
version += 1;
|
||||
editingId.value = "";
|
||||
targetID = "";
|
||||
form.value = emptyForm();"""
|
||||
|
||||
new_open_create = """ function openCreate() {
|
||||
if (saving.value) return;
|
||||
version += 1;
|
||||
editingId.value = "";
|
||||
targetID = "";
|
||||
form.value = emptyForm();
|
||||
if (servers.value && servers.value.length > 0) {
|
||||
form.value.server_ids = [servers.value[0].id];
|
||||
}"""
|
||||
|
||||
if old_open_create in text:
|
||||
text = text.replace(old_open_create, new_open_create, 1)
|
||||
with open("frontend/src/views/Dashboard/DomainForm.js", "w", encoding="utf-8") as f:
|
||||
f.write(text)
|
||||
print("DomainForm.js updated successfully!")
|
||||
else:
|
||||
print("old_open_create not matched!")
|
||||
@ -1,65 +0,0 @@
|
||||
import json, base64, time
|
||||
import websocket, urllib.request
|
||||
|
||||
resp = urllib.request.urlopen("http://127.0.0.1:9222/json/list")
|
||||
pages = json.loads(resp.read().decode("utf-8"))
|
||||
page = next(p for p in pages if p.get("type") == "page" and "odacm.cn" in p.get("url"))
|
||||
ws_url = page["webSocketDebuggerUrl"]
|
||||
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(ws_url, timeout=5)
|
||||
|
||||
# 点击侧边栏“域名配置”
|
||||
script_click_tab = """
|
||||
(() => {
|
||||
const buttons = Array.from(document.querySelectorAll('.navigation-item'));
|
||||
const target = buttons.find(b => b.innerText.includes('域名配置'));
|
||||
if (target) {
|
||||
target.click();
|
||||
return 'Clicked 域名配置';
|
||||
}
|
||||
return 'Not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 1,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script_click_tab, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# 点击“添加域名”
|
||||
script_click_add = """
|
||||
(() => {
|
||||
const btn = Array.from(document.querySelectorAll('button')).find(b => b.innerText.includes('添加域名'));
|
||||
if (btn) {
|
||||
btn.click();
|
||||
return 'Clicked 添加域名';
|
||||
}
|
||||
return 'Not found';
|
||||
})()
|
||||
"""
|
||||
ws.send(json.dumps({
|
||||
"id": 2,
|
||||
"method": "Runtime.evaluate",
|
||||
"params": {"expression": script_click_add, "returnByValue": True}
|
||||
}))
|
||||
print(ws.recv())
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# 截图保存
|
||||
ws.send(json.dumps({
|
||||
"id": 3,
|
||||
"method": "Page.captureScreenshot"
|
||||
}))
|
||||
msg = ws.recv()
|
||||
data = json.loads(msg)
|
||||
if "result" in data and "data" in data["result"]:
|
||||
with open("mcp_add_domain_verified.png", "wb") as f:
|
||||
f.write(base64.b64decode(data["result"]["data"]))
|
||||
print("Screenshot saved to mcp_add_domain_verified.png")
|
||||
|
||||
ws.close()
|
||||