Skip to content

Commit 537c7a3

Browse files
skullcmdclaude
andcommitted
feat(public): unified disclosure page with sub-tabs (disclosure + claim + opt-out + abuse)
The /disclosure (new), /claim, /opt-out, /abuse routes now serve a single focused HTML extracted from public-site.html. Path-driven sub-tab JS reveals the matching pane on load and intercepts tab clicks with history.pushState (graceful fallback if JS disabled — each path serves the same HTML directly). Existing form-POST endpoints under /api/public/* are unchanged. Test: public_disclosure_routes_serve_form_content (in-process, GETs all 4 paths and asserts each carries #responsible-disclosure + #claim + #opt-out + #abuse + the sub-tab strip). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ef60f6f commit 537c7a3

2 files changed

Lines changed: 2445 additions & 3 deletions

File tree

src/bin/anyscan-api.rs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -419,9 +419,10 @@ async fn main() -> Result<()> {
419419
.route("/app/overview", get(operator_pages::operator_overview))
420420
.route("/scanning-policy", get(public_page))
421421
.route("/scanner-identity", get(public_page))
422-
.route("/opt-out", get(public_page))
423-
.route("/claim", get(public_page))
424-
.route("/abuse", get(public_page))
422+
.route("/opt-out", get(public_disclosure))
423+
.route("/claim", get(public_disclosure))
424+
.route("/abuse", get(public_disclosure))
425+
.route("/disclosure", get(public_disclosure))
425426
.route("/data-policy", get(public_page))
426427
.route("/.well-known/security.txt", get(security_txt))
427428
.route("/api/public/profile", get(public_profile))
@@ -604,6 +605,10 @@ async fn public_page() -> Html<&'static str> {
604605
Html(include_str!("../../public-site.html"))
605606
}
606607

608+
async fn public_disclosure() -> Html<&'static str> {
609+
Html(include_str!("../../templates/public/disclosure.html"))
610+
}
611+
607612
async fn enforce_worker_only_host_routes(
608613
State(state): State<Arc<AppState>>,
609614
request: Request,
@@ -4483,4 +4488,43 @@ mod tests {
44834488
assert!(resolved.request_opt_in_enabled);
44844489
assert!(resolved.is_enabled());
44854490
}
4491+
4492+
#[tokio::test]
4493+
async fn public_disclosure_routes_serve_form_content() {
4494+
use axum::{Router, routing::get};
4495+
use tokio::net::TcpListener;
4496+
4497+
let app = Router::new()
4498+
.route("/disclosure", get(public_disclosure))
4499+
.route("/claim", get(public_disclosure))
4500+
.route("/opt-out", get(public_disclosure))
4501+
.route("/abuse", get(public_disclosure));
4502+
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
4503+
let addr = listener.local_addr().unwrap();
4504+
let server = tokio::spawn(async move {
4505+
axum::serve(listener, app).await.unwrap();
4506+
});
4507+
4508+
for path in ["/disclosure", "/claim", "/opt-out", "/abuse"] {
4509+
let body = reqwest::get(format!("http://{addr}{path}"))
4510+
.await
4511+
.unwrap()
4512+
.text()
4513+
.await
4514+
.unwrap();
4515+
assert!(
4516+
body.contains(r#"id="responsible-disclosure""#),
4517+
"missing responsible-disclosure on {path}"
4518+
);
4519+
assert!(body.contains(r#"id="claim""#), "missing #claim on {path}");
4520+
assert!(body.contains(r#"id="opt-out""#), "missing #opt-out on {path}");
4521+
assert!(body.contains(r#"id="abuse""#), "missing #abuse on {path}");
4522+
assert!(
4523+
body.contains("data-disclosure-tabs"),
4524+
"sub-tab strip missing on {path}"
4525+
);
4526+
}
4527+
4528+
server.abort();
4529+
}
44864530
}

0 commit comments

Comments
 (0)