max / makenotwork
16 files changed,
+140 insertions,
-135 deletions
| @@ -1214,15 +1214,16 @@ dependencies = [ | |||
| 1214 | 1214 | ||
| 1215 | 1215 | [[package]] | |
| 1216 | 1216 | name = "axum-extra" | |
| 1217 | - | version = "0.10.3" | |
| 1217 | + | version = "0.12.6" | |
| 1218 | 1218 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1219 | - | checksum = "9963ff19f40c6102c76756ef0a46004c0d58957d87259fc9208ff8441c12ab96" | |
| 1219 | + | checksum = "be44683b41ccb9ab2d23a5230015c9c3c55be97a25e4428366de8873103f7970" | |
| 1220 | 1220 | dependencies = [ | |
| 1221 | 1221 | "axum", | |
| 1222 | 1222 | "axum-core", | |
| 1223 | 1223 | "bytes", | |
| 1224 | 1224 | "cookie", | |
| 1225 | 1225 | "form_urlencoded", | |
| 1226 | + | "futures-core", | |
| 1226 | 1227 | "futures-util", | |
| 1227 | 1228 | "headers", | |
| 1228 | 1229 | "http 1.4.2", | |
| @@ -1230,7 +1231,6 @@ dependencies = [ | |||
| 1230 | 1231 | "http-body-util", | |
| 1231 | 1232 | "mime", | |
| 1232 | 1233 | "pin-project-lite", | |
| 1233 | - | "rustversion", | |
| 1234 | 1234 | "serde_core", | |
| 1235 | 1235 | "serde_html_form", | |
| 1236 | 1236 | "serde_path_to_error", | |
| @@ -3233,9 +3233,9 @@ dependencies = [ | |||
| 3233 | 3233 | ||
| 3234 | 3234 | [[package]] | |
| 3235 | 3235 | name = "governor" | |
| 3236 | - | version = "0.8.1" | |
| 3236 | + | version = "0.10.4" | |
| 3237 | 3237 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3238 | - | checksum = "be93b4ec2e4710b04d9264c0c7350cdd62a8c20e5e4ac732552ebb8f0debe8eb" | |
| 3238 | + | checksum = "9efcab3c1958580ff1f25a2a41be1668f7603d849bb63af523b208a3cc1223b8" | |
| 3239 | 3239 | dependencies = [ | |
| 3240 | 3240 | "cfg-if", | |
| 3241 | 3241 | "dashmap", | |
| @@ -3243,7 +3243,7 @@ dependencies = [ | |||
| 3243 | 3243 | "futures-timer", | |
| 3244 | 3244 | "futures-util", | |
| 3245 | 3245 | "getrandom 0.3.4", | |
| 3246 | - | "no-std-compat", | |
| 3246 | + | "hashbrown 0.16.1", | |
| 3247 | 3247 | "nonzero_ext", | |
| 3248 | 3248 | "parking_lot", | |
| 3249 | 3249 | "portable-atomic", | |
| @@ -3559,6 +3559,19 @@ dependencies = [ | |||
| 3559 | 3559 | ] | |
| 3560 | 3560 | ||
| 3561 | 3561 | [[package]] | |
| 3562 | + | name = "hyper-timeout" | |
| 3563 | + | version = "0.5.2" | |
| 3564 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3565 | + | checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" | |
| 3566 | + | dependencies = [ | |
| 3567 | + | "hyper", | |
| 3568 | + | "hyper-util", | |
| 3569 | + | "pin-project-lite", | |
| 3570 | + | "tokio", | |
| 3571 | + | "tower-service", | |
| 3572 | + | ] | |
| 3573 | + | ||
| 3574 | + | [[package]] | |
| 3562 | 3575 | name = "hyper-tls" | |
| 3563 | 3576 | version = "0.6.0" | |
| 3564 | 3577 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -4368,7 +4381,7 @@ dependencies = [ | |||
| 4368 | 4381 | "toml 1.1.3+spec-1.1.0", | |
| 4369 | 4382 | "totp-rs", | |
| 4370 | 4383 | "tower", | |
| 4371 | - | "tower-http", | |
| 4384 | + | "tower-http 0.7.0", | |
| 4372 | 4385 | "tower-sessions", | |
| 4373 | 4386 | "tower-sessions-sqlx-store", | |
| 4374 | 4387 | "tower_governor", | |
| @@ -4648,12 +4661,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 4648 | 4661 | checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" | |
| 4649 | 4662 | ||
| 4650 | 4663 | [[package]] | |
| 4651 | - | name = "no-std-compat" | |
| 4652 | - | version = "0.4.1" | |
| 4653 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 4654 | - | checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" | |
| 4655 | - | ||
| 4656 | - | [[package]] | |
| 4657 | 4664 | name = "nom" | |
| 4658 | 4665 | version = "7.1.3" | |
| 4659 | 4666 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -5967,7 +5974,7 @@ dependencies = [ | |||
| 5967 | 5974 | "tokio", | |
| 5968 | 5975 | "tokio-rustls", | |
| 5969 | 5976 | "tower", | |
| 5970 | - | "tower-http", | |
| 5977 | + | "tower-http 0.6.11", | |
| 5971 | 5978 | "tower-service", | |
| 5972 | 5979 | "url", | |
| 5973 | 5980 | "wasm-bindgen", | |
| @@ -6011,7 +6018,7 @@ dependencies = [ | |||
| 6011 | 6018 | "tokio", | |
| 6012 | 6019 | "tokio-rustls", | |
| 6013 | 6020 | "tower", | |
| 6014 | - | "tower-http", | |
| 6021 | + | "tower-http 0.6.11", | |
| 6015 | 6022 | "tower-service", | |
| 6016 | 6023 | "url", | |
| 6017 | 6024 | "wasm-bindgen", | |
| @@ -7556,6 +7563,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 7556 | 7563 | checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" | |
| 7557 | 7564 | ||
| 7558 | 7565 | [[package]] | |
| 7566 | + | name = "tonic" | |
| 7567 | + | version = "0.14.6" | |
| 7568 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 7569 | + | checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" | |
| 7570 | + | dependencies = [ | |
| 7571 | + | "async-trait", | |
| 7572 | + | "axum", | |
| 7573 | + | "base64 0.22.1", | |
| 7574 | + | "bytes", | |
| 7575 | + | "h2", | |
| 7576 | + | "http 1.4.2", | |
| 7577 | + | "http-body 1.0.1", | |
| 7578 | + | "http-body-util", | |
| 7579 | + | "hyper", | |
| 7580 | + | "hyper-timeout", | |
| 7581 | + | "hyper-util", | |
| 7582 | + | "percent-encoding", | |
| 7583 | + | "pin-project", | |
| 7584 | + | "socket2", | |
| 7585 | + | "sync_wrapper", | |
| 7586 | + | "tokio", | |
| 7587 | + | "tokio-stream", | |
| 7588 | + | "tower", | |
| 7589 | + | "tower-layer", | |
| 7590 | + | "tower-service", | |
| 7591 | + | "tracing", | |
| 7592 | + | ] | |
| 7593 | + | ||
| 7594 | + | [[package]] | |
| 7559 | 7595 | name = "totp-rs" | |
| 7560 | 7596 | version = "5.7.2" | |
| 7561 | 7597 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -7579,9 +7615,12 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" | |||
| 7579 | 7615 | dependencies = [ | |
| 7580 | 7616 | "futures-core", | |
| 7581 | 7617 | "futures-util", | |
| 7618 | + | "indexmap", | |
| 7582 | 7619 | "pin-project-lite", | |
| 7620 | + | "slab", | |
| 7583 | 7621 | "sync_wrapper", | |
| 7584 | 7622 | "tokio", | |
| 7623 | + | "tokio-util", | |
| 7585 | 7624 | "tower-layer", | |
| 7586 | 7625 | "tower-service", | |
| 7587 | 7626 | "tracing", | |
| @@ -7611,6 +7650,24 @@ checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" | |||
| 7611 | 7650 | dependencies = [ | |
| 7612 | 7651 | "bitflags 2.13.0", | |
| 7613 | 7652 | "bytes", | |
| 7653 | + | "futures-util", | |
| 7654 | + | "http 1.4.2", | |
| 7655 | + | "http-body 1.0.1", | |
| 7656 | + | "pin-project-lite", | |
| 7657 | + | "tower", | |
| 7658 | + | "tower-layer", | |
| 7659 | + | "tower-service", | |
| 7660 | + | "url", | |
| 7661 | + | ] | |
| 7662 | + | ||
| 7663 | + | [[package]] | |
| 7664 | + | name = "tower-http" | |
| 7665 | + | version = "0.7.0" | |
| 7666 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 7667 | + | checksum = "b11f75e912b0c2be01b63d8cf8057b8c3f97cf34abb3d431a3a4c8675498e233" | |
| 7668 | + | dependencies = [ | |
| 7669 | + | "bitflags 2.13.0", | |
| 7670 | + | "bytes", | |
| 7614 | 7671 | "futures-core", | |
| 7615 | 7672 | "futures-util", | |
| 7616 | 7673 | "http 1.4.2", | |
| @@ -7624,11 +7681,9 @@ dependencies = [ | |||
| 7624 | 7681 | "pin-project-lite", | |
| 7625 | 7682 | "tokio", | |
| 7626 | 7683 | "tokio-util", | |
| 7627 | - | "tower", | |
| 7628 | 7684 | "tower-layer", | |
| 7629 | 7685 | "tower-service", | |
| 7630 | 7686 | "tracing", | |
| 7631 | - | "url", | |
| 7632 | 7687 | "uuid", | |
| 7633 | 7688 | ] | |
| 7634 | 7689 | ||
| @@ -7711,9 +7766,9 @@ dependencies = [ | |||
| 7711 | 7766 | ||
| 7712 | 7767 | [[package]] | |
| 7713 | 7768 | name = "tower_governor" | |
| 7714 | - | version = "0.6.0" | |
| 7769 | + | version = "0.8.0" | |
| 7715 | 7770 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 7716 | - | checksum = "57a2ccff6830fa835371af7541e561a90e4c07b84f72991ebac4b3cb6790dc0d" | |
| 7771 | + | checksum = "44de9b94d849d3c46e06a883d72d408c2de6403367b39df2b1c9d9e7b6736fe6" | |
| 7717 | 7772 | dependencies = [ | |
| 7718 | 7773 | "axum", | |
| 7719 | 7774 | "forwarded-header-value", | |
| @@ -7721,6 +7776,7 @@ dependencies = [ | |||
| 7721 | 7776 | "http 1.4.2", | |
| 7722 | 7777 | "pin-project", | |
| 7723 | 7778 | "thiserror 2.0.18", | |
| 7779 | + | "tonic", | |
| 7724 | 7780 | "tower", | |
| 7725 | 7781 | "tracing", | |
| 7726 | 7782 | ] |
| @@ -16,7 +16,7 @@ async-trait = "0.1" | |||
| 16 | 16 | ||
| 17 | 17 | # Web framework | |
| 18 | 18 | axum = { version = "0.8.8", features = ["macros"] } | |
| 19 | - | axum-extra = { version = "0.10.3", features = ["cookie", "form", "query", "typed-header"] } | |
| 19 | + | axum-extra = { version = "0.12.6", features = ["cookie", "form", "query", "typed-header"] } | |
| 20 | 20 | ||
| 21 | 21 | # OpenAPI spec generation | |
| 22 | 22 | utoipa = { version = "5", features = ["axum_extras", "chrono", "uuid"] } | |
| @@ -29,7 +29,7 @@ tokio = { version = "1.50.0", features = ["macros", "rt-multi-thread", "net", "s | |||
| 29 | 29 | tokio-stream = { version = "0.1", features = ["sync"] } | |
| 30 | 30 | tokio-util = { version = "0.7", features = ["io"] } | |
| 31 | 31 | tower = "0.5.3" | |
| 32 | - | tower-http = { version = "0.6.8", features = ["trace", "fs", "limit", "request-id", "propagate-header", "set-header"] } | |
| 32 | + | tower-http = { version = "0.7.0", features = ["trace", "fs", "limit", "request-id", "propagate-header", "set-header"] } | |
| 33 | 33 | tracing = "0.1.44" | |
| 34 | 34 | tracing-subscriber = { version = "0.3.22", features = ["env-filter", "json"] } | |
| 35 | 35 | ||
| @@ -53,8 +53,8 @@ tower-sessions-sqlx-store = { version = "0.15.0", features = ["postgres"] } | |||
| 53 | 53 | dashmap = "6" | |
| 54 | 54 | ||
| 55 | 55 | # Rate Limiting | |
| 56 | - | tower_governor = "0.6.0" | |
| 57 | - | governor = "0.8.1" | |
| 56 | + | tower_governor = "0.8.0" | |
| 57 | + | governor = "0.10.4" | |
| 58 | 58 | ||
| 59 | 59 | # JWT (SyncKit) | |
| 60 | 60 | jsonwebtoken = "9.3.1" |
| @@ -548,9 +548,7 @@ pub fn api_routes() -> CsrfRouter<AppState> { | |||
| 548 | 548 | "/api/email-signup", | |
| 549 | 549 | post_csrf_skip("pre-auth landing signup, no session", email_signup), | |
| 550 | 550 | ) | |
| 551 | - | .route_layer(GovernorLayer { | |
| 552 | - | config: write_rate_limit, | |
| 553 | - | }); | |
| 551 | + | .route_layer(GovernorLayer::new(write_rate_limit)); | |
| 554 | 552 | ||
| 555 | 553 | // Password/code-verifying TOTP mutations — strict auth-strength rate limit | |
| 556 | 554 | // (matching login), not the looser API-write limit, so confirm-code and | |
| @@ -566,9 +564,7 @@ pub fn api_routes() -> CsrfRouter<AppState> { | |||
| 566 | 564 | "/api/users/me/totp/backup-codes", | |
| 567 | 565 | post_csrf(totp::regenerate_backup_codes), | |
| 568 | 566 | ) | |
| 569 | - | .route_layer(GovernorLayer { | |
| 570 | - | config: totp_sensitive_rate_limit, | |
| 571 | - | }); | |
| 567 | + | .route_layer(GovernorLayer::new(totp_sensitive_rate_limit)); | |
| 572 | 568 | ||
| 573 | 569 | // Export routes — stricter rate limit | |
| 574 | 570 | let export_routes = CsrfRouter::new() | |
| @@ -589,9 +585,7 @@ pub fn api_routes() -> CsrfRouter<AppState> { | |||
| 589 | 585 | ) | |
| 590 | 586 | .route("/api/export/content", post_csrf(exports::export_content)) | |
| 591 | 587 | .route("/api/export/contacts", post_csrf(exports::export_contacts)) | |
| 592 | - | .route_layer(GovernorLayer { | |
| 593 | - | config: export_rate_limit, | |
| 594 | - | }); | |
| 588 | + | .route_layer(GovernorLayer::new(export_rate_limit)); | |
| 595 | 589 | ||
| 596 | 590 | let key_rate_limit = crate::helpers::rate_limiter_ms( | |
| 597 | 591 | constants::LICENSE_KEY_RATE_LIMIT_MS, | |
| @@ -639,9 +633,7 @@ pub fn api_routes() -> CsrfRouter<AppState> { | |||
| 639 | 633 | "/api/v1/license/deactivate", | |
| 640 | 634 | post_csrf_skip(LICENSE_BEARER_SKIP, license_keys::license_deactivate), | |
| 641 | 635 | ) | |
| 642 | - | .route_layer(GovernorLayer { | |
| 643 | - | config: key_rate_limit, | |
| 644 | - | }); | |
| 636 | + | .route_layer(GovernorLayer::new(key_rate_limit)); | |
| 645 | 637 | ||
| 646 | 638 | let read_rate_limit = crate::helpers::rate_limiter_ms( | |
| 647 | 639 | constants::API_READ_RATE_LIMIT_MS, | |
| @@ -706,9 +698,7 @@ pub fn api_routes() -> CsrfRouter<AppState> { | |||
| 706 | 698 | "/api/v1/items/{id}/license.txt", | |
| 707 | 699 | get(license_keys::license_text), | |
| 708 | 700 | ) | |
| 709 | - | .route_layer(GovernorLayer { | |
| 710 | - | config: read_rate_limit, | |
| 711 | - | }); | |
| 701 | + | .route_layer(GovernorLayer::new(read_rate_limit)); | |
| 712 | 702 | ||
| 713 | 703 | let validate_rate_limit = crate::helpers::rate_limiter_per_sec( | |
| 714 | 704 | constants::VALIDATE_RATE_LIMIT_PER_SEC, | |
| @@ -728,9 +718,7 @@ pub fn api_routes() -> CsrfRouter<AppState> { | |||
| 728 | 718 | "/api/validate/blog-slug", | |
| 729 | 719 | post_csrf(validate::validate_blog_slug), | |
| 730 | 720 | ) | |
| 731 | - | .route_layer(GovernorLayer { | |
| 732 | - | config: validate_rate_limit, | |
| 733 | - | }); | |
| 721 | + | .route_layer(GovernorLayer::new(validate_rate_limit)); | |
| 734 | 722 | ||
| 735 | 723 | // Import route needs a higher body limit (base64-encoded CSV up to 10 MB | |
| 736 | 724 | // ≈ 14 MB encoded). The global 1 MB RequestBodyLimitLayer would reject it, | |
| @@ -738,12 +726,10 @@ pub fn api_routes() -> CsrfRouter<AppState> { | |||
| 738 | 726 | let import_routes = CsrfRouter::new() | |
| 739 | 727 | .route("/api/users/me/import", post_csrf(imports::start_import)) | |
| 740 | 728 | .layer(axum::extract::DefaultBodyLimit::max(15 * 1024 * 1024)) | |
| 741 | - | .route_layer(GovernorLayer { | |
| 742 | - | config: crate::helpers::rate_limiter_ms( | |
| 743 | - | constants::API_WRITE_RATE_LIMIT_MS, | |
| 744 | - | constants::API_WRITE_RATE_LIMIT_BURST, | |
| 745 | - | ), | |
| 746 | - | }); | |
| 729 | + | .route_layer(GovernorLayer::new(crate::helpers::rate_limiter_ms( | |
| 730 | + | constants::API_WRITE_RATE_LIMIT_MS, | |
| 731 | + | constants::API_WRITE_RATE_LIMIT_BURST, | |
| 732 | + | ))); | |
| 747 | 733 | ||
| 748 | 734 | // Guest checkout routes — public, no auth, CORS-enabled, stricter rate limit | |
| 749 | 735 | let guest_checkout_rate_limit = crate::helpers::rate_limiter_per_sec( | |
| @@ -767,9 +753,7 @@ pub fn api_routes() -> CsrfRouter<AppState> { | |||
| 767 | 753 | "/api/purchases/claim", | |
| 768 | 754 | post_csrf(guest_checkout::claim_purchase), | |
| 769 | 755 | ) | |
| 770 | - | .route_layer(GovernorLayer { | |
| 771 | - | config: guest_checkout_rate_limit, | |
| 772 | - | }); | |
| 756 | + | .route_layer(GovernorLayer::new(guest_checkout_rate_limit)); | |
| 773 | 757 | ||
| 774 | 758 | // Guest download route — separate, more lenient per-IP rate limit. Unauth'd | |
| 775 | 759 | // and token-gated; the throttle is defense-in-depth against anonymous flooding | |
| @@ -780,9 +764,7 @@ pub fn api_routes() -> CsrfRouter<AppState> { | |||
| 780 | 764 | ); | |
| 781 | 765 | let download_routes = CsrfRouter::new() | |
| 782 | 766 | .route_get("/download/{token}", get(guest_checkout::guest_download)) | |
| 783 | - | .route_layer(GovernorLayer { | |
| 784 | - | config: download_rate_limit, | |
| 785 | - | }); | |
| 767 | + | .route_layer(GovernorLayer::new(download_rate_limit)); | |
| 786 | 768 | ||
| 787 | 769 | write_routes | |
| 788 | 770 | .merge(totp_sensitive_routes) |
| @@ -53,17 +53,17 @@ pub fn auth_routes() -> CsrfRouter<AppState> { | |||
| 53 | 53 | .route("/login", with_csrf_manual( | |
| 54 | 54 | "POST validates via validate_token_consuming (defense-in-depth on top of SameSite=Lax)", | |
| 55 | 55 | get(crate::routes::pages::public::landing::login_page) | |
| 56 | - | .post(login_handler.layer(GovernorLayer { config: auth_rate_limit.clone() })), | |
| 56 | + | .post(login_handler.layer(GovernorLayer::new(auth_rate_limit.clone()))), | |
| 57 | 57 | )) | |
| 58 | 58 | .route("/auth/passkey/start", with_csrf_skip( | |
| 59 | 59 | "pre-auth WebAuthn challenge", | |
| 60 | 60 | post(passkey_auth_start) | |
| 61 | - | .layer(GovernorLayer { config: auth_rate_limit.clone() }), | |
| 61 | + | .layer(GovernorLayer::new(auth_rate_limit.clone())), | |
| 62 | 62 | )) | |
| 63 | 63 | .route("/auth/passkey/finish", with_csrf_skip( | |
| 64 | 64 | "pre-auth WebAuthn assertion", | |
| 65 | 65 | post(passkey_auth_finish) | |
| 66 | - | .layer(GovernorLayer { config: auth_rate_limit }), | |
| 66 | + | .layer(GovernorLayer::new(auth_rate_limit)), | |
| 67 | 67 | )) | |
| 68 | 68 | // Routes without auth rate limiting | |
| 69 | 69 | .route("/logout", post_csrf(logout_handler)) | |
| @@ -71,9 +71,7 @@ pub fn auth_routes() -> CsrfRouter<AppState> { | |||
| 71 | 71 | // Username validation with its own rate limit | |
| 72 | 72 | .route( | |
| 73 | 73 | "/api/validate/username", | |
| 74 | - | with_csrf(post(validate_username).layer(GovernorLayer { | |
| 75 | - | config: validate_rate_limit, | |
| 76 | - | })), | |
| 74 | + | with_csrf(post(validate_username).layer(GovernorLayer::new(validate_rate_limit))), | |
| 77 | 75 | ) | |
| 78 | 76 | } | |
| 79 | 77 |
| @@ -546,9 +546,7 @@ pub fn build_routes() -> CsrfRouter<AppState> { | |||
| 546 | 546 | "/api/sync/builds/apps/{app_id}/builds/{build_id}/cancel", | |
| 547 | 547 | post_csrf_skip(SYNC_SKIP, cancel_build), | |
| 548 | 548 | ) | |
| 549 | - | .route_layer(GovernorLayer { | |
| 550 | - | config: write_rate_limit, | |
| 551 | - | }); | |
| 549 | + | .route_layer(GovernorLayer::new(write_rate_limit)); | |
| 552 | 550 | ||
| 553 | 551 | let trigger_rate_limit = crate::helpers::rate_limiter_per_sec( | |
| 554 | 552 | constants::BUILD_TRIGGER_RATE_LIMIT_PER_SEC, | |
| @@ -560,9 +558,7 @@ pub fn build_routes() -> CsrfRouter<AppState> { | |||
| 560 | 558 | "/api/internal/builds/trigger", | |
| 561 | 559 | post_csrf_skip("internal CI hook: HMAC bearer auth", hook_trigger), | |
| 562 | 560 | ) | |
| 563 | - | .route_layer(GovernorLayer { | |
| 564 | - | config: trigger_rate_limit, | |
| 565 | - | }); | |
| 561 | + | .route_layer(GovernorLayer::new(trigger_rate_limit)); | |
| 566 | 562 | ||
| 567 | 563 | mgmt_routes.merge(internal_routes) | |
| 568 | 564 | } |
| @@ -74,9 +74,7 @@ pub fn git_routes() -> Router<AppState> { | |||
| 74 | 74 | post(raw::smart_http_receive_pack) | |
| 75 | 75 | .layer(DefaultBodyLimit::max(constants::GIT_RECEIVE_PACK_MAX_BYTES)), | |
| 76 | 76 | ) | |
| 77 | - | .route_layer(GovernorLayer { | |
| 78 | - | config: browse_rate_limit, | |
| 79 | - | }) | |
| 77 | + | .route_layer(GovernorLayer::new(browse_rate_limit)) | |
| 80 | 78 | } | |
| 81 | 79 | ||
| 82 | 80 | // ============================================================================ |
| @@ -1078,18 +1078,14 @@ pub fn oauth_routes() -> CsrfRouter<AppState> { | |||
| 1078 | 1078 | let authorize_routes = CsrfRouter::new() | |
| 1079 | 1079 | .route_get("/oauth/authorize", get(authorize_get)) | |
| 1080 | 1080 | .route("/oauth/authorize", post_csrf_manual("OAuth authorize validates the consent form _csrf in-handler via validate_token_consuming", authorize_post)) | |
| 1081 | - | .route_layer(GovernorLayer { | |
| 1082 | - | config: authorize_rate_limit, | |
| 1083 | - | }); | |
| 1081 | + | .route_layer(GovernorLayer::new(authorize_rate_limit)); | |
| 1084 | 1082 | ||
| 1085 | 1083 | let token_routes = CsrfRouter::new() | |
| 1086 | 1084 | .route( | |
| 1087 | 1085 | "/oauth/token", | |
| 1088 | 1086 | post_csrf_skip("pre-auth OAuth token exchange", token_exchange), | |
| 1089 | 1087 | ) | |
| 1090 | - | .route_layer(GovernorLayer { | |
| 1091 | - | config: token_rate_limit, | |
| 1092 | - | }); | |
| 1088 | + | .route_layer(GovernorLayer::new(token_rate_limit)); | |
| 1093 | 1089 | ||
| 1094 | 1090 | // userinfo is DB-amplifying (user + creator-tier lookup) and discovery is a | |
| 1095 | 1091 | // public read; govern both so every public OAuth route carries a rate limit | |
| @@ -1104,9 +1100,7 @@ pub fn oauth_routes() -> CsrfRouter<AppState> { | |||
| 1104 | 1100 | "/.well-known/oauth-authorization-server", | |
| 1105 | 1101 | get(discovery_metadata), | |
| 1106 | 1102 | ) | |
| 1107 | - | .route_layer(GovernorLayer { | |
| 1108 | - | config: read_rate_limit, | |
| 1109 | - | }); | |
| 1103 | + | .route_layer(GovernorLayer::new(read_rate_limit)); | |
| 1110 | 1104 | ||
| 1111 | 1105 | authorize_routes.merge(token_routes).merge(read_routes) | |
| 1112 | 1106 | } |
| @@ -611,9 +611,7 @@ pub fn ota_routes() -> CsrfRouter<AppState> { | |||
| 611 | 611 | "/api/v1/sync/ota/apps/{app_id}/releases/{release_id}/artifacts/confirm", | |
| 612 | 612 | post_csrf_skip(OTA_SKIP, confirm_artifact), | |
| 613 | 613 | ) | |
| 614 | - | .route_layer(GovernorLayer { | |
| 615 | - | config: write_rate_limit, | |
| 616 | - | }); | |
| 614 | + | .route_layer(GovernorLayer::new(write_rate_limit)); | |
| 617 | 615 | ||
| 618 | 616 | let read_rate_limit = crate::helpers::rate_limiter_ms( | |
| 619 | 617 | constants::OTA_READ_RATE_LIMIT_MS, | |
| @@ -637,9 +635,7 @@ pub fn ota_routes() -> CsrfRouter<AppState> { | |||
| 637 | 635 | "/api/v1/sync/ota/{slug}/download/{release_id}/{target}/{arch}", | |
| 638 | 636 | get(artifact_download), | |
| 639 | 637 | ) | |
| 640 | - | .route_layer(GovernorLayer { | |
| 641 | - | config: read_rate_limit, | |
| 642 | - | }); | |
| 638 | + | .route_layer(GovernorLayer::new(read_rate_limit)); | |
| 643 | 639 | ||
| 644 | 640 | mgmt_routes.merge(public_routes) | |
| 645 | 641 | } |
| @@ -153,9 +153,7 @@ pub fn dashboard_routes() -> CsrfRouter<AppState> { | |||
| 153 | 153 | "/dashboard/item/{id}/analytics", | |
| 154 | 154 | get(main::dashboard_item_analytics), | |
| 155 | 155 | ) | |
| 156 | - | .route_layer(GovernorLayer { | |
| 157 | - | config: read_rate_limit, | |
| 158 | - | }); | |
| 156 | + | .route_layer(GovernorLayer::new(read_rate_limit)); | |
| 159 | 157 | ||
| 160 | 158 | CsrfRouter::new() | |
| 161 | 159 | .merge(wizards::wizard_routes()) |
| @@ -67,7 +67,5 @@ pub fn email_action_routes() -> CsrfRouter<AppState> { | |||
| 67 | 67 | get(account::unsubscribe_page).post(account::unsubscribe_handler), | |
| 68 | 68 | ), | |
| 69 | 69 | ) | |
| 70 | - | .layer(GovernorLayer { | |
| 71 | - | config: auth_rate_limit, | |
| 72 | - | }) | |
| 70 | + | .layer(GovernorLayer::new(auth_rate_limit)) | |
| 73 | 71 | } |
| @@ -80,15 +80,11 @@ pub fn public_routes() -> CsrfRouter<AppState> { | |||
| 80 | 80 | // any real uptime monitor while capping a flood. | |
| 81 | 81 | .route_get( | |
| 82 | 82 | "/health", | |
| 83 | - | get(health::health).layer(GovernorLayer { | |
| 84 | - | config: search_rate_limit.clone(), | |
| 85 | - | }), | |
| 83 | + | get(health::health).layer(GovernorLayer::new(search_rate_limit.clone())), | |
| 86 | 84 | ) | |
| 87 | 85 | .route_get( | |
| 88 | 86 | "/api/health", | |
| 89 | - | get(health::health_json).layer(GovernorLayer { | |
| 90 | - | config: search_rate_limit.clone(), | |
| 91 | - | }), | |
| 87 | + | get(health::health_json).layer(GovernorLayer::new(search_rate_limit.clone())), | |
| 92 | 88 | ) | |
| 93 | 89 | .route_get("/robots.txt", get(sitemap::robots_txt)) | |
| 94 | 90 | .route_get("/sitemap.xml", get(sitemap::sitemap_xml)) | |
| @@ -102,9 +98,7 @@ pub fn public_routes() -> CsrfRouter<AppState> { | |||
| 102 | 98 | "join-wizard step 1: pre-auth signup", | |
| 103 | 99 | join_wizard::step_account_create, | |
| 104 | 100 | ) | |
| 105 | - | .layer(GovernorLayer { | |
| 106 | - | config: join_rate_limit, | |
| 107 | - | }), | |
| 101 | + | .layer(GovernorLayer::new(join_rate_limit)), | |
| 108 | 102 | ) | |
| 109 | 103 | .route( | |
| 110 | 104 | "/join/step/{step}", | |
| @@ -116,27 +110,20 @@ pub fn public_routes() -> CsrfRouter<AppState> { | |||
| 116 | 110 | .route_get("/discover", get(discover::discover)) | |
| 117 | 111 | .route_get( | |
| 118 | 112 | "/discover/results", | |
| 119 | - | get(discover::discover_results).layer(GovernorLayer { | |
| 120 | - | config: search_rate_limit.clone(), | |
| 121 | - | }), | |
| 113 | + | get(discover::discover_results).layer(GovernorLayer::new(search_rate_limit.clone())), | |
| 122 | 114 | ) | |
| 123 | 115 | .route_get( | |
| 124 | 116 | "/discover/suggestions", | |
| 125 | - | get(discover::search_suggestions_handler).layer(GovernorLayer { | |
| 126 | - | config: search_rate_limit.clone(), | |
| 127 | - | }), | |
| 117 | + | get(discover::search_suggestions_handler) | |
| 118 | + | .layer(GovernorLayer::new(search_rate_limit.clone())), | |
| 128 | 119 | ) | |
| 129 | 120 | .route_get( | |
| 130 | 121 | "/discover/tags", | |
| 131 | - | get(discover::tag_tree).layer(GovernorLayer { | |
| 132 | - | config: search_rate_limit.clone(), | |
| 133 | - | }), | |
| 122 | + | get(discover::tag_tree).layer(GovernorLayer::new(search_rate_limit.clone())), | |
| 134 | 123 | ) | |
| 135 | 124 | .route_get( | |
| 136 | 125 | "/discover/tag-suggest", | |
| 137 | - | get(discover::tag_suggestions_handler).layer(GovernorLayer { | |
| 138 | - | config: search_rate_limit, | |
| 139 | - | }), | |
| 126 | + | get(discover::tag_suggestions_handler).layer(GovernorLayer::new(search_rate_limit)), | |
| 140 | 127 | ) | |
| 141 | 128 | .route_get("/feed", get(feed::feed_page)) | |
| 142 | 129 | .route_get("/u/{username}", get(content::user_page)) | |
| @@ -176,9 +163,7 @@ pub fn public_routes() -> CsrfRouter<AppState> { | |||
| 176 | 163 | "2FA verification: pre-promotion to full auth, no session yet", | |
| 177 | 164 | two_factor::verify_two_factor, | |
| 178 | 165 | ) | |
| 179 | - | .layer(GovernorLayer { | |
| 180 | - | config: twofa_rate_limit, | |
| 181 | - | }), | |
| 166 | + | .layer(GovernorLayer::new(twofa_rate_limit)), | |
| 182 | 167 | ) | |
| 183 | 168 | } | |
| 184 | 169 |
| @@ -39,9 +39,7 @@ pub fn sandbox_routes() -> CsrfRouter<AppState> { | |||
| 39 | 39 | .route_get("/sandbox", get(sandbox_page)) | |
| 40 | 40 | .route( | |
| 41 | 41 | "/sandbox", | |
| 42 | - | post_csrf(create_sandbox).layer(GovernorLayer { | |
| 43 | - | config: sandbox_rate_limit, | |
| 44 | - | }), | |
| 42 | + | post_csrf(create_sandbox).layer(GovernorLayer::new(sandbox_rate_limit)), | |
| 45 | 43 | ) | |
| 46 | 44 | } | |
| 47 | 45 |
| @@ -181,9 +181,7 @@ pub fn storage_routes() -> CsrfRouter<AppState> { | |||
| 181 | 181 | .route_get("/api/media", get(media::media_list)) | |
| 182 | 182 | .route_get("/api/media/folders", get(media::media_folders)) | |
| 183 | 183 | .route("/api/media/{id}", delete_csrf(media::media_delete)) | |
| 184 | - | .route_layer(GovernorLayer { | |
| 185 | - | config: upload_rate_limit, | |
| 186 | - | }); | |
| 184 | + | .route_layer(GovernorLayer::new(upload_rate_limit)); | |
| 187 | 185 | ||
| 188 | 186 | let stream_rate_limit = crate::helpers::rate_limiter_ms( | |
| 189 | 187 | constants::STREAM_RATE_LIMIT_MS, | |
| @@ -196,9 +194,7 @@ pub fn storage_routes() -> CsrfRouter<AppState> { | |||
| 196 | 194 | "/api/versions/{version_id}/download", | |
| 197 | 195 | get(downloads::version_download), | |
| 198 | 196 | ) | |
| 199 | - | .route_layer(GovernorLayer { | |
| 200 | - | config: stream_rate_limit, | |
| 201 | - | }); | |
| 197 | + | .route_layer(GovernorLayer::new(stream_rate_limit)); | |
| 202 | 198 | ||
| 203 | 199 | upload_routes.merge(stream_routes) | |
| 204 | 200 | } |
| @@ -668,9 +668,7 @@ pub fn synckit_routes(synckit_jwt_secret: Option<std::sync::Arc<String>>) -> Csr | |||
| 668 | 668 | "/api/v1/sync/app/pricing", | |
| 669 | 669 | post_csrf_skip(SYNCKIT_API_KEY_SKIP, sync::get_app_pricing), | |
| 670 | 670 | ) | |
| 671 | - | .route_layer(GovernorLayer { | |
| 672 | - | config: auth_rate_limit, | |
| 673 | - | }); | |
| 671 | + | .route_layer(GovernorLayer::new(auth_rate_limit)); | |
| 674 | 672 | ||
| 675 | 673 | let sync_ip_rate_limit = crate::helpers::rate_limiter_ms( | |
| 676 | 674 | constants::SYNCKIT_SYNC_RATE_LIMIT_MS, | |
| @@ -871,14 +869,10 @@ pub fn synckit_routes(synckit_jwt_secret: Option<std::sync::Arc<String>>) -> Csr | |||
| 871 | 869 | ) | |
| 872 | 870 | // Per-app rate limit (inner layer runs first): prevents one developer's | |
| 873 | 871 | // app from starving other apps. Extracts app ID from JWT payload. | |
| 874 | - | .route_layer(GovernorLayer { | |
| 875 | - | config: sync_app_rate_limit, | |
| 876 | - | }) | |
| 872 | + | .route_layer(GovernorLayer::new(sync_app_rate_limit)) | |
| 877 | 873 | // Per-IP rate limit (outer layer): prevents a single client from | |
| 878 | 874 | // overwhelming the endpoint regardless of which app they claim. | |
| 879 | - | .route_layer(GovernorLayer { | |
| 880 | - | config: sync_ip_rate_limit, | |
| 881 | - | }); | |
| 875 | + | .route_layer(GovernorLayer::new(sync_ip_rate_limit)); | |
| 882 | 876 | ||
| 883 | 877 | // App management endpoints use session auth (no extra rate limit beyond global) | |
| 884 | 878 | let app_routes = CsrfRouter::new() |
| @@ -681,7 +681,7 @@ dependencies = [ | |||
| 681 | 681 | "tokio", | |
| 682 | 682 | "tokio-stream", | |
| 683 | 683 | "tower", | |
| 684 | - | "tower-http", | |
| 684 | + | "tower-http 0.7.0", | |
| 685 | 685 | "tracing", | |
| 686 | 686 | "tracing-subscriber", | |
| 687 | 687 | "uuid", | |
| @@ -959,7 +959,7 @@ dependencies = [ | |||
| 959 | 959 | "tokio", | |
| 960 | 960 | "tokio-rustls", | |
| 961 | 961 | "tower", | |
| 962 | - | "tower-http", | |
| 962 | + | "tower-http 0.6.11", | |
| 963 | 963 | "tower-service", | |
| 964 | 964 | "url", | |
| 965 | 965 | "wasm-bindgen", | |
| @@ -1459,11 +1459,27 @@ dependencies = [ | |||
| 1459 | 1459 | "tower", | |
| 1460 | 1460 | "tower-layer", | |
| 1461 | 1461 | "tower-service", | |
| 1462 | - | "tracing", | |
| 1463 | 1462 | "url", | |
| 1464 | 1463 | ] | |
| 1465 | 1464 | ||
| 1466 | 1465 | [[package]] | |
| 1466 | + | name = "tower-http" | |
| 1467 | + | version = "0.7.0" | |
| 1468 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1469 | + | checksum = "b11f75e912b0c2be01b63d8cf8057b8c3f97cf34abb3d431a3a4c8675498e233" | |
| 1470 | + | dependencies = [ | |
| 1471 | + | "bitflags", | |
| 1472 | + | "bytes", | |
| 1473 | + | "http", | |
| 1474 | + | "http-body", | |
| 1475 | + | "percent-encoding", | |
| 1476 | + | "pin-project-lite", | |
| 1477 | + | "tower-layer", | |
| 1478 | + | "tower-service", | |
| 1479 | + | "tracing", | |
| 1480 | + | ] | |
| 1481 | + | ||
| 1482 | + | [[package]] | |
| 1467 | 1483 | name = "tower-layer" | |
| 1468 | 1484 | version = "0.3.3" | |
| 1469 | 1485 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -23,7 +23,7 @@ uuid = { version = "1", features = ["v4", "serde"] } | |||
| 23 | 23 | ||
| 24 | 24 | axum = { version = "0.8", optional = true } | |
| 25 | 25 | tower = { version = "0.5", optional = true } | |
| 26 | - | tower-http = { version = "0.6", optional = true, features = ["trace"] } | |
| 26 | + | tower-http = { version = "0.7", optional = true, features = ["trace"] } | |
| 27 | 27 | ||
| 28 | 28 | reqwest = { version = "0.13", optional = true, features = ["json"] } | |
| 29 | 29 |