From 187caebe0c28d1ee55a65cdcb409ce9fa31cc6bc Mon Sep 17 00:00:00 2001 From: smail Date: Fri, 11 Sep 2026 18:44:37 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=A4=87=E4=BB=BD=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=B8=8E=E8=BF=90=E7=BB=B4=E5=B7=A5=E5=85=B7=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/tools/add_dns.py | 75 +++++++++++++++++++++++ test/tools/add_missing_dns.py | 62 +++++++++++++++++++ test/tools/add_missing_dns_v2.py | 62 +++++++++++++++++++ test/tools/check_alidns.py | 35 +++++++++++ test/tools/debug_login.py | 43 +++++++++++++ test/tools/get_dkim.py | 9 +++ test/tools/test_login_dashboard.py | 38 ++++++++++++ test/tools/test_login_dashboard_backup.py | 38 ++++++++++++ test/tools/test_question.py | 51 +++++++++++++++ test/tools/test_visual_domain_login.py | 38 ++++++++++++ 10 files changed, 451 insertions(+) create mode 100644 test/tools/add_dns.py create mode 100644 test/tools/add_missing_dns.py create mode 100644 test/tools/add_missing_dns_v2.py create mode 100644 test/tools/check_alidns.py create mode 100644 test/tools/debug_login.py create mode 100644 test/tools/get_dkim.py create mode 100644 test/tools/test_login_dashboard.py create mode 100644 test/tools/test_login_dashboard_backup.py create mode 100644 test/tools/test_question.py create mode 100644 test/tools/test_visual_domain_login.py diff --git a/test/tools/add_dns.py b/test/tools/add_dns.py new file mode 100644 index 00000000..b559d14f --- /dev/null +++ b/test/tools/add_dns.py @@ -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') diff --git a/test/tools/add_missing_dns.py b/test/tools/add_missing_dns.py new file mode 100644 index 00000000..2645bbec --- /dev/null +++ b/test/tools/add_missing_dns.py @@ -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) diff --git a/test/tools/add_missing_dns_v2.py b/test/tools/add_missing_dns_v2.py new file mode 100644 index 00000000..723eee90 --- /dev/null +++ b/test/tools/add_missing_dns_v2.py @@ -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) diff --git a/test/tools/check_alidns.py b/test/tools/check_alidns.py new file mode 100644 index 00000000..4cd38bdd --- /dev/null +++ b/test/tools/check_alidns.py @@ -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}') diff --git a/test/tools/debug_login.py b/test/tools/debug_login.py new file mode 100644 index 00000000..da6f1efd --- /dev/null +++ b/test/tools/debug_login.py @@ -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() diff --git a/test/tools/get_dkim.py b/test/tools/get_dkim.py new file mode 100644 index 00000000..8f74e1c8 --- /dev/null +++ b/test/tools/get_dkim.py @@ -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() diff --git a/test/tools/test_login_dashboard.py b/test/tools/test_login_dashboard.py new file mode 100644 index 00000000..86553cf8 --- /dev/null +++ b/test/tools/test_login_dashboard.py @@ -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() diff --git a/test/tools/test_login_dashboard_backup.py b/test/tools/test_login_dashboard_backup.py new file mode 100644 index 00000000..909aa948 --- /dev/null +++ b/test/tools/test_login_dashboard_backup.py @@ -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("button").filter(has_text="登录").first.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() diff --git a/test/tools/test_question.py b/test/tools/test_question.py new file mode 100644 index 00000000..54a03db7 --- /dev/null +++ b/test/tools/test_question.py @@ -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() diff --git a/test/tools/test_visual_domain_login.py b/test/tools/test_visual_domain_login.py new file mode 100644 index 00000000..69319cd9 --- /dev/null +++ b/test/tools/test_visual_domain_login.py @@ -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()