feat: add public h5 dashboard and publish traceability

This commit is contained in:
Wyndham ARR
2026-08-03 13:04:41 +08:00
parent 7e7470821b
commit 2107f00e32
29 changed files with 1047 additions and 71 deletions

View File

@@ -370,6 +370,9 @@ class PortalApplicationTests(unittest.TestCase):
self.assertIn(".company-page-title", styles)
self.assertIn(".company-fixed-companies", styles)
self.assertIn(".company-source-action", styles)
self.assertIn(".company-source-current", styles)
self.assertIn(".company-job-source", styles)
self.assertIn(".company-history-source", styles)
self.assertIn(".company-review-file", styles)
self.assertIn("overflow-wrap: anywhere", styles)
self.assertIn(".company-review-row.is-pending", styles)
@@ -392,6 +395,8 @@ class PortalApplicationTests(unittest.TestCase):
self.assertNotIn("window.confirm", script)
self.assertIn("companyReportConfirmIsOpen", script)
self.assertIn("confirmCompanyReportAction", script)
self.assertIn("BOOKING_EXCEL_SOURCE_ALREADY_ACTIVATED", script)
self.assertIn("requestError.code", script)
self.assertIn("state.companyReportConfirmRequest", script)
self.assertNotIn("company-review-guidance", script)
self.assertNotIn("company-review-description", script)
@@ -400,6 +405,17 @@ class PortalApplicationTests(unittest.TestCase):
self.assertNotIn("Excel 已校验", script)
self.assertNotIn('id="company-source-state"', desktop_text)
self.assertNotIn('id="company-source-summary"', desktop_text)
for required in (
'id="company-source-current"',
'id="company-source-current-filename"',
'id="company-source-current-meta"',
'id="company-source-status"',
'id="company-report-job-source"',
'id="company-report-job-source-file"',
'id="company-report-job-source-meta"',
"来源 Excel",
):
self.assertIn(required, desktop_text)
self.assertIn('String(summary.filename || "").trim()', script)
self.assertIn('$("#company-review-filename").textContent = filename || "未记录文件名"', script)
self.assertLess(
@@ -580,6 +596,36 @@ class PortalApplicationTests(unittest.TestCase):
"COMPANY-A",
)
def test_anonymous_h5_exposes_only_sanitized_read_only_dashboard(self) -> None:
mobile = self.app.handle("GET", "/h5", {})
mobile_css = self.app.handle("GET", "/assets/h5.css", {})
mobile_js = self.app.handle("GET", "/assets/h5.js", {})
months = decoded(self.app.handle("GET", "/api/public/h5/months", {}))
analytics_response = self.app.handle(
"GET",
"/api/public/h5/analytics?month=2026-07",
{},
)
analytics = decoded(analytics_response)
protected_generic = self.app.handle(
"GET",
"/api/analytics?month=2026-07",
{},
)
protected_legacy = self.app.handle("GET", "/api/h5/months", {})
self.assertEqual(mobile.status, 200)
self.assertEqual(mobile_css.status, 200)
self.assertEqual(mobile_js.status, 200)
self.assertEqual(months["data"][0]["month_key"], "2026-07")
self.assertEqual(analytics_response.headers["Cache-Control"], "no-store")
self.assertEqual(analytics["data"]["month_key"], "2026-07")
self.assertIn("channels", analytics["data"])
self.assertNotIn("source_monthly_sha256", analytics["data"])
self.assertNotIn("filename", months["data"][0])
self.assertEqual(protected_generic.status, 401)
self.assertEqual(protected_legacy.status, 401)
def test_health_exposes_only_programmatic_processing_readiness(self) -> None:
payload = decoded(
self.app.handle("GET", "/api/health", self.auth_headers)