Update Axum to 0.8.

This commit is contained in:
Sebastian Jeltsch
2025-01-01 23:02:23 +01:00
parent 083dddc021
commit 4ae0966810
17 changed files with 277 additions and 195 deletions

193
Cargo.lock generated
View File

@@ -469,16 +469,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
dependencies = [
"async-trait",
"axum-core",
"axum-core 0.4.5",
"bytes",
"futures-util",
"http 1.2.0",
"http-body",
"http-body-util",
"itoa",
"matchit 0.7.3",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"rustversion",
"serde",
"serde_json",
"serde_path_to_error",
"sync_wrapper",
"tower 0.5.2",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8"
dependencies = [
"axum-core 0.5.0",
"bytes",
"form_urlencoded",
"futures-util",
"http 1.2.0",
"http-body",
"http-body-util",
"hyper",
"hyper-util",
"itoa",
"matchit",
"matchit 0.8.4",
"memchr",
"mime",
"multer",
@@ -499,11 +528,11 @@ dependencies = [
[[package]]
name = "axum-client-ip"
version = "0.6.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9eefda7e2b27e1bda4d6fa8a06b50803b8793769045918bc37ad062d48a6efac"
checksum = "dff8ee1869817523c8f91c20bf17fd932707f66c2e7e0b0f811b29a227289562"
dependencies = [
"axum",
"axum 0.8.1",
"forwarded-header-value",
"serde",
]
@@ -526,17 +555,14 @@ dependencies = [
"sync_wrapper",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-extra"
version = "0.9.6"
name = "axum-core"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c794b30c904f0a1c2fb7740f7df7f7972dfaa14ef6f57cb6178dc63e5dca2f04"
checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733"
dependencies = [
"axum",
"axum-core",
"bytes",
"futures-util",
"http 1.2.0",
@@ -544,7 +570,29 @@ dependencies = [
"http-body-util",
"mime",
"pin-project-lite",
"prost 0.12.6",
"rustversion",
"sync_wrapper",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-extra"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fc6f625a1f7705c6cf62d0d070794e94668988b1c38111baeec177c715f7b"
dependencies = [
"axum 0.8.1",
"axum-core 0.5.0",
"bytes",
"futures-util",
"http 1.2.0",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"prost",
"serde",
"tower 0.5.2",
"tower-layer",
@@ -553,14 +601,14 @@ dependencies = [
[[package]]
name = "axum-test"
version = "16.4.1"
version = "17.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e3a443d2608936a02a222da7b746eb412fede7225b3030b64fe9be99eab8dc"
checksum = "53f1009889890a439cbf67a4071a2593d027c65209da4faeac5582f28ca9e6c3"
dependencies = [
"anyhow",
"assert-json-diff",
"auto-future",
"axum",
"axum 0.8.1",
"bytes",
"bytesize",
"cookie",
@@ -1252,7 +1300,7 @@ dependencies = [
name = "custom-binary"
version = "0.1.0"
dependencies = [
"axum",
"axum 0.8.1",
"env_logger",
"tokio",
"tracing-subscriber",
@@ -2891,15 +2939,6 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.13.0"
@@ -3168,6 +3207,12 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]]
name = "matchit"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
name = "maxminddb"
version = "0.24.0"
@@ -3575,7 +3620,7 @@ dependencies = [
"opentelemetry-http",
"opentelemetry-proto",
"opentelemetry_sdk",
"prost 0.13.4",
"prost",
"serde_json",
"thiserror 1.0.69",
"tokio",
@@ -3592,7 +3637,7 @@ dependencies = [
"hex",
"opentelemetry",
"opentelemetry_sdk",
"prost 0.13.4",
"prost",
"serde",
"tonic",
]
@@ -4019,16 +4064,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "prost"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29"
dependencies = [
"bytes",
"prost-derive 0.12.6",
]
[[package]]
name = "prost"
version = "0.13.4"
@@ -4036,7 +4071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec"
dependencies = [
"bytes",
"prost-derive 0.13.4",
"prost-derive",
]
[[package]]
@@ -4052,26 +4087,13 @@ dependencies = [
"once_cell",
"petgraph",
"prettyplease",
"prost 0.13.4",
"prost-types 0.13.4",
"prost",
"prost-types",
"regex",
"syn 2.0.95",
"tempfile",
]
[[package]]
name = "prost-derive"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
dependencies = [
"anyhow",
"itertools 0.12.1",
"proc-macro2",
"quote",
"syn 2.0.95",
]
[[package]]
name = "prost-derive"
version = "0.13.4"
@@ -4085,29 +4107,17 @@ dependencies = [
"syn 2.0.95",
]
[[package]]
name = "prost-reflect"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f5eec97d5d34bdd17ad2db2219aabf46b054c6c41bd5529767c9ce55be5898f"
dependencies = [
"logos",
"once_cell",
"prost 0.12.6",
"prost-reflect-derive 0.13.0",
"prost-types 0.12.6",
]
[[package]]
name = "prost-reflect"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20ae544fca2892fd4b7e9ff26cba1090cedf1d4d95c2aded1af15d2f93f270b8"
dependencies = [
"logos",
"once_cell",
"prost 0.13.4",
"prost-reflect-derive 0.14.0",
"prost-types 0.13.4",
"prost",
"prost-reflect-derive",
"prost-types",
]
[[package]]
@@ -4117,18 +4127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50e2537231d94dd2778920c2ada37dd9eb1ac0325bb3ee3ee651bd44c1134123"
dependencies = [
"prost-build",
"prost-reflect 0.14.3",
]
[[package]]
name = "prost-reflect-derive"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64c3f519df051f8a700c5aa42b53f9c42d54959506b7ed58ac7a6af7991fdc22"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.95",
"prost-reflect",
]
[[package]]
@@ -4142,22 +4141,13 @@ dependencies = [
"syn 2.0.95",
]
[[package]]
name = "prost-types"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0"
dependencies = [
"prost 0.12.6",
]
[[package]]
name = "prost-types"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc"
dependencies = [
"prost 0.13.4",
"prost",
]
[[package]]
@@ -5951,7 +5941,7 @@ checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
dependencies = [
"async-stream",
"async-trait",
"axum",
"axum 0.7.9",
"base64 0.22.1",
"bytes",
"h2",
@@ -5963,7 +5953,7 @@ dependencies = [
"hyper-util",
"percent-encoding",
"pin-project",
"prost 0.13.4",
"prost",
"socket2",
"tokio",
"tokio-stream",
@@ -6011,12 +6001,11 @@ dependencies = [
[[package]]
name = "tower-cookies"
version = "0.10.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fd0118512cf0b3768f7fcccf0bef1ae41d68f2b45edc1e77432b36c97c56c6d"
checksum = "151b5a3e3c45df17466454bb74e9ecedecc955269bdedbf4d150dfa393b55a36"
dependencies = [
"async-trait",
"axum-core",
"axum-core 0.5.0",
"cookie",
"futures-util",
"http 1.2.0",
@@ -6132,7 +6121,7 @@ dependencies = [
"argon2",
"async-channel 2.3.1",
"async-trait",
"axum",
"axum 0.8.1",
"axum-client-ip",
"axum-extra",
"axum-test",
@@ -6158,9 +6147,9 @@ dependencies = [
"oauth2",
"object_store",
"parking_lot",
"prost 0.12.6",
"prost",
"prost-build",
"prost-reflect 0.13.1",
"prost-reflect",
"prost-reflect-build",
"quoted_printable",
"rand",
@@ -6202,7 +6191,7 @@ dependencies = [
name = "trailbase-cli"
version = "0.2.0"
dependencies = [
"axum",
"axum 0.8.1",
"chrono",
"clap",
"env_logger",
@@ -6524,7 +6513,7 @@ version = "8.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db4b5ac679cc6dfc5ea3f2823b0291c777750ffd5e13b21137e0f7ac0e8f9617"
dependencies = [
"axum",
"axum 0.7.9",
"base64 0.22.1",
"mime_guess",
"regex",

View File

@@ -33,7 +33,7 @@ addRoute(
addRoute(
"GET",
"/test/:table",
"/test/{table}",
stringHandler(async (req: StringRequestType) => {
const table = req.params["table"];
if (table) {

View File

@@ -5,7 +5,7 @@ edition = "2021"
publish = false
[dependencies]
axum = { version = "^0.7.5" }
axum = "^0.8.1"
env_logger = "^0.11.3"
tokio = { version = "^1.38.0", features=["macros", "rt-multi-thread"] }
tracing-subscriber = "0.3.18"

View File

@@ -12,7 +12,7 @@ name = "trail"
openapi = ["dep:utoipa", "dep:utoipa-swagger-ui"]
[dependencies]
axum = { version = "^0.7.5", features=["multipart"] }
axum = { version = "^0.8.1", features=["multipart"] }
chrono = "^0.4.38"
clap = { version = "^4.4.11", features=["derive", "env"] }
env_logger = "^0.11.3"

View File

@@ -27,9 +27,9 @@ arc-swap = "1.7.1"
argon2 = { version = "^0.5.3", default-features = false, features = ["alloc", "password-hash"] }
async-channel = "2.3.1"
async-trait = "0.1.80"
axum = { version = "^0.7.5", features = ["multipart"] }
axum-client-ip = "0.6.0"
axum-extra = { version = "^0.9.3", default-features = false, features = ["protobuf"] }
axum = { version = "^0.8.1", features = ["multipart"] }
axum-client-ip = "0.7.0"
axum-extra = { version = "^0.10.0", default-features = false, features = ["protobuf"] }
base64 = { version = "0.22.1", default-features = false }
bytes = { version = "1.8.0", features = ["serde"] }
chrono = "^0.4.38"
@@ -51,8 +51,8 @@ minijinja = "2.1.2"
oauth2 = { version = "5.0.0-alpha.4", default-features = false, features = ["reqwest", "rustls-tls"] }
object_store = { version = "0.11.0", default-features = false, features = ["aws"] }
parking_lot = "0.12.3"
prost = "^0.12.6"
prost-reflect = { version = "^0.13.0", features = ["derive", "text-format"] }
prost = "^0.13.4"
prost-reflect = { version = "^0.14.3", features = ["derive", "text-format"] }
rand = "0.8.5"
trailbase-refinery-core = { workspace = true }
trailbase-refinery-macros = { workspace = true }
@@ -71,7 +71,7 @@ sqlite3-parser = "0.13.0"
thiserror = "2.0.1"
thread_local = "1.1.8"
tokio = { version = "^1.38.0", features = ["macros", "rt-multi-thread", "fs", "signal", "time"] }
tower-cookies = { version = "0.10.0" }
tower-cookies = "0.11.0"
tower-http = { version = "^0.6.0", features = ["cors", "trace", "fs", "limit"] }
tower-service = "0.3.3"
tracing = "0.1.40"
@@ -91,7 +91,7 @@ prost-reflect-build = "0.14.0"
[dev-dependencies]
anyhow = "^1.0.86"
axum-test = "16.3.0"
axum-test = "17.0.1"
criterion = { version = "0.5", features = ["html_reports", "async_tokio"] }
trailbase-extension = { workspace = true }
quoted_printable = "0.5.1"

View File

@@ -21,19 +21,22 @@ use axum::{
pub fn router() -> Router<AppState> {
Router::new()
// Row actions.
.route("/table/:table_name/rows", get(rows::list_rows_handler))
.route("/table/:table_name/files", get(rows::read_files_handler))
.route("/table/:table_name/rows", delete(rows::delete_rows_handler))
.route("/table/:table_name", patch(rows::update_row_handler))
.route("/table/:table_name", post(rows::insert_row_handler))
.route("/table/:table_name", delete(rows::delete_row_handler))
.route("/table/{table_name}/rows", get(rows::list_rows_handler))
.route("/table/{table_name}/files", get(rows::read_files_handler))
.route(
"/table/{table_name}/rows",
delete(rows::delete_rows_handler),
)
.route("/table/{table_name}", patch(rows::update_row_handler))
.route("/table/{table_name}", post(rows::insert_row_handler))
.route("/table/{table_name}", delete(rows::delete_row_handler))
// Index actions.
.route("/index", post(table::create_index_handler))
.route("/index", patch(table::alter_index_handler))
.route("/index", delete(table::drop_index_handler))
// Table actions.
.route(
"/table/:table_name/schema.json",
"/table/{table_name}/schema.json",
get(table::get_table_schema_handler),
)
.route("/table", post(table::create_table_handler))

View File

@@ -133,6 +133,7 @@ mod tests {
Part::bytes(body_slice.to_vec()).file_name(filename),
);
let content_type = form.content_type();
let body: axum::body::Body = form.into();
http::Request::builder()

View File

@@ -22,6 +22,8 @@ pub use jwt::{JwtHelper, TokenClaims};
pub(crate) use ui::auth_ui_router;
pub use user::User;
use crate::constants::AUTH_API_PATH;
#[derive(OpenApi)]
#[openapi(
paths(
@@ -82,68 +84,101 @@ pub(super) fn router() -> Router<crate::AppState> {
// * vacuum expired pending registrations.
return Router::new()
// Sign-up new users.
.route("/register", post(api::register::register_user_handler))
.route(
&format!("/{AUTH_API_PATH}/register"),
post(api::register::register_user_handler),
)
// E-mail verification and change flows.
.route(
"/verify_email/trigger",
&format!("/{AUTH_API_PATH}/verify_email/trigger"),
get(api::verify_email::request_email_verification_handler),
)
.route(
"/verify_email/confirm/:email_verification_code",
&format!("/{AUTH_API_PATH}/verify_email/confirm/{{email_verification_code}}"),
get(api::verify_email::verify_email_handler),
)
.route(
"/change_email/request",
&format!("/{AUTH_API_PATH}/change_email/request"),
post(api::change_email::change_email_request_handler),
)
.route(
"/change_email/confirm/:email_verification_code",
&format!("/{AUTH_API_PATH}/change_email/confirm/{{email_verification_code}}"),
get(api::change_email::change_email_confirm_handler),
)
// Password-reset flow.
.route(
"/reset_password/request",
&format!("/{AUTH_API_PATH}/reset_password/request"),
post(api::reset_password::reset_password_request_handler),
)
.route(
"/reset_password/update/:password_reset_code",
&format!("/{AUTH_API_PATH}/reset_password/update/{{password_reset_code}}"),
post(api::reset_password::reset_password_update_handler),
)
// Change password flow.
.route(
"/change_password",
&format!("/{AUTH_API_PATH}/change_password"),
post(api::change_password::change_password_handler),
)
// Token refresh flow.
.route("/refresh", post(api::refresh::refresh_handler))
.route(
&format!("/{AUTH_API_PATH}/refresh"),
post(api::refresh::refresh_handler),
)
// Login
.route("/login", post(api::login::login_handler))
.route(
&format!("/{AUTH_API_PATH}/login"),
post(api::login::login_handler),
)
// Converts auth code (+pkce code verifier) to auth tokens
.route("/token", post(api::token::auth_code_to_token_handler))
.route(
&format!("/{AUTH_API_PATH}/token"),
post(api::token::auth_code_to_token_handler),
)
// Login status (also let's one lift tokens from cookies).
.route("/status", get(api::login::login_status_handler))
.route(
&format!("/{AUTH_API_PATH}/status"),
get(api::login::login_status_handler),
)
// Logout [get]: deletes all sessions for the current user.
.route("/logout", get(api::logout::logout_handler))
.route(
&format!("/{AUTH_API_PATH}/logout"),
get(api::logout::logout_handler),
)
// Logout [post]: deletes given session
.route("/logout", post(api::logout::post_logout_handler))
.route(
&format!("/{AUTH_API_PATH}/logout"),
post(api::logout::post_logout_handler),
)
// Get a user's avatar.
.route(
"/avatar/:b64_user_id",
&format!("/{AUTH_API_PATH}/avatar/{{b64_user_id}}"),
get(api::avatar::get_avatar_url_handler),
)
// User delete.
.route("/delete", delete(api::delete::delete_handler))
.route(
&format!("/{AUTH_API_PATH}/delete"),
delete(api::delete::delete_handler),
)
// OAuth flows: list providers, login+callback
.nest("/oauth", oauth::oauth_router());
.nest(&format!("/{AUTH_API_PATH}/oauth"), oauth::oauth_router());
}
/// Replicating minimal functionality of the above main router in case the admin dash is routed
/// from a different port to prevent cross-origin requests.
pub(super) fn admin_auth_router() -> Router<crate::AppState> {
return Router::new()
.route("/login", post(api::login::login_handler))
.route("/status", get(api::login::login_status_handler))
.route("/logout", get(api::logout::logout_handler));
.route(
&format!("/{AUTH_API_PATH}/login"),
post(api::login::login_handler),
)
.route(
&format!("/{AUTH_API_PATH}/status"),
get(api::login::login_status_handler),
)
.route(
&format!("/{AUTH_API_PATH}/logout"),
get(api::logout::logout_handler),
);
}
#[cfg(test)]

View File

@@ -23,11 +23,11 @@ pub fn oauth_router() -> Router<AppState> {
get(list_providers::list_configured_providers_handler),
)
.route(
"/:provider/login",
"/{provider}/login",
get(login::login_with_external_auth_provider),
)
.route(
"/:provider/callback",
"/{provider}/callback",
get(callback::callback_from_external_auth_provider),
)
}

View File

@@ -1,6 +1,5 @@
use axum::{
async_trait,
extract::{FromRef, FromRequestParts},
extract::{FromRef, FromRequestParts, OptionalFromRequestParts},
http::{header, request::Parts},
};
use chrono::Duration;
@@ -25,7 +24,6 @@ pub(crate) struct Tokens {
pub refresh_token: Option<String>,
}
#[async_trait]
impl<S> FromRequestParts<S> for Tokens
where
AppState: FromRef<S>,
@@ -45,6 +43,28 @@ where
}
}
impl<S> OptionalFromRequestParts<S> for Tokens
where
AppState: FromRef<S>,
S: Send + Sync,
{
type Rejection = AuthError;
async fn from_request_parts(
parts: &mut Parts,
state: &S,
) -> Result<Option<Self>, Self::Rejection> {
let state = AppState::from_ref(state);
if let Ok(tokens) = extract_tokens_from_headers(&state, &parts.headers).await {
return Ok(Some(tokens));
}
let cookies = extract_cookies_from_parts(parts)?;
return Ok(extract_tokens_from_cookies(&state, &cookies).await.ok());
}
}
async fn extract_tokens_from_headers(
state: &AppState,
headers: &header::HeaderMap,

View File

@@ -233,20 +233,20 @@ pub(crate) fn auth_ui_router() -> Router<crate::AppState> {
);
return Router::new()
.route("/login", get(ui_login_handler))
.route("/logout", get(ui_logout_handler))
.route("/register", get(ui_register_handler))
.route("/_/auth/login", get(ui_login_handler))
.route("/_/auth/logout", get(ui_logout_handler))
.route("/_/auth/register", get(ui_register_handler))
.route(
"/reset_password/request",
"/_/auth/reset_password/request",
get(ui_reset_password_request_handler),
)
.route(
"/reset_password/update",
"/_/auth/reset_password/update",
get(ui_reset_password_update_handler),
)
.route("/change_password", get(ui_change_password_handler))
.route("/change_email", get(ui_change_email_handler))
.nest_service("/", serve_auth_assets);
.route("/_/auth/change_password", get(ui_change_password_handler))
.route("/_/auth/change_email", get(ui_change_email_handler))
.nest_service("/_/auth/", serve_auth_assets);
}
fn hidden_input(name: &str, value: Option<&String>) -> String {

View File

@@ -1,6 +1,5 @@
use axum::{
async_trait,
extract::{FromRef, FromRequestParts},
extract::{FromRef, FromRequestParts, OptionalFromRequestParts},
http::request::Parts,
};
use serde::{Deserialize, Serialize};
@@ -110,7 +109,6 @@ impl User {
}
}
#[async_trait]
impl<S> FromRequestParts<S> for User
where
AppState: FromRef<S>,
@@ -119,10 +117,30 @@ where
type Rejection = AuthError;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
let tokens = Tokens::from_request_parts(parts, state).await?;
let tokens = <Tokens as FromRequestParts<S>>::from_request_parts(parts, state).await?;
return User::from_token_claims(tokens.auth_token_claims);
}
}
impl<S> OptionalFromRequestParts<S> for User
where
AppState: FromRef<S>,
S: Send + Sync,
{
type Rejection = AuthError;
async fn from_request_parts(
parts: &mut Parts,
state: &S,
) -> Result<Option<Self>, Self::Rejection> {
let tokens = <Tokens as OptionalFromRequestParts<S>>::from_request_parts(parts, state).await?;
if let Some(tokens) = tokens {
return Ok(Some(User::from_token_claims(tokens.auth_token_claims)?));
}
return Ok(None);
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -165,6 +183,8 @@ mod tests {
.unwrap();
let (mut parts, _body) = request.into_parts();
User::from_request_parts(&mut parts, &state).await.unwrap();
<User as FromRequestParts<AppState>>::from_request_parts(&mut parts, &state)
.await
.unwrap();
}
}

View File

@@ -107,24 +107,27 @@ pub(crate) fn remove_all_cookies(cookies: &Cookies) {
}
}
#[cfg(test)]
pub(crate) fn extract_cookies_from_parts(parts: &mut Parts) -> Result<Cookies, AuthError> {
let cookies = Cookies::default();
for ref header in parts.headers.get_all(axum::http::header::COOKIE) {
cookies.add(Cookie::parse(header.to_str().unwrap().to_string()).unwrap());
}
return Ok(cookies);
}
#[cfg(not(test))]
pub(crate) fn extract_cookies_from_parts(parts: &mut Parts) -> Result<Cookies, AuthError> {
if let Some(cookies) = parts.extensions.get::<Cookies>() {
return Ok(cookies.clone());
};
log::error!("Failed to get Cookies");
return Err(AuthError::Internal("cookie error".into()));
// Fallback code for when handlers are called directly in unit tests w/o tower::Cookies
// middleware to parse the cookies header for us.
#[cfg(test)]
{
let cookies = Cookies::default();
for ref header in parts.headers.get_all(axum::http::header::COOKIE) {
cookies.add(Cookie::parse(header.to_str().unwrap().to_string()).unwrap());
}
return Ok(cookies);
}
#[cfg(not(test))]
{
log::error!("Failed to get Cookies");
return Err(AuthError::Internal("cookie error".into()));
}
}
pub async fn user_by_email(state: &AppState, email: &str) -> Result<DbUser, AuthError> {

View File

@@ -1,4 +1,3 @@
use axum::async_trait;
use axum::extract::{rejection::*, Form, FromRequest, Request};
use axum::http::header::CONTENT_TYPE;
use axum::http::StatusCode;
@@ -43,7 +42,6 @@ pub enum Either<T> {
// Proto(DynamicMessage),
}
#[async_trait]
impl<S, T> FromRequest<S> for Either<T>
where
T: DeserializeOwned + Sync + Send + 'static,

View File

@@ -758,21 +758,20 @@ pub(crate) async fn load_routes_from_js_modules(
}
};
let mut js_router = Some(Router::new());
let mut js_router = Router::new();
for module in modules {
let fname = module.filename().to_owned();
let router = install_routes(state.script_runtime(), module).await?;
if let Some(router) = router {
js_router = Some(js_router.take().unwrap().nest("/", router));
js_router = js_router.merge(router);
} else {
log::debug!("Skipping js module '{fname:?}': no routes");
}
}
let router = js_router.take().unwrap();
if router.has_routes() {
return Ok(Some(router));
if js_router.has_routes() {
return Ok(Some(js_router));
}
return Ok(None);

View File

@@ -24,6 +24,7 @@ pub(crate) use validate::validate_record_api_config;
use crate::config::proto::{PermissionFlag, RecordApiConfig};
use crate::config::ConfigError;
use crate::constants::RECORD_API_PATH;
use crate::AppState;
#[derive(OpenApi)]
@@ -44,26 +45,38 @@ pub(super) struct RecordOpenApi;
pub(crate) fn router() -> Router<AppState> {
return Router::new()
.route("/:name/:record", get(read_record::read_record_handler))
.route("/:name", post(create_record::create_record_handler))
.route(
"/:name/:record",
&format!("/{RECORD_API_PATH}/{{name}}/{{record}}"),
get(read_record::read_record_handler),
)
.route(
&format!("/{RECORD_API_PATH}/{{name}}"),
post(create_record::create_record_handler),
)
.route(
&format!("/{RECORD_API_PATH}/{{name}}/{{record}}"),
patch(update_record::update_record_handler),
)
.route(
"/:name/:record",
&format!("/{RECORD_API_PATH}/{{name}}/{{record}}"),
delete(delete_record::delete_record_handler),
)
.route("/:name", get(list_records::list_records_handler))
.route(
"/:name/:record/file/:column_name",
&format!("/{RECORD_API_PATH}/{{name}}"),
get(list_records::list_records_handler),
)
.route(
&format!("/{RECORD_API_PATH}/{{name}}/{{record}}/file/{{column_name}}"),
get(read_record::get_uploaded_file_from_record_handler),
)
.route(
"/:name/:record/files/:column_name/:file_index",
&format!("/{RECORD_API_PATH}/{{name}}/{{record}}/files/{{column_name}}/{{file_index}}"),
get(read_record::get_uploaded_files_from_record_handler),
)
.route("/:name/schema", get(json_schema::json_schema_handler));
.route(
&format!("/{RECORD_API_PATH}/{{name}}/schema"),
get(json_schema::json_schema_handler),
);
}
// Since this is for APIs access control, we'll use the API- space CRUD terminology instead of

View File

@@ -19,9 +19,10 @@ use crate::app_state::AppState;
use crate::assets::AssetService;
use crate::auth::util::is_admin;
use crate::auth::{self, AuthError, User};
use crate::constants::{ADMIN_API_PATH, AUTH_API_PATH, HEADER_CSRF_TOKEN, RECORD_API_PATH};
use crate::constants::{ADMIN_API_PATH, HEADER_CSRF_TOKEN};
use crate::data_dir::DataDir;
use crate::logging;
use crate::records;
use crate::scheduler;
pub use init::{init_app_state, InitArgs, InitError};
@@ -114,7 +115,7 @@ impl Server {
.map_err(|err| InitError::ScriptError(err.to_string()))?;
if let Some(js_routes) = js_routes {
Some(custom_routes.unwrap_or_default().nest("/", js_routes))
Some(custom_routes.unwrap_or_default().merge(js_routes))
} else {
custom_routes
}
@@ -233,8 +234,8 @@ impl Server {
}
let router = Router::new()
.nest(&format!("/{AUTH_API_PATH}"), auth::admin_auth_router())
.nest("/", Self::build_admin_router(state));
.merge(auth::admin_auth_router())
.merge(Self::build_admin_router(state));
return Some((
address.clone(),
@@ -249,20 +250,20 @@ impl Server {
) -> (String, Router<()>) {
let mut router = Router::new()
// Public, stable and versioned APIs.
.nest(&format!("/{RECORD_API_PATH}"), crate::records::router())
.nest(&format!("/{AUTH_API_PATH}"), auth::router())
.merge(records::router())
.merge(auth::router())
.route("/api/healthcheck", get(healthcheck_handler));
if !has_indepenedent_admin_router(opts) {
router = router.nest("/", Self::build_admin_router(state));
router = router.merge(Self::build_admin_router(state));
}
if !opts.disable_auth_ui {
router = router.nest("/_/auth", crate::auth::auth_ui_router());
router = router.merge(auth::auth_ui_router());
}
if let Some(custom_router) = custom_router {
router = router.nest("/", custom_router);
router = router.merge(custom_router);
}
if let Some(public_dir) = &opts.public_dir {