清理不应该存在的照片、脚本

This commit is contained in:
xuans 2026-10-18 12:29:46 +08:00
parent a5b7832d53
commit 17004786b2
66 changed files with 0 additions and 1230 deletions

View File

@ -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` 的“二十七、首轮开发复审记录”为准。

View File

@ -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")

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 447 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 KiB

View File

@ -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!")

View File

View File

@ -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()

View File

@ -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()

View File

@ -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")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

View File

@ -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()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 KiB

View File

@ -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()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

View File

@ -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.")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 163 KiB

View File

@ -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()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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")

View File

@ -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")

View File

@ -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!")

View File

@ -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()