chore: 备份测试与运维工具脚本

This commit is contained in:
smail 2026-09-11 18:44:37 +08:00
parent a4dba70d0b
commit 187caebe0c
10 changed files with 451 additions and 0 deletions

75
test/tools/add_dns.py Normal file
View File

@ -0,0 +1,75 @@
import urllib.request, urllib.parse, hmac, hashlib, base64, time, json, paramiko, subprocess
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('47.108.78.12', port=22, username='root', password='10102442Lb!', timeout=10)
stdin, stdout, stderr = client.exec_command('cat /opt/smail/ssl/dkim/dkim.priv')
priv_key = stdout.read().decode('utf-8')
client.close()
p = subprocess.Popen(['openssl', 'rsa', '-pubout', '-outform', 'PEM'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate(input=priv_key.encode('utf-8'))
pub_lines = [line.strip() for line in out.decode('utf-8').splitlines() if '---' not in line and line.strip()]
dkim_pub = ''.join(pub_lines)
dkim_val = 'v=DKIM1; k=rsa; p=' + dkim_pub
print('DKIM Value:', dkim_val)
dmarc_val = 'v=DMARC1; p=quarantine; rua=mailto:admin@solit.cn'
print('DMARC Value:', dmarc_val)
ak = 'ENV_ALIDNS_ACCESS_KEY_ID'
sk = 'ENV_ALIDNS_ACCESS_KEY_SECRET'
def aliyun_request(params):
public_params = {
'Format': 'JSON',
'Version': '2015-01-09',
'AccessKeyId': ak,
'SignatureMethod': 'HMAC-SHA1',
'Timestamp': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
'SignatureVersion': '1.0',
'SignatureNonce': str(int(time.time()*1000)),
}
all_params = {**public_params, **params}
sorted_params = sorted(all_params.items())
query_str = urllib.parse.urlencode(sorted_params)
str_to_sign = 'GET' + chr(38) + '%2F' + chr(38) + urllib.parse.quote(query_str, safe='')
h = hmac.new((sk + chr(38)).encode('utf-8'), str_to_sign.encode('utf-8'), hashlib.sha1)
signature = base64.b64encode(h.digest()).decode('utf-8')
all_params['Signature'] = signature
url = 'https://alidns.aliyuncs.com/?' + urllib.parse.urlencode(all_params)
req = urllib.request.Request(url)
with urllib.request.urlopen(req) as response:
return json.loads(response.read().decode('utf-8'))
records = aliyun_request({'Action': 'DescribeDomainRecords', 'DomainName': 'solit.cn', 'PageSize': 50})
existing = { (r.get('RR'), r.get('Type')): r.get('RecordId') for r in records.get('DomainRecords', {}).get('Record', []) }
print('Existing RRs:', list(existing.keys()))
if ('_dmarc', 'TXT') not in existing:
print('Adding _dmarc...')
res = aliyun_request({
'Action': 'AddDomainRecord',
'DomainName': 'solit.cn',
'RR': '_dmarc',
'Type': 'TXT',
'Value': dmarc_val,
'TTL': 600
})
print('Added _dmarc result:', res)
else:
print('_dmarc already exists')
if ('default._domainkey', 'TXT') not in existing:
print('Adding default._domainkey...')
res = aliyun_request({
'Action': 'AddDomainRecord',
'DomainName': 'solit.cn',
'RR': 'default._domainkey',
'Type': 'TXT',
'Value': dkim_val,
'TTL': 600
})
print('Added default._domainkey result:', res)
else:
print('default._domainkey already exists')

View File

@ -0,0 +1,62 @@
import urllib.request, urllib.parse, hmac, hashlib, base64, time, json
ak = 'ENV_ALIDNS_ACCESS_KEY_ID'
sk = 'ENV_ALIDNS_ACCESS_KEY_SECRET'
def aliyun_request(params):
public_params = {
'Format': 'JSON',
'Version': '2015-01-09',
'AccessKeyId': ak,
'SignatureMethod': 'HMAC-SHA1',
'Timestamp': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
'SignatureVersion': '1.0',
'SignatureNonce': str(int(time.time()*1000)),
}
all_params = {**public_params, **params}
sorted_params = sorted(all_params.items())
query_str = urllib.parse.urlencode(sorted_params)
str_to_sign = 'GET&%2F&' + urllib.parse.quote(query_str, safe='')
h = hmac.new((sk + '&').encode('utf-8'), str_to_sign.encode('utf-8'), hashlib.sha1)
signature = base64.b64encode(h.digest()).decode('utf-8')
all_params['Signature'] = signature
url = 'https://alidns.aliyuncs.com/?' + urllib.parse.urlencode(all_params)
req = urllib.request.Request(url)
try:
with urllib.request.urlopen(req) as response:
return json.loads(response.read().decode('utf-8'))
except urllib.error.HTTPError as e:
print('Aliyun error body:', e.read().decode('utf-8'))
raise e
dkim_pub = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm1F4k0SRUf0aAkIAJewM/oNzARx0XJjCXbA+HZNpqWg0rFpmGzNUsdXaD6Lw3VGok9P2JaE2t8wQxdIqZGX8f1WjXSEYkN2jv4mY2MTECy4Git8gwlwgXG4oBNcfdQ4MXJj+5YYRuqxAZPKhVoJgWbZwpHWmgdoKIoPHJVBRZQ00+K18WqYJC+kXQYGiB6+ztbYIhlZlvgoaPcvApMJE7TdhCulHKSG0DSz+lX4GaYzxGUZuGgKIb5lUBeatSsKsM8WH2eNG2NP+zS/HIdWsppkUh6u4m8V+8K3ieMENPxOsAJ9DSg1GFtCf1WU7KqTmUeQsApZOxbaHWn0W+6nfjwIDAQAB'
dkim_val = 'v=DKIM1; k=rsa; p=' + dkim_pub
dmarc_val = 'v=DMARC1; p=quarantine; rua=mailto:admin@solit.cn'
records = aliyun_request({'Action': 'DescribeDomainRecords', 'DomainName': 'solit.cn', 'PageSize': 50})
existing = { (r.get('RR'), r.get('Type')): r.get('RecordId') for r in records.get('DomainRecords', {}).get('Record', []) }
print('Existing RRs:', list(existing.keys()))
if ('_dmarc', 'TXT') not in existing:
print('Adding _dmarc...')
res = aliyun_request({
'Action': 'AddDomainRecord',
'DomainName': 'solit.cn',
'RR': '_dmarc',
'Type': 'TXT',
'Value': dmarc_val,
'TTL': 600
})
print('Added _dmarc result:', res)
if ('default._domainkey', 'TXT') not in existing:
print('Adding default._domainkey...')
res = aliyun_request({
'Action': 'AddDomainRecord',
'DomainName': 'solit.cn',
'RR': 'default._domainkey',
'Type': 'TXT',
'Value': dkim_val,
'TTL': 600
})
print('Added default._domainkey result:', res)

View File

@ -0,0 +1,62 @@
import urllib.request, urllib.parse, hmac, hashlib, base64, time, json
ak = 'ENV_ALIDNS_ACCESS_KEY_ID'
sk = 'ENV_ALIDNS_ACCESS_KEY_SECRET'
def percent_encode(val):
res = urllib.parse.quote(str(val), safe='')
res = res.replace('+', '%20')
res = res.replace('*', '%2A')
res = res.replace('%7E', '~')
return res
def aliyun_request(params):
public_params = {
'Format': 'JSON',
'Version': '2015-01-09',
'AccessKeyId': ak,
'SignatureMethod': 'HMAC-SHA1',
'Timestamp': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
'SignatureVersion': '1.0',
'SignatureNonce': str(int(time.time()*1000)),
}
all_params = {**public_params, **params}
sorted_params = sorted(all_params.items(), key=lambda x: x[0])
query_str = '&'.join([f'{percent_encode(k)}={percent_encode(v)}' for k, v in sorted_params])
str_to_sign = 'GET&%2F&' + percent_encode(query_str)
h = hmac.new((sk + '&').encode('utf-8'), str_to_sign.encode('utf-8'), hashlib.sha1)
signature = base64.b64encode(h.digest()).decode('utf-8')
final_url = f'https://alidns.aliyuncs.com/?{query_str}&Signature={percent_encode(signature)}'
req = urllib.request.Request(final_url)
try:
with urllib.request.urlopen(req) as response:
return json.loads(response.read().decode('utf-8'))
except urllib.error.HTTPError as e:
print('HTTP Error body:', e.read().decode('utf-8'))
raise e
dkim_pub = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm1F4k0SRUf0aAkIAJewM/oNzARx0XJjCXbA+HZNpqWg0rFpmGzNUsdXaD6Lw3VGok9P2JaE2t8wQxdIqZGX8f1WjXSEYkN2jv4mY2MTECy4Git8gwlwgXG4oBNcfdQ4MXJj+5YYRuqxAZPKhVoJgWbZwpHWmgdoKIoPHJVBRZQ00+K18WqYJC+kXQYGiB6+ztbYIhlZlvgoaPcvApMJE7TdhCulHKSG0DSz+lX4GaYzxGUZuGgKIb5lUBeatSsKsM8WH2eNG2NP+zS/HIdWsppkUh6u4m8V+8K3ieMENPxOsAJ9DSg1GFtCf1WU7KqTmUeQsApZOxbaHWn0W+6nfjwIDAQAB'
dkim_val = 'v=DKIM1; k=rsa; p=' + dkim_pub
dmarc_val = 'v=DMARC1; p=quarantine; rua=mailto:admin@solit.cn'
print('--- 添加 _dmarc ---')
res_dmarc = aliyun_request({
'Action': 'AddDomainRecord',
'DomainName': 'solit.cn',
'RR': '_dmarc',
'Type': 'TXT',
'Value': dmarc_val,
'TTL': 600
})
print('Success:', res_dmarc)
print('--- 添加 default._domainkey ---')
res_dkim = aliyun_request({
'Action': 'AddDomainRecord',
'DomainName': 'solit.cn',
'RR': 'default._domainkey',
'Type': 'TXT',
'Value': dkim_val,
'TTL': 600
})
print('Success:', res_dkim)

View File

@ -0,0 +1,35 @@
import urllib.request, urllib.parse, hmac, hashlib, base64, time, json
ak = 'ENV_ALIDNS_ACCESS_KEY_ID'
sk = 'ENV_ALIDNS_ACCESS_KEY_SECRET'
def aliyun_request(params):
public_params = {
'Format': 'JSON',
'Version': '2015-01-09',
'AccessKeyId': ak,
'SignatureMethod': 'HMAC-SHA1',
'Timestamp': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
'SignatureVersion': '1.0',
'SignatureNonce': str(int(time.time()*1000)),
}
all_params = {**public_params, **params}
sorted_params = sorted(all_params.items())
query_str = urllib.parse.urlencode(sorted_params)
str_to_sign = 'GET&%2F&' + urllib.parse.quote(query_str, safe='')
h = hmac.new((sk + '&').encode('utf-8'), str_to_sign.encode('utf-8'), hashlib.sha1)
signature = base64.b64encode(h.digest()).decode('utf-8')
all_params['Signature'] = signature
url = 'https://alidns.aliyuncs.com/?' + urllib.parse.urlencode(all_params)
req = urllib.request.Request(url)
with urllib.request.urlopen(req) as response:
return json.loads(response.read().decode('utf-8'))
records = aliyun_request({'Action': 'DescribeDomainRecords', 'DomainName': 'solit.cn', 'PageSize': 50})
print('Total records in solit.cn:', records.get('TotalCount'))
for r in records.get('DomainRecords', {}).get('Record', []):
rr = r.get('RR')
t = r.get('Type')
v = r.get('Value')
s = r.get('Status')
print(f'{rr:<22} | {t:<6} | {s:<8} | {v}')

43
test/tools/debug_login.py Normal file
View File

@ -0,0 +1,43 @@
import time, os
from playwright.sync_api import sync_playwright
test_log_dir = os.path.join(os.getcwd(), 'test-log', 'debug-login')
os.makedirs(test_log_dir, exist_ok=True)
with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=600, args=['--start-maximized', '--ignore-certificate-errors'])
context = browser.new_context(viewport=None, ignore_https_errors=True)
page = context.new_page()
page.on('console', lambda msg: print('[CONSOLE]', msg.type, msg.text))
page.on('pageerror', lambda exc: print('[PAGE ERROR]', exc))
def on_res(res):
try:
print('[RES]', res.status, res.url, res.text()[:150])
except Exception:
print('[RES]', res.status, res.url)
page.on('response', on_res)
page.goto('https://47.108.78.12/#/login', wait_until='networkidle')
time.sleep(2)
acc = page.locator('.login-content input').nth(0)
pwd = page.locator('.login-content input').nth(1)
acc.fill('xuans')
pwd.fill('12345678')
time.sleep(1)
btn = page.locator('button.login-btn')
btn.click()
time.sleep(4)
# 检查所有 el-message
messages = page.locator('.el-message')
print('El-Messages count:', messages.count())
for i in range(messages.count()):
print(f'Msg {i}: {messages.nth(i).inner_text()}')
page.screenshot(path=os.path.join(test_log_dir, '04_detailed.png'))
time.sleep(3)
browser.close()

9
test/tools/get_dkim.py Normal file
View File

@ -0,0 +1,9 @@
import paramiko
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('47.108.78.12', port=22, username='root', password='10102442Lb!', timeout=10)
cmd = 'openssl rsa -in /opt/smail/ssl/dkim/dkim.priv -pubout -outform PEM'
stdin, stdout, stderr = client.exec_command(cmd)
lines = [l.strip() for l in stdout.read().decode('utf-8').splitlines() if '---' not in l and l.strip()]
print(''.join(lines))
client.close()

View File

@ -0,0 +1,38 @@
import time, os
from playwright.sync_api import sync_playwright
test_log_dir = os.path.join(os.getcwd(), "test-log", "login-and-dashboard-v2")
os.makedirs(test_log_dir, exist_ok=True)
with sync_playwright() as p:
print("鍚姩鍙鍖栫湡瀹炴祻瑙堝櫒...")
browser = p.chromium.launch(
headless=False,
slow_mo=800,
args=["--start-maximized", "--no-sandbox", "--ignore-certificate-errors"]
)
context = browser.new_context(viewport=None, ignore_https_errors=True)
page = context.new_page()
print("璁块棶鐧诲綍椤? https://47.108.78.12/#/login")
page.goto("https://47.108.78.12/#/login", wait_until="networkidle")
time.sleep(2)
page.screenshot(path=os.path.join(test_log_dir, "01_login_page.png"))
print("杈撳叆绠$悊鍛樿处鍙峰瘑鐮? xuans / 12345678")
inputs = page.locator("input")
print("杈撳叆妗嗘暟閲?", inputs.count())
inputs.nth(0).fill("xuans")
inputs.nth(1).fill("12345678")
time.sleep(1)
page.screenshot(path=os.path.join(test_log_dir, "02_credentials_filled.png"))
print("鐐瑰嚮鐧诲綍鎸夐挳...")
page.locator(".login-btn").click()
time.sleep(3)
page.screenshot(path=os.path.join(test_log_dir, "03_after_login_action.png"))
print("褰撳墠椤甸潰 URL:", page.url)
time.sleep(2)
page.screenshot(path=os.path.join(test_log_dir, "04_security_dialog_or_dashboard.png"))
time.sleep(5)
browser.close()

View File

@ -0,0 +1,38 @@
import time, os
from playwright.sync_api import sync_playwright
test_log_dir = os.path.join(os.getcwd(), "test-log", "login-and-dashboard-v2")
os.makedirs(test_log_dir, exist_ok=True)
with sync_playwright() as p:
print("å<EFBFBD>¯åЍå<EFBFBD>¯è§†åŒ–真实æµ<EFBFBD>览器...")
browser = p.chromium.launch(
headless=False,
slow_mo=800,
args=["--start-maximized", "--no-sandbox", "--ignore-certificate-errors"]
)
context = browser.new_context(viewport=None, ignore_https_errors=True)
page = context.new_page()
print("访问登录� https://47.108.78.12/#/login")
page.goto("https://47.108.78.12/#/login", wait_until="networkidle")
time.sleep(2)
page.screenshot(path=os.path.join(test_log_dir, "01_login_page.png"))
print("输入管ç<EFBFBD>†å‘˜è´¦å<EFBFBD>·å¯†ç ? xuans / 12345678")
inputs = page.locator("input")
print("输入框数�", inputs.count())
inputs.nth(0).fill("xuans")
inputs.nth(1).fill("12345678")
time.sleep(1)
page.screenshot(path=os.path.join(test_log_dir, "02_credentials_filled.png"))
print("点击登录按钮...")
page.locator("button").filter(has_text="登录").first.click()
time.sleep(3)
page.screenshot(path=os.path.join(test_log_dir, "03_after_login_action.png"))
print("当å‰<EFBFBD>页é<EFBFBD>¢ URL:", page.url)
time.sleep(2)
page.screenshot(path=os.path.join(test_log_dir, "04_security_dialog_or_dashboard.png"))
time.sleep(5)
browser.close()

View File

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
from playwright.sync_api import sync_playwright
import time
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context(ignore_https_errors=True)
page = context.new_page()
page.on("request", lambda req: print("[REQ]", req.method, req.url) if "api" in req.url else None)
page.on("response", lambda res: print("[RES]", res.status, res.url, res.text()) if "question" in res.url else None)
# 登录
page.goto("https://mail.solit.cn/#/login")
page.wait_for_selector(".login-btn")
inputs = page.locator(".login-content input")
inputs.nth(0).fill("xuans")
inputs.nth(1).fill("12345678")
page.locator(".login-btn").click()
page.wait_for_timeout(3000)
# 直接访问设置安全路由
page.goto("https://mail.solit.cn/#/setting?tab=security")
page.wait_for_timeout(2000)
# 查看当前页面按钮和文本
print("Page URL:", page.url)
# 点击密保问题选项卡
page.locator(".settings-submenu-item:has-text('密保问题')").click()
page.wait_for_timeout(1000)
q_input = page.locator("input[placeholder*='最喜欢的颜色']")
a_input = page.locator("input[placeholder*='请输入答案']")
print("q_input visible:", q_input.is_visible())
print("a_input visible:", a_input.is_visible())
# 尝试两字符输入:90 和 12
print("--- 测试 1:问题 '90',答案 '12' ---")
q_input.fill("90")
a_input.fill("12")
page.locator("button:has-text('设置密保问题')").click()
page.wait_for_timeout(2000)
# 尝试8字符输入:90 和 12345678
print("--- 测试 2:问题 '90',答案 '12345678' ---")
a_input.fill("12345678")
page.locator("button:has-text('设置密保问题')").click()
page.wait_for_timeout(2000)
browser.close()

View File

@ -0,0 +1,38 @@
import time, os
from playwright.sync_api import sync_playwright
test_log_dir = os.path.join(os.getcwd(), 'test-log', 'domain-login-acceptance-v2')
os.makedirs(test_log_dir, exist_ok=True)
with sync_playwright() as p:
browser = p.chromium.launch(
headless=False,
slow_mo=600,
args=['--start-maximized', '--no-sandbox']
)
context = browser.new_context(viewport=None)
page = context.new_page()
page.on('console', lambda msg: print('[CONSOLE]', msg.type, msg.text))
page.on('pageerror', lambda exc: print('[ERROR]', exc))
page.on('request', lambda req: print('[REQ]', req.method, req.url) if 'api' in req.url else None)
page.on('response', lambda res: print('[RES]', res.status, res.url) if 'api' in res.url else None)
page.goto('https://mail.solit.cn/#/login', wait_until='networkidle')
time.sleep(2)
acc = page.locator('.login-content input').nth(0)
pwd = page.locator('.login-content input').nth(1)
acc.fill('xuans')
pwd.fill('12345678')
time.sleep(1)
page.locator('button.login-btn').click()
time.sleep(5)
page.screenshot(path=os.path.join(test_log_dir, 'after_login.png'))
print('URL after login:', page.url)
# 检查页面上的文本或状态
time.sleep(10)
browser.close()