From 19a52abb95d379d86aa8b1c13e54d486c8081460 Mon Sep 17 00:00:00 2001 From: "Dotan J. Nahum" Date: Sun, 13 Oct 2024 13:00:48 +0300 Subject: [PATCH] prep v0.10.0 (#811) releasing v0.10.x --- CHANGELOG.md | 14 +- Cargo.toml | 2 +- docs-site/content/docs/the-app/controller.md | 162 +++++- examples/demo/Cargo.lock | 2 +- examples/demo/config/development.yaml | 53 +- loco-extras/Cargo.toml | 2 +- src/bgworker/mod.rs | 7 +- src/bgworker/pg.rs | 37 +- src/bgworker/skq.rs | 20 + src/boot.rs | 28 +- src/cli.rs | 40 +- src/config.rs | 1 + src/controller/app_routes.rs | 29 +- src/controller/middleware/catch_panic.rs | 8 +- src/controller/middleware/compression.rs | 12 +- src/controller/middleware/cors.rs | 56 +- src/controller/middleware/etag.rs | 23 +- src/controller/middleware/fallback.rs | 10 +- src/controller/middleware/limit_payload.rs | 10 +- src/controller/middleware/logger.rs | 42 +- src/controller/middleware/mod.rs | 168 ++++-- src/controller/middleware/remote_ip.rs | 5 +- src/controller/middleware/request_id.rs | 9 +- src/controller/middleware/secure_headers.rs | 58 +- src/controller/middleware/static_assets.rs | 34 +- src/controller/middleware/timeout.rs | 43 +- src/gen/mod.rs | 17 +- src/lib.rs | 2 +- starters/lightweight-service/Cargo.lock | 541 +++++------------- starters/lightweight-service/Cargo.toml | 12 +- .../config/development.yaml | 41 -- starters/lightweight-service/config/test.yaml | 22 - starters/lightweight-service/src/app.rs | 6 +- starters/rest-api/Cargo.lock | 344 +++++------ starters/rest-api/Cargo.toml | 6 +- starters/rest-api/config/development.yaml | 55 +- starters/rest-api/config/test.yaml | 36 +- starters/rest-api/migration/Cargo.toml | 2 +- starters/rest-api/src/app.rs | 7 +- starters/rest-api/src/models/users.rs | 2 +- starters/rest-api/src/workers/downloader.rs | 9 +- starters/saas/Cargo.lock | 352 ++++++------ starters/saas/Cargo.toml | 6 +- starters/saas/config/development.yaml | 64 +-- starters/saas/config/test.yaml | 35 +- starters/saas/migration/Cargo.toml | 2 +- starters/saas/src/app.rs | 7 +- starters/saas/src/models/users.rs | 2 +- starters/saas/src/workers/downloader.rs | 9 +- tests/controller/middlewares.rs | 45 +- ...rs_[none]_overrides[none]@middlewares.snap | 4 +- 51 files changed, 1148 insertions(+), 1355 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 609bb37ea..decc2642a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ * `Format(respond_to): Format` extractor in controller can now be replaced with `respond_to: RespondTo` extractor for less typing. * When supplying data to views, you can now use `data!` instead of `serde_json::json!` for shorthand. -* Refactor middlewares. [https://github.com/loco-rs/loco/pull/785](https://github.com/loco-rs/loco/pull/785) +* Refactor middlewares. [https://github.com/loco-rs/loco/pull/785](https://github.com/loco-rs/loco/pull/785). Middleware selection, configuration, and tweaking is MUCH more powerful and convenient now. You can keep the `middleware:` section empty or remove it now, see more in [the middleware docs](https://loco.rs/docs/the-app/controller/#middleware) * **NEW (BREAKING)** background worker subsystem is now queue agnostic. Providing for both Redis and Postgres with a change of configuration. This means you can now use a full-Postgres stack to remove Redis as a dependency if you wish. Here are steps to migrate your codebase: ```rust @@ -21,9 +21,11 @@ async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()>{ Ok(()) } -// in your app.rs, remove the `worker` module references. +// in your app.rs, replace the `worker` module references. // REMOVE worker::{AppWorker, Processor}, +// REPLACE WITH +bgworker::{BackgroundWorker, Queue}, // in your workers change the signature, and add the `build` function @@ -49,6 +51,14 @@ impl worker::AppWorker for DownloadWorker { } ``` +Finally, update your `development.yaml` and `test.yaml` with a `kind`: + +```yaml +queue: + kind: Redis # add this to the existing `queue` section +``` + + * **UPGRADED (BREAKING)**: `validator` crate was upgraded which require some small tweaks to work with the new API: ```rust diff --git a/Cargo.toml b/Cargo.toml index 708762a94..e5df7eebb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ license = "Apache-2.0" [package] name = "loco-rs" -version = "0.9.0" +version = "0.10.1" description = "The one-person framework for Rust" homepage = "https://loco.rs/" documentation = "https://docs.rs/loco-rs" diff --git a/docs-site/content/docs/the-app/controller.md b/docs-site/content/docs/the-app/controller.md index fd12c42cd..d2d70d6c0 100644 --- a/docs-site/content/docs/the-app/controller.md +++ b/docs-site/content/docs/the-app/controller.md @@ -261,6 +261,8 @@ impl Hooks for App { Loco comes with a set of built-in middleware out of the box. Some are enabled by default, while others need to be configured. Middleware registration is flexible and can be managed either through the `*.yaml` environment configuration or directly in the code. +## The default stack + You get all the enabled middlewares run the following command ```sh @@ -268,6 +270,139 @@ cargo loco middleware --config ``` +This is the stack in `development` mode: + +```sh +$ cargo loco middleware --config + +limit_payload {"enable":true,"body_limit":2000000} +cors {"enable":true,"allow_origins":["any"],"allow_headers":["*"],"allow_methods":["*"],"max_age":null,"vary":["origin","access-control-request-method","access-control-request-headers"]} +catch_panic {"enable":true} +etag {"enable":true} +logger {"config":{"enable":true},"environment":"development"} +request_id {"enable":true} +fallback {"enable":true,"code":200,"file":null,"not_found":null} +powered_by {"ident":"loco.rs"} + + +remote_ip (disabled) +compression (disabled) +timeout (disabled) +static_assets (disabled) +secure_headers (disabled) +``` + +### Example: disable all middleware + +Take what ever is enabled, and use `enable: false` with the relevant field. If `middlewares:` section in `server` is missing, add it. + +```yaml +server: + middlewares: + limit_payload: + enable: false + cors: + enable: false + catch_panic: + enable: false + etag: + enable: false + logger: + enable: false + request_id: + enable: false + fallback: + enable: false +``` + +The result: + +```sh +$ cargo loco middleware --config +powered_by {"ident":"loco.rs"} + + +limit_payload (disabled) +cors (disabled) +catch_panic (disabled) +etag (disabled) +remote_ip (disabled) +compression (disabled) +timeout_request (disabled) +static (disabled) +secure_headers (disabled) +logger (disabled) +request_id (disabled) +fallback (disabled) +``` + +You can control the `powered_by` middleware by changing the value for `server.ident`: + +```yaml +server: + ident: my-server #(or empty string to disable) +``` + +### Example: add a non-default middleware + +Lets add the _Remote IP_ middleware to the stack. This is done just by configuration: + +```yaml +server: + middlewares: + remote_ip: + enable: true +``` + +The result: + +```sh +$ cargo loco middleware --config + +limit_payload {"enable":true,"body_limit":2000000} +cors {"enable":true,"allow_origins":["any"],"allow_headers":["*"],"allow_methods":["*"],"max_age":null,"vary":["origin","access-control-request-method","access-control-request-headers"]} +catch_panic {"enable":true} +etag {"enable":true} +remote_ip {"enable":true,"trusted_proxies":null} +logger {"config":{"enable":true},"environment":"development"} +request_id {"enable":true} +fallback {"enable":true,"code":200,"file":null,"not_found":null} +powered_by {"ident":"loco.rs"} +``` + +### Example: change a configuration for an enabled middleware + +Let's change the request body limit to `5mb`. When overriding a middleware configuration, rememeber to keep an `enable: true`: + +```yaml + middlewares: + limit_payload: + enable: true + body_limit: 5mb +``` + +The result: + +```sh +$ cargo loco middleware --config + +limit_payload {"enable":true,"body_limit":5000000} +cors {"enable":true,"allow_origins":["any"],"allow_headers":["*"],"allow_methods":["*"],"max_age":null,"vary":["origin","access-control-request-method","access-control-request-headers"]} +catch_panic {"enable":true} +etag {"enable":true} +logger {"config":{"enable":true},"environment":"development"} +request_id {"enable":true} +fallback {"enable":true,"code":200,"file":null,"not_found":null} +powered_by {"ident":"loco.rs"} + + +remote_ip (disabled) +compression (disabled) +timeout_request (disabled) +static (disabled) +secure_headers (disabled) +``` + ### Authentication In the `Loco` framework, middleware plays a crucial role in authentication. `Loco` supports various authentication methods, including JSON Web Token (JWT) and API Key authentication. This section outlines how to configure and use authentication middleware in your application. @@ -523,6 +658,16 @@ server: foo: bar ``` +To support `htmx`, You can add the following override, to allow some inline running of scripts: + +```yaml +secure_headers: + preset: github + overrides: + # this allows you to use HTMX, and has unsafe-inline. Remove or consider in production + "Content-Security-Policy": "default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src 'unsafe-inline' 'self' https:; style-src 'self' https: 'unsafe-inline'" +``` + ## Compression `Loco` leverages [CompressionLayer](https://docs.rs/tower-http/0.5.0/tower_http/compression/index.html) to enable a `one click` solution. @@ -554,14 +699,7 @@ middlewares: precompressed: true ``` -## Handler and Route based middleware - -`Loco` also allow us to apply [layers](https://docs.rs/tower/latest/tower/trait.Layer.html) to specific handlers or -routes. -For more information on handler and route based middleware, refer to the [middleware](/docs/the-app/middlewares) -documentation. - -## Cors +## CORS This middleware enables Cross-Origin Resource Sharing (CORS) by allowing configurable origins, methods, and headers in HTTP requests. It can be tailored to fit various application requirements, supporting permissive CORS or specific rules as defined in the middleware configuration. @@ -585,6 +723,14 @@ middlewares: ``` +## Handler and Route based middleware + +`Loco` also allow us to apply [layers](https://docs.rs/tower/latest/tower/trait.Layer.html) to specific handlers or +routes. +For more information on handler and route based middleware, refer to the [middleware](/docs/the-app/middlewares) +documentation. + + ### Handler based middleware: Apply a layer to a specific handler using `layer` method. diff --git a/examples/demo/Cargo.lock b/examples/demo/Cargo.lock index 7e691e0d2..f5803b146 100644 --- a/examples/demo/Cargo.lock +++ b/examples/demo/Cargo.lock @@ -3015,7 +3015,7 @@ dependencies = [ [[package]] name = "loco-rs" -version = "0.9.0" +version = "0.10.0" dependencies = [ "argon2", "async-trait", diff --git a/examples/demo/config/development.yaml b/examples/demo/config/development.yaml index a73d85950..ac3c78131 100644 --- a/examples/demo/config/development.yaml +++ b/examples/demo/config/development.yaml @@ -30,58 +30,7 @@ server: port: {{ get_env(name="NODE_PORT", default=5150) }} # The UI hostname or IP address that mailers will point to. host: http://localhost - # Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block - # - middlewares: - # Allows to limit the payload size request. payload that bigger than this file will blocked the request. - limit_payload: - # Enable/Disable the middleware. - enable: true - # the limit size. can be b,kb,kib,mb,mib,gb,gib - body_limit: 5mb - # set secure headers - secure_headers: - preset: github - overrides: - # this allows you to use HTMX, and has unsafe-inline. Remove or consider in production - "Content-Security-Policy": "default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src 'unsafe-inline' 'self' https:; style-src 'self' https: 'unsafe-inline'" - # Generating a unique request ID and enhancing logging with additional information such as the start and completion of request processing, latency, status code, and other request details. - logger: - # Enable/Disable the middleware. - enable: true - # when your code is panicked, the request still returns 500 status code. - catch_panic: - # Enable/Disable the middleware. - enable: true - # Timeout for incoming requests middleware. requests that take more time from the configuration will cute and 408 status code will returned. - timeout_request: - # Enable/Disable the middleware. - enable: true - # Duration time in milliseconds. - timeout: 5000 - compression: - # Enable/Disable the middleware. - enable: true - static_assets: - enable: true - must_exist: true - precompressed: true - folder: - path: assets - fallback: index.html - cors: - enable: true - # Set the value of the [`Access-Control-Allow-Origin`][mdn] header - # allow_origins: - # - https://loco.rs - # Set the value of the [`Access-Control-Allow-Headers`][mdn] header - # allow_headers: - # - Content-Type - # Set the value of the [`Access-Control-Allow-Methods`][mdn] header - # allow_methods: - # - POST - # Set the value of the [`Access-Control-Max-Age`][mdn] header in seconds - # max_age: 3600 +# # Worker Configuration workers: diff --git a/loco-extras/Cargo.toml b/loco-extras/Cargo.toml index 55c4ffe51..350c13cb8 100644 --- a/loco-extras/Cargo.toml +++ b/loco-extras/Cargo.toml @@ -39,7 +39,7 @@ mongodb = { version = "2.8.0", optional = true } [dependencies.loco-rs] path = "../" -version = "0.9.0" +version = "*" default-features = true features = ["with-db", "auth_jwt"] diff --git a/src/bgworker/mod.rs b/src/bgworker/mod.rs index 8a2727f89..d32c23be7 100644 --- a/src/bgworker/mod.rs +++ b/src/bgworker/mod.rs @@ -87,7 +87,7 @@ impl Queue { #[cfg(feature = "bg_pg")] Self::Postgres(_, registry, _) => { let mut r = registry.lock().await; - r.register_worker(W::class_name(), worker); + r.register_worker(W::class_name(), worker)?; } _ => {} } @@ -250,6 +250,11 @@ pub trait BackgroundWorker: Send + async fn perform(&self, args: A) -> crate::Result<()>; } +/// Initialize the system according to configuration +/// +/// # Errors +/// +/// This function will return an error if it fails pub async fn converge(queue: &Queue, config: &QueueConfig) -> Result<()> { queue.setup().await?; match config { diff --git a/src/bgworker/pg.rs b/src/bgworker/pg.rs index ac3836f15..b4ef06092 100644 --- a/src/bgworker/pg.rs +++ b/src/bgworker/pg.rs @@ -14,7 +14,7 @@ use tracing::{debug, error, trace}; use ulid::Ulid; use super::{BackgroundWorker, Queue}; -use crate::{config::PostgresQueueConfig, Result}; +use crate::{config::PostgresQueueConfig, Error, Result}; type TaskId = String; type TaskData = JsonValue; type TaskStatus = String; @@ -45,6 +45,7 @@ pub struct TaskRegistry { impl TaskRegistry { /// Creates a new `TaskRegistry`. + #[must_use] pub fn new() -> Self { Self { handlers: Arc::new(HashMap::new()), @@ -52,7 +53,9 @@ impl TaskRegistry { } /// Registers a task handler with the provided name. - pub fn register_worker(&mut self, name: String, worker: W) + /// # Errors + /// Fails if cannot register worker + pub fn register_worker(&mut self, name: String, worker: W) -> Result<()> where Args: Send + Serialize + Sync + 'static, W: BackgroundWorker + 'static, @@ -72,16 +75,19 @@ impl TaskRegistry { }; Arc::get_mut(&mut self.handlers) - .unwrap() + .ok_or_else(|| Error::string("cannot register worker"))? .insert(name, Box::new(wrapped_handler)); + Ok(()) } /// Returns a reference to the task handlers. + #[must_use] pub fn handlers(&self) -> &Arc> { &self.handlers } /// Runs the task handlers with the provided number of workers. + #[must_use] pub fn run(&self, pool: &PgPool, opts: &RunOpts) -> Vec> { let mut tasks = Vec::new(); @@ -167,6 +173,11 @@ async fn connect(cfg: &PostgresQueueConfig) -> Result { Ok(pool) } +/// Initialize task tables +/// +/// # Errors +/// +/// This function will return an error if it fails pub async fn initialize_database(pool: &PgPool) -> Result<()> { debug!("pg worker: initialize database"); sqlx::raw_sql( @@ -188,6 +199,11 @@ pub async fn initialize_database(pool: &PgPool) -> Result<()> { Ok(()) } +/// Add a task +/// +/// # Errors +/// +/// This function will return an error if it fails pub async fn enqueue( pool: &PgPool, name: &str, @@ -287,6 +303,11 @@ async fn fail_task(pool: &PgPool, task_id: &TaskId, error: &crate::Error) -> Res Ok(()) } +/// Clear all tasks +/// +/// # Errors +/// +/// This function will return an error if it fails pub async fn clear(pool: &PgPool) -> Result<()> { sqlx::query("DELETE from pg_loco_queue") .execute(pool) @@ -294,6 +315,11 @@ pub async fn clear(pool: &PgPool) -> Result<()> { Ok(()) } +/// Ping system +/// +/// # Errors +/// +/// This function will return an error if it fails pub async fn ping(pool: &PgPool) -> Result<()> { sqlx::query("SELECT id from pg_loco_queue LIMIT 1") .execute(pool) @@ -306,6 +332,11 @@ pub struct RunOpts { pub poll_interval_sec: u32, } +/// Create this provider +/// +/// # Errors +/// +/// This function will return an error if it fails pub async fn create_provider(qcfg: &PostgresQueueConfig) -> Result { let pool = connect(qcfg).await.map_err(Box::from)?; let registry = TaskRegistry::new(); diff --git a/src/bgworker/skq.rs b/src/bgworker/skq.rs index 248137303..9d883cda0 100644 --- a/src/bgworker/skq.rs +++ b/src/bgworker/skq.rs @@ -45,6 +45,11 @@ where res.map_err(|e| sidekiq::Error::Any(Box::from(e))) } } +/// Clear tasks +/// +/// # Errors +/// +/// This function will return an error if it fails pub async fn clear(pool: &RedisPool) -> Result<()> { let mut conn = pool.get().await?; sidekiq::redis_rs::cmd("FLUSHDB") @@ -53,6 +58,11 @@ pub async fn clear(pool: &RedisPool) -> Result<()> { Ok(()) } +/// Add a task +/// +/// # Errors +/// +/// This function will return an error if it fails pub async fn enqueue( pool: &RedisPool, class: String, @@ -67,6 +77,11 @@ pub async fn enqueue( Ok(()) } +/// Ping system +/// +/// # Errors +/// +/// This function will return an error if it fails pub async fn ping(pool: &RedisPool) -> Result<()> { let mut conn = pool.get().await?; Ok(sidekiq::redis_rs::cmd("PING") @@ -93,6 +108,11 @@ pub fn get_queues(config_queues: &Option>) -> Vec { queues } +/// Create this provider +/// +/// # Errors +/// +/// This function will return an error if it fails pub async fn create_provider(qcfg: &RedisQueueConfig) -> Result { let manager = RedisConnectionManager::new(qcfg.uri.clone())?; let redis = Pool::builder().build(manager).await?; diff --git a/src/boot.rs b/src/boot.rs index 3f0e80f56..236f38043 100644 --- a/src/boot.rs +++ b/src/boot.rs @@ -25,7 +25,6 @@ use crate::{ task::{self, Tasks}, Result, }; -use colored::Colorize; /// Represents the application startup mode. pub enum StartMode { @@ -381,23 +380,22 @@ pub fn list_endpoints(ctx: &AppContext) -> Vec { H::routes(ctx).collect() } +pub struct MiddlewareInfo { + pub id: String, + pub enabled: bool, + pub detail: String, +} + #[must_use] -pub fn list_middlewares(ctx: &AppContext, with_config: bool) -> Vec { - H::routes(ctx) - .middlewares::(ctx) +pub fn list_middlewares(ctx: &AppContext) -> Vec { + H::middlewares(ctx) .iter() - .map(|m| { - let text = heck::AsSnakeCase(m.name()).to_string().bold(); - if with_config { - format!( - "{text:<22} {}", - serde_json::to_string(&m.config().unwrap_or_default()).unwrap_or_default() - ) - } else { - format!("{text}") - } + .map(|m| MiddlewareInfo { + id: m.name().to_string(), + enabled: m.is_enabled(), + detail: m.config().unwrap_or_default().to_string(), }) - .collect::>() + .collect::>() } /// Initializes an [`EmailSender`] based on the mailer configuration settings diff --git a/src/cli.rs b/src/cli.rs index 27f962472..fa3b7b19e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -423,6 +423,8 @@ pub async fn playground() -> crate::Result { #[allow(clippy::too_many_lines)] #[allow(clippy::cognitive_complexity)] pub async fn main() -> crate::Result<()> { + use colored::Colorize; + let cli: Cli = Cli::parse(); let environment: Environment = cli.environment.unwrap_or_else(resolve_from_env).into(); @@ -473,9 +475,21 @@ pub async fn main() -> crate::Result<()> { } Commands::Middleware { config } => { let app_context = create_context::(&environment).await?; - let middlewares = list_middlewares::(&app_context, config); - for middleware in middlewares { - println!("{middleware}"); + let middlewares = list_middlewares::(&app_context); + for middleware in middlewares.iter().filter(|m| m.enabled) { + println!( + "{:<22} {}", + middleware.id.bold(), + if config { + middleware.detail.as_str() + } else { + "" + } + ); + } + println!("\n"); + for middleware in middlewares.iter().filter(|m| !m.enabled) { + println!("{:<22} (disabled)", middleware.id.bold().dimmed(),); } } Commands::Task { name, params } => { @@ -543,6 +557,8 @@ pub async fn main() -> crate::Result<()> { #[cfg(not(feature = "with-db"))] pub async fn main() -> crate::Result<()> { + use colored::Colorize; + let cli = Cli::parse(); let environment: Environment = cli.environment.unwrap_or_else(resolve_from_env).into(); @@ -586,9 +602,21 @@ pub async fn main() -> crate::Result<()> { } Commands::Middleware { config } => { let app_context = create_context::(&environment).await?; - let middlewares = list_middlewares::(&app_context, config); - for middleware in middlewares { - println!("{middleware}"); + let middlewares = list_middlewares::(&app_context); + for middleware in middlewares.iter().filter(|m| m.enabled) { + println!( + "{:<22} {}", + middleware.id.bold(), + if config { + middleware.detail.as_str() + } else { + "" + } + ); + } + println!("\n"); + for middleware in middlewares.iter().filter(|m| !m.enabled) { + println!("{:<22} (disabled)", middleware.id.bold().dimmed(),); } } Commands::Task { name, params } => { diff --git a/src/config.rs b/src/config.rs index 110f20f70..77d781120 100644 --- a/src/config.rs +++ b/src/config.rs @@ -373,6 +373,7 @@ pub struct Server { pub ident: Option, /// Middleware configurations for the server, including payload limits, /// logging, and error handling. + #[serde(default)] pub middlewares: middleware::Config, } diff --git a/src/controller/app_routes.rs b/src/controller/app_routes.rs index 449ee886a..20b2481ad 100644 --- a/src/controller/app_routes.rs +++ b/src/controller/app_routes.rs @@ -2,14 +2,14 @@ //! configuring routes in an Axum application. It allows you to define route //! prefixes, add routes, and configure middlewares for the application. +use std::fmt; + use axum::Router as AXRouter; use lazy_static::lazy_static; use regex::Regex; -use std::fmt; #[cfg(feature = "channels")] use super::channels::AppChannels; - use crate::{ app::{AppContext, Hooks}, controller::{middleware::MiddlewareLayer, routes::Routes}, @@ -209,10 +209,25 @@ impl AppRoutes { if let Some(channels) = self.channels.as_ref() { tracing::info!("[Middleware] +channels"); let channel_layer_app = tower::ServiceBuilder::new().layer(channels.layer.clone()); - if ctx.config.server.middlewares.cors.is_enabled() { + if ctx + .config + .server + .middlewares + .cors + .as_ref() + .is_some_and(super::middleware::MiddlewareLayer::is_enabled) + { app = app.layer( tower::ServiceBuilder::new() - .layer(ctx.config.server.middlewares.cors.cors()?) + .layer( + ctx.config + .server + .middlewares + .cors + .clone() + .unwrap_or_default() + .cors()?, + ) .layer(channel_layer_app), ); } else { @@ -237,13 +252,13 @@ impl AppRoutes { #[cfg(test)] mod tests { - use super::*; - use crate::prelude::*; - use crate::tests_cfg; use insta::assert_debug_snapshot; use rstest::rstest; use tower::ServiceExt; + use super::*; + use crate::{prelude::*, tests_cfg}; + async fn action() -> Result { format::json("loco") } diff --git a/src/controller/middleware/catch_panic.rs b/src/controller/middleware/catch_panic.rs index 3e42e8a58..2214e220b 100644 --- a/src/controller/middleware/catch_panic.rs +++ b/src/controller/middleware/catch_panic.rs @@ -17,15 +17,10 @@ use crate::{ #[derive(Debug, Clone, Deserialize, Serialize)] pub struct CatchPanic { + #[serde(default)] pub enable: bool, } -impl Default for CatchPanic { - fn default() -> Self { - Self { enable: true } - } -} - /// Handler function for the [`CatchPanicLayer`] middleware. /// /// This function processes panics by extracting error messages, logging them, @@ -77,6 +72,7 @@ mod tests { use super::*; use crate::tests_cfg; + #[allow(dependency_on_unit_never_type_fallback)] #[tokio::test] async fn panic_enabled() { let middleware = CatchPanic { enable: true }; diff --git a/src/controller/middleware/compression.rs b/src/controller/middleware/compression.rs index 1b301192e..42a2de54a 100644 --- a/src/controller/middleware/compression.rs +++ b/src/controller/middleware/compression.rs @@ -5,20 +5,16 @@ //! times and reducing bandwidth usage. The middleware configuration allows for //! enabling or disabling compression based on the application settings. -use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Result}; use axum::Router as AXRouter; use serde::{Deserialize, Serialize}; use tower_http::compression::CompressionLayer; +use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Result}; + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Compression { - enable: bool, -} - -impl Default for Compression { - fn default() -> Self { - Self { enable: true } - } + #[serde(default)] + pub enable: bool, } impl MiddlewareLayer for Compression { diff --git a/src/controller/middleware/cors.rs b/src/controller/middleware/cors.rs index 00fa3d439..4ed7de71b 100644 --- a/src/controller/middleware/cors.rs +++ b/src/controller/middleware/cors.rs @@ -1,19 +1,23 @@ //! Configurable and Flexible CORS Middleware //! //! This middleware enables Cross-Origin Resource Sharing (CORS) by allowing -//! configurable origins, methods, and headers in HTTP requests. It can be tailored -//! to fit various application requirements, supporting permissive CORS or -//! specific rules as defined in the middleware configuration. +//! configurable origins, methods, and headers in HTTP requests. It can be +//! tailored to fit various application requirements, supporting permissive CORS +//! or specific rules as defined in the middleware configuration. + +use std::time::Duration; -use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Result}; use axum::Router as AXRouter; use serde::{Deserialize, Serialize}; -use std::time::Duration; +use serde_json::json; use tower_http::cors; +use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Result}; + /// CORS middleware configuration -#[derive(Default, Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Clone, Deserialize, Serialize)] pub struct Cors { + #[serde(default)] pub enable: bool, /// Allow origins #[serde(default = "default_allow_origins")] @@ -31,6 +35,12 @@ pub struct Cors { pub vary: Vec, } +impl Default for Cors { + fn default() -> Self { + serde_json::from_value(json!({})).unwrap() + } +} + fn default_allow_origins() -> Vec { vec!["any".to_string()] } @@ -52,21 +62,32 @@ fn default_vary_headers() -> Vec { } impl Cors { + #[must_use] + pub fn empty() -> Self { + Self { + enable: true, + allow_headers: vec![], + allow_methods: vec![], + allow_origins: vec![], + max_age: None, + vary: vec![], + } + } /// Creates cors layer /// /// # Errors /// /// This function returns an error in the following cases: /// - /// - If any of the provided origins in `allow_origins` cannot be parsed as a valid URI, - /// the function will return a parsing error. - /// - If any of the provided headers in `allow_headers` cannot be parsed as valid HTTP headers, - /// the function will return a parsing error. - /// - If any of the provided methods in `allow_methods` cannot be parsed as valid HTTP methods, - /// the function will return a parsing error. + /// - If any of the provided origins in `allow_origins` cannot be parsed as + /// a valid URI, the function will return a parsing error. + /// - If any of the provided headers in `allow_headers` cannot be parsed as + /// valid HTTP headers, the function will return a parsing error. + /// - If any of the provided methods in `allow_methods` cannot be parsed as + /// valid HTTP methods, the function will return a parsing error. /// - /// In all of these cases, the error returned will be the result of the `parse` method - /// of the corresponding type. + /// In all of these cases, the error returned will be the result of the + /// `parse` method of the corresponding type. pub fn cors(&self) -> Result { let mut cors: cors::CorsLayer = cors::CorsLayer::permissive(); @@ -139,8 +160,6 @@ impl MiddlewareLayer for Cors { #[cfg(test)] mod tests { - use super::*; - use crate::tests_cfg; use axum::{ body::Body, http::{Method, Request}, @@ -151,6 +170,9 @@ mod tests { use rstest::rstest; use tower::ServiceExt; + use super::*; + use crate::tests_cfg; + #[rstest] #[case("default", None, None, None)] #[case("with_allow_headers", Some(vec!["token".to_string(), "user".to_string()]), None, None)] @@ -164,7 +186,7 @@ mod tests { #[case] allow_methods: Option>, #[case] max_age: Option, ) { - let mut middleware = Cors::default(); + let mut middleware = Cors::empty(); if let Some(allow_headers) = allow_headers { middleware.allow_headers = allow_headers; } diff --git a/src/controller/middleware/etag.rs b/src/controller/middleware/etag.rs index f2b54227d..87029fd81 100644 --- a/src/controller/middleware/etag.rs +++ b/src/controller/middleware/etag.rs @@ -1,31 +1,29 @@ //! `ETag` Middleware for Caching Requests //! //! This middleware implements the [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) -//! HTTP header for caching responses in Axum. `ETags` are used to validate cache entries by comparing -//! a client's stored `ETag` with the one generated by the server. If the `ETags` match, a `304 Not Modified` -//! response is sent, avoiding the need to resend the full content. +//! HTTP header for caching responses in Axum. `ETags` are used to validate +//! cache entries by comparing a client's stored `ETag` with the one generated +//! by the server. If the `ETags` match, a `304 Not Modified` response is sent, +//! avoiding the need to resend the full content. + +use std::task::{Context, Poll}; -use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Result}; use axum::{ body::Body, extract::Request, http::StatusCode, response::Response, Router as AXRouter, }; use futures_util::future::BoxFuture; use hyper::header::{ETAG, IF_NONE_MATCH}; use serde::{Deserialize, Serialize}; -use std::task::{Context, Poll}; use tower::{Layer, Service}; +use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Result}; + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Etag { + #[serde(default)] pub enable: bool, } -impl Default for Etag { - fn default() -> Self { - Self { enable: true } - } -} - impl MiddlewareLayer for Etag { /// Returns the name of the middleware fn name(&self) -> &'static str { @@ -47,7 +45,8 @@ impl MiddlewareLayer for Etag { } } -/// [`EtagLayer`] struct for adding `ETag` functionality as a Tower service layer. +/// [`EtagLayer`] struct for adding `ETag` functionality as a Tower service +/// layer. #[derive(Default, Clone)] struct EtagLayer; diff --git a/src/controller/middleware/fallback.rs b/src/controller/middleware/fallback.rs index 4c4ebdcf5..0ef7faf19 100644 --- a/src/controller/middleware/fallback.rs +++ b/src/controller/middleware/fallback.rs @@ -6,16 +6,18 @@ use axum::{http::StatusCode, response::Html, Router as AXRouter}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde_json::json; use tower_http::services::ServeFile; use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Result}; pub struct StatusCodeWrapper(pub StatusCode); -#[derive(Default, Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Clone, Deserialize, Serialize)] pub struct Fallback { /// By default when enabled, returns a prebaked 404 not found page optimized /// for development. For production set something else (see fields below) + #[serde(default)] pub enable: bool, /// For the unlikely reason to return something different than `404`, you /// can set it here @@ -37,6 +39,12 @@ fn default_status_code() -> StatusCode { StatusCode::OK } +impl Default for Fallback { + fn default() -> Self { + serde_json::from_value(json!({})).unwrap() + } +} + fn deserialize_status_code<'de, D>(de: D) -> Result where D: Deserializer<'de>, diff --git a/src/controller/middleware/limit_payload.rs b/src/controller/middleware/limit_payload.rs index 5dd6b5184..313a93183 100644 --- a/src/controller/middleware/limit_payload.rs +++ b/src/controller/middleware/limit_payload.rs @@ -18,20 +18,26 @@ use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Result}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct LimitPayload { + #[serde(default)] pub enable: bool, #[serde(deserialize_with = "deserialize_body_limit")] + #[serde(default = "default_body_limit")] pub body_limit: usize, } impl Default for LimitPayload { fn default() -> Self { Self { - enable: true, - body_limit: 2_000_000, + enable: false, + body_limit: default_body_limit(), } } } +fn default_body_limit() -> usize { + 2_000_000 +} + fn deserialize_body_limit<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, diff --git a/src/controller/middleware/logger.rs b/src/controller/middleware/logger.rs index 98146e658..6f30b16b6 100644 --- a/src/controller/middleware/logger.rs +++ b/src/controller/middleware/logger.rs @@ -1,9 +1,15 @@ //! Logger Middleware //! -//! This middleware provides logging functionality for HTTP requests. It uses `TraceLayer` to -//! log detailed information about each request, such as the HTTP method, URI, version, user agent, -//! and an associated request ID. Additionally, it integrates the application's runtime environment -//! into the log context, allowing environment-specific logging (e.g., "development", "production"). +//! This middleware provides logging functionality for HTTP requests. It uses +//! `TraceLayer` to log detailed information about each request, such as the +//! HTTP method, URI, version, user agent, and an associated request ID. +//! Additionally, it integrates the application's runtime environment +//! into the log context, allowing environment-specific logging (e.g., +//! "development", "production"). + +use axum::{http, Router as AXRouter}; +use serde::{Deserialize, Serialize}; +use tower_http::{add_extension::AddExtensionLayer, trace::TraceLayer}; use crate::{ app::AppContext, @@ -11,19 +17,11 @@ use crate::{ environment::Environment, Result, }; -use axum::{http, Router as AXRouter}; -use serde::{Deserialize, Serialize}; -use tower_http::{add_extension::AddExtensionLayer, trace::TraceLayer}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Config { - enable: bool, -} - -impl Default for Config { - fn default() -> Self { - Self { enable: true } - } + #[serde(default)] + pub enable: bool, } /// [`Middleware`] struct responsible for logging HTTP requests. @@ -33,7 +31,8 @@ pub struct Middleware { environment: Environment, } -/// Creates a new instance of [`Middleware`] by cloning the [`Config`] configuration. +/// Creates a new instance of [`Middleware`] by cloning the [`Config`] +/// configuration. #[must_use] pub fn new(config: &Config, environment: &Environment) -> Middleware { Middleware { @@ -57,15 +56,16 @@ impl MiddlewareLayer for Middleware { serde_json::to_value(self) } - /// Applies the logger middleware to the application router by adding layers for: + /// Applies the logger middleware to the application router by adding layers + /// for: /// /// - `TraceLayer`: Logs detailed information about each HTTP request. - /// - `AddExtensionLayer`: Adds the current environment to the request extensions, making it - /// accessible to the `TraceLayer` for logging. - /// - /// The `TraceLayer` is customized with `make_span_with` to extract request-specific details - /// like method, URI, version, user agent, and request ID, then create a tracing span for the request. + /// - `AddExtensionLayer`: Adds the current environment to the request + /// extensions, making it accessible to the `TraceLayer` for logging. /// + /// The `TraceLayer` is customized with `make_span_with` to extract + /// request-specific details like method, URI, version, user agent, and + /// request ID, then create a tracing span for the request. fn apply(&self, app: AXRouter) -> Result> { Ok(app .layer( diff --git a/src/controller/middleware/mod.rs b/src/controller/middleware/mod.rs index 5675443db..4dc341675 100644 --- a/src/controller/middleware/mod.rs +++ b/src/controller/middleware/mod.rs @@ -24,16 +24,31 @@ pub mod static_assets; pub mod timeout; use axum::Router as AXRouter; +use limit_payload::LimitPayload; use serde::{Deserialize, Serialize}; -use crate::{app::AppContext, Result}; +use crate::{app::AppContext, environment::Environment, Result}; /// Trait representing the behavior of middleware components in the application. +/// When implementing a new middleware, make sure to go over this checklist: +/// * The name of the middleware should be an ID that is similar to the field +/// name in configuration (look at how `serde` calls it) +/// * Default value implementation should be paired with `serde` default +/// handlers and default serialization implementation. Which means deriving +/// `Default` will _not_ work. You can use `serde_json` and serialize a new +/// config from an empty value, which will cause `serde` default value +/// handlers to kick in. +/// * If you need completely blank values for configuration (for example for +/// testing), implement an `::empty() -> Self` call ad-hoc. pub trait MiddlewareLayer { /// Returns the name of the middleware. + /// This should match the name of the property in the containing + /// `middleware` section in configuration (as named by `serde`) fn name(&self) -> &'static str; /// Returns whether the middleware is enabled or not. + /// If the middleware is switchable, take this value from a configuration + /// value fn is_enabled(&self) -> bool { true } @@ -53,30 +68,109 @@ pub trait MiddlewareLayer { fn apply(&self, app: AXRouter) -> Result>; } -/// Constructs a default stack of middleware for the Axum application based on -/// the provided context. -/// -/// This function initializes and returns a vector of middleware components that -/// are commonly used in the application. Each middleware is created using its -/// respective `new` function and +#[allow(clippy::unnecessary_lazy_evaluations)] #[must_use] pub fn default_middleware_stack(ctx: &AppContext) -> Vec> { + // Shortened reference to middlewares + let middlewares = &ctx.config.server.middlewares; + vec![ - Box::new(ctx.config.server.middlewares.limit_payload.clone()), - Box::new(ctx.config.server.middlewares.cors.clone()), - Box::new(ctx.config.server.middlewares.catch_panic.clone()), - Box::new(ctx.config.server.middlewares.etag.clone()), - Box::new(ctx.config.server.middlewares.remote_ip.clone()), - Box::new(ctx.config.server.middlewares.compression.clone()), - Box::new(ctx.config.server.middlewares.timeout_request.clone()), - Box::new(ctx.config.server.middlewares.static_assets.clone()), - Box::new(ctx.config.server.middlewares.secure_headers.clone()), + // Limit Payload middleware with a default if none + Box::new( + middlewares + .limit_payload + .clone() + .unwrap_or_else(|| LimitPayload { + enable: true, + ..Default::default() + }), + ), + // CORS middleware with a default if none + Box::new(middlewares.cors.clone().unwrap_or_else(|| cors::Cors { + enable: true, + ..Default::default() + })), + // Catch Panic middleware with a default if none + Box::new( + middlewares + .catch_panic + .clone() + .unwrap_or_else(|| catch_panic::CatchPanic { enable: true }), + ), + // Etag middleware with a default if none + Box::new( + middlewares + .etag + .clone() + .unwrap_or_else(|| etag::Etag { enable: true }), + ), + // Remote IP middleware with a default if none + Box::new( + middlewares + .remote_ip + .clone() + .unwrap_or_else(|| remote_ip::RemoteIpMiddleware { + enable: false, + ..Default::default() + }), + ), + // Compression middleware with a default if none + Box::new( + middlewares + .compression + .clone() + .unwrap_or_else(|| compression::Compression { enable: false }), + ), + // Timeout Request middleware with a default if none + Box::new( + middlewares + .timeout_request + .clone() + .unwrap_or_else(|| timeout::TimeOut { + enable: false, + ..Default::default() + }), + ), + // Static Assets middleware with a default if none + Box::new(middlewares.static_assets.clone().unwrap_or_else(|| { + static_assets::StaticAssets { + enable: false, + ..Default::default() + } + })), + // Secure Headers middleware with a default if none + Box::new(middlewares.secure_headers.clone().unwrap_or_else(|| { + secure_headers::SecureHeader { + enable: false, + ..Default::default() + } + })), + // Logger middleware with default logger configuration Box::new(logger::new( - &ctx.config.server.middlewares.logger, + &middlewares + .logger + .clone() + .unwrap_or_else(|| logger::Config { enable: true }), &ctx.environment, )), - Box::new(ctx.config.server.middlewares.request_id.clone()), - Box::new(ctx.config.server.middlewares.fallback.clone()), + // Request ID middleware with a default if none + Box::new( + middlewares + .request_id + .clone() + .unwrap_or_else(|| request_id::RequestId { enable: true }), + ), + // Fallback middleware with a default if none + Box::new( + middlewares + .fallback + .clone() + .unwrap_or_else(|| fallback::Fallback { + enable: ctx.environment != Environment::Production, + ..Default::default() + }), + ), + // Powered by middleware with a default identifier Box::new(powered_by::new(ctx.config.server.ident.as_deref())), ] } @@ -85,51 +179,39 @@ pub fn default_middleware_stack(ctx: &AppContext) -> Vec, /// Etag cache headers. - #[serde(default)] - pub etag: etag::Etag, + pub etag: Option, /// Limit the payload request. - #[serde(default)] - pub limit_payload: limit_payload::LimitPayload, + pub limit_payload: Option, /// Logger and augmenting trace id with request data - #[serde(default)] - pub logger: logger::Config, + pub logger: Option, /// Catch any code panic and log the error. - #[serde(default)] - pub catch_panic: catch_panic::CatchPanic, + pub catch_panic: Option, /// Setting a global timeout for requests - #[serde(default)] - pub timeout_request: timeout::TimeOut, + pub timeout_request: Option, /// CORS configuration - #[serde(default)] - pub cors: cors::Cors, + pub cors: Option, /// Serving static assets #[serde(rename = "static")] - #[serde(default)] - pub static_assets: static_assets::StaticAssets, + pub static_assets: Option, /// Sets a set of secure headers - #[serde(default)] - pub secure_headers: secure_headers::SecureHeader, + pub secure_headers: Option, /// Calculates a remote IP based on `X-Forwarded-For` when behind a proxy - #[serde(default)] - pub remote_ip: remote_ip::RemoteIpMiddleware, + pub remote_ip: Option, /// Configure fallback behavior when hitting a missing URL - #[serde(default)] - pub fallback: fallback::Fallback, + pub fallback: Option, /// Request ID - #[serde(default)] - pub request_id: request_id::RequestId, + pub request_id: Option, } diff --git a/src/controller/middleware/remote_ip.rs b/src/controller/middleware/remote_ip.rs index 7723e1c01..b0fbf18d8 100644 --- a/src/controller/middleware/remote_ip.rs +++ b/src/controller/middleware/remote_ip.rs @@ -93,6 +93,7 @@ const X_FORWARDED_FOR: &str = "X-Forwarded-For"; /// "Trusted proxy list" #[derive(Default, Serialize, Deserialize, Debug, Clone)] pub struct RemoteIpMiddleware { + #[serde(default)] pub enable: bool, /// A list of alternative proxy list IP ranges and/or network range (will /// replace built-in proxy list) @@ -107,7 +108,9 @@ impl MiddlewareLayer for RemoteIpMiddleware { /// Returns whether the middleware is enabled or not fn is_enabled(&self) -> bool { - self.enable && self.trusted_proxies.as_ref().is_some_and(|t| !t.is_empty()) + self.enable + && (self.trusted_proxies.is_none() + || self.trusted_proxies.as_ref().is_some_and(|t| !t.is_empty())) } fn config(&self) -> serde_json::Result { diff --git a/src/controller/middleware/request_id.rs b/src/controller/middleware/request_id.rs index b22a95f41..aab60f54a 100644 --- a/src/controller/middleware/request_id.rs +++ b/src/controller/middleware/request_id.rs @@ -24,13 +24,8 @@ lazy_static! { #[derive(Debug, Clone, Deserialize, Serialize)] pub struct RequestId { - enable: bool, -} - -impl Default for RequestId { - fn default() -> Self { - Self { enable: true } - } + #[serde(default)] + pub enable: bool, } impl MiddlewareLayer for RequestId { diff --git a/src/controller/middleware/secure_headers.rs b/src/controller/middleware/secure_headers.rs index cbe35a608..bbdbe26c7 100644 --- a/src/controller/middleware/secure_headers.rs +++ b/src/controller/middleware/secure_headers.rs @@ -16,7 +16,7 @@ use axum::{ use futures_util::future::BoxFuture; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; -use serde_json; +use serde_json::{self, json}; use tower::{Layer, Service}; use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Error, Result}; @@ -61,35 +61,42 @@ lazy_static! { /// one: two /// ``` /// +/// To support `htmx`, You can add the following override, to allow some inline +/// running of scripts: +/// +/// ```yaml +/// secure_headers: +/// preset: github +/// overrides: +/// # this allows you to use HTMX, and has unsafe-inline. Remove or consider in production +/// "Content-Security-Policy": "default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src 'unsafe-inline' 'self' https:; style-src 'self' https: 'unsafe-inline'" +/// ``` +/// /// For the list of presets and their content look at [secure_headers.json](https://github.com/loco-rs/loco/blob/master/src/controller/middleware/secure_headers.rs) #[derive(Serialize, Deserialize, Debug, Clone)] pub struct SecureHeader { - #[serde(default = "default_true")] + #[serde(default)] pub enable: bool, - pub preset: Option, + #[serde(default = "default_preset")] + pub preset: String, + #[serde(default)] pub overrides: Option>, } -fn default_true() -> bool { - true -} - impl Default for SecureHeader { - /// Provides a default secure header configuration, using the `github` - /// preset. fn default() -> Self { - Self { - enable: true, - preset: Some("github".to_string()), - overrides: None, - } + serde_json::from_value(json!({})).unwrap() } } +fn default_preset() -> String { + "github".to_string() +} + impl MiddlewareLayer for SecureHeader { /// Returns the name of the middleware fn name(&self) -> &'static str { - "secure headers" + "secure_headers" } /// Returns whether the middleware is enabled or not @@ -113,14 +120,15 @@ impl SecureHeader { /// Applies the preset headers and any custom overrides. fn as_headers(&self) -> Result> { let mut headers = vec![]; - if let Some(preset) = &self.preset { - let p = PRESETS.get(preset).ok_or_else(|| { - Error::Message(format!( - "secure_headers: a preset named `{preset}` does not exist" - )) - })?; - Self::push_headers(&mut headers, p)?; - } + + let preset = &self.preset; + let p = PRESETS.get(preset).ok_or_else(|| { + Error::Message(format!( + "secure_headers: a preset named `{preset}` does not exist" + )) + })?; + + Self::push_headers(&mut headers, p)?; if let Some(overrides) = &self.overrides { Self::push_headers(&mut headers, overrides)?; } @@ -227,7 +235,7 @@ mod tests { async fn can_set_headers() { let config = SecureHeader { enable: true, - preset: Some("github".to_string()), + preset: "github".to_string(), overrides: None, }; let app = Router::new() @@ -251,7 +259,7 @@ mod tests { let config = SecureHeader { enable: true, - preset: Some("github".to_string()), + preset: "github".to_string(), overrides: Some(overrides), }; let app = Router::new() diff --git a/src/controller/middleware/static_assets.rs b/src/controller/middleware/static_assets.rs index 06e95e2f4..20aea4f45 100644 --- a/src/controller/middleware/static_assets.rs +++ b/src/controller/middleware/static_assets.rs @@ -13,25 +13,55 @@ use std::path::PathBuf; use axum::Router as AXRouter; use serde::{Deserialize, Serialize}; +use serde_json::json; use tower_http::services::{ServeDir, ServeFile}; use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Error, Result}; /// Static asset middleware configuration -#[derive(Default, Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Clone, Deserialize, Serialize)] pub struct StaticAssets { + #[serde(default)] pub enable: bool, /// Check that assets must exist on disk + #[serde(default = "default_must_exist")] pub must_exist: bool, /// Assets location + #[serde(default = "default_folder_config")] pub folder: FolderConfig, /// Fallback page for a case when no asset exists (404). Useful for SPA /// (single page app) where routes are virtual. + #[serde(default = "default_fallback")] pub fallback: String, /// Enable `precompressed_gzip` + #[serde(default = "default_precompressed")] pub precompressed: bool, } +impl Default for StaticAssets { + fn default() -> Self { + serde_json::from_value(json!({})).unwrap() + } +} + +fn default_must_exist() -> bool { + true +} + +fn default_precompressed() -> bool { + false +} + +fn default_fallback() -> String { + "assets/static/404.html".to_string() +} + +fn default_folder_config() -> FolderConfig { + FolderConfig { + uri: "/static".to_string(), + path: "assets/static".to_string(), + } +} #[derive(Default, Debug, Clone, Deserialize, Serialize)] pub struct FolderConfig { /// Uri for the assets @@ -44,7 +74,7 @@ pub struct FolderConfig { impl MiddlewareLayer for StaticAssets { /// Returns the name of the middleware. fn name(&self) -> &'static str { - "static_assets" + "static" } /// Checks if the static assets middleware is enabled. diff --git a/src/controller/middleware/timeout.rs b/src/controller/middleware/timeout.rs index 1488d204f..fc0a5c16e 100644 --- a/src/controller/middleware/timeout.rs +++ b/src/controller/middleware/timeout.rs @@ -1,32 +1,47 @@ //! Timeout Request Middleware. //! //! This middleware applies a timeout to requests processed by the application. -//! The timeout duration is configurable and defined via the [`TimeoutRequestMiddleware`] -//! configuration. The middleware ensures that requests do not run beyond the specified -//! timeout period, improving the overall performance and responsiveness of the application. +//! The timeout duration is configurable and defined via the +//! [`TimeoutRequestMiddleware`] configuration. The middleware ensures that +//! requests do not run beyond the specified timeout period, improving the +//! overall performance and responsiveness of the application. //! -//! If a request exceeds the specified timeout duration, the middleware will return -//! a `408 Request Timeout` status code to the client, indicating that the request -//! took too long to process. -//! -use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Result}; +//! If a request exceeds the specified timeout duration, the middleware will +//! return a `408 Request Timeout` status code to the client, indicating that +//! the request took too long to process. +use std::time::Duration; + use axum::Router as AXRouter; use serde::{Deserialize, Serialize}; -use std::time::Duration; +use serde_json::json; use tower_http::timeout::TimeoutLayer; +use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Result}; + /// Timeout middleware configuration -#[derive(Default, Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Clone, Deserialize, Serialize)] pub struct TimeOut { + #[serde(default)] pub enable: bool, // Timeout request in milliseconds + #[serde(default = "default_timeout")] pub timeout: u64, } +impl Default for TimeOut { + fn default() -> Self { + serde_json::from_value(json!({})).unwrap() + } +} + +fn default_timeout() -> u64 { + 5_000 +} + impl MiddlewareLayer for TimeOut { /// Returns the name of the middleware. fn name(&self) -> &'static str { - "timeout" + "timeout_request" } /// Checks if the timeout middleware is enabled. @@ -40,9 +55,9 @@ impl MiddlewareLayer for TimeOut { /// Applies the timeout middleware to the application router. /// - /// This method wraps the provided [`AXRouter`] in a [`TimeoutLayer`], ensuring - /// that requests exceeding the specified timeout duration will be interrupted. - /// + /// This method wraps the provided [`AXRouter`] in a [`TimeoutLayer`], + /// ensuring that requests exceeding the specified timeout duration will + /// be interrupted. fn apply(&self, app: AXRouter) -> Result> { Ok(app.layer(TimeoutLayer::new(Duration::from_millis(self.timeout)))) } diff --git a/src/gen/mod.rs b/src/gen/mod.rs index a8c629b9a..7640b74d6 100644 --- a/src/gen/mod.rs +++ b/src/gen/mod.rs @@ -246,10 +246,21 @@ pub fn generate(component: Component, config: &Config) -> Result<()> { match deployment_kind { DeploymentKind::Docker => { - let copy_asset_folder = - &config.server.middlewares.static_assets.folder.path.clone(); + let copy_asset_folder = &config + .server + .middlewares + .static_assets + .clone() + .map(|a| a.folder.path) + .unwrap_or_default(); - let fallback_file = &config.server.middlewares.static_assets.fallback.clone(); + let fallback_file = &config + .server + .middlewares + .static_assets + .clone() + .map(|a| a.fallback) + .unwrap_or_default(); let vars = json!({ "pkg_name": H::app_name(), diff --git a/src/lib.rs b/src/lib.rs index a359e2e56..5b3568b78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ pub use self::errors::Error; mod banner; -mod bgworker; +pub mod bgworker; pub mod prelude; #[cfg(feature = "with-db")] diff --git a/starters/lightweight-service/Cargo.lock b/starters/lightweight-service/Cargo.lock index 7576cfe58..43d503eef 100644 --- a/starters/lightweight-service/Cargo.lock +++ b/starters/lightweight-service/Cargo.lock @@ -141,6 +141,16 @@ dependencies = [ "password-hash", ] +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "async-compression" version = "0.4.12" @@ -182,9 +192,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" -version = "0.7.5" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", @@ -209,7 +219,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", "tracing", @@ -217,9 +227,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -230,7 +240,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", "tracing", @@ -253,7 +263,7 @@ dependencies = [ "mime", "pin-project-lite", "serde", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -261,11 +271,10 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" dependencies = [ - "heck 0.4.1", "proc-macro2", "quote", "syn 2.0.72", @@ -273,12 +282,12 @@ dependencies = [ [[package]] name = "axum-test" -version = "14.10.0" +version = "16.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167294800740b4b6bc7bfbccbf3a1d50a6c6e097342580ec4c11d1672e456292" +checksum = "3254184de359bbae2a8ca10b050870a7a4f1c8332a2c27d53f360b9835bb3911" dependencies = [ "anyhow", - "async-trait", + "assert-json-diff", "auto-future", "axum", "bytes", @@ -296,7 +305,7 @@ dependencies = [ "serde_urlencoded", "smallvec", "tokio", - "tower", + "tower 0.5.1", "url", ] @@ -339,24 +348,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "bb8" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10cf871f3ff2ce56432fddc2615ac7acc3aa22ca321f8fea800846fbb32f188" -dependencies = [ - "async-trait", - "futures-util", - "parking_lot", - "tokio", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.6.0" @@ -436,9 +427,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.6.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "camino" @@ -590,20 +581,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", - "tokio-util", -] - [[package]] name = "console" version = "0.15.8" @@ -613,6 +590,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", + "unicode-width", "windows-sys 0.52.0", ] @@ -662,17 +640,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "cron_clock" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8699d8ed16e3db689f8ae04d8dc3c6666a4ba7e724e5a157884b7cc385d16b" -dependencies = [ - "chrono", - "nom", - "once_cell", -] - [[package]] name = "crossbeam-channel" version = "0.5.13" @@ -707,31 +674,6 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" -[[package]] -name = "crossterm" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" -dependencies = [ - "bitflags 1.3.2", - "crossterm_winapi", - "libc", - "mio 0.8.11", - "parking_lot", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "cruet" version = "0.13.3" @@ -752,6 +694,41 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.72", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.72", +] + [[package]] name = "deranged" version = "0.3.11" @@ -767,6 +744,19 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "tempfile", + "thiserror", + "zeroize", +] + [[package]] name = "diff" version = "0.1.13" @@ -784,33 +774,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "duct" version = "0.13.7" @@ -1031,16 +994,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "gethostname" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -1083,7 +1036,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.6.0", + "bitflags", "ignore", "walkdir", ] @@ -1116,12 +1069,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "hostname" version = "0.4.0" @@ -1246,7 +1193,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower", + "tower 0.4.13", "tower-service", "tracing", ] @@ -1275,14 +1222,10 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.4.0" +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" @@ -1294,12 +1237,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - [[package]] name = "ignore" version = "0.4.22" @@ -1370,17 +1307,6 @@ dependencies = [ "serde", ] -[[package]] -name = "is-terminal" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -1442,7 +1368,7 @@ dependencies = [ "futures-util", "hostname", "httpdate", - "idna 0.5.0", + "idna", "mime", "nom", "percent-encoding", @@ -1468,16 +1394,6 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.6.0", - "libc", -] - [[package]] name = "linked-hash-map" version = "0.5.6" @@ -1502,7 +1418,7 @@ dependencies = [ [[package]] name = "loco-rs" -version = "0.9.0" +version = "0.10.1" dependencies = [ "argon2", "async-trait", @@ -1510,7 +1426,6 @@ dependencies = [ "axum-extra", "axum-test", "backtrace_printer", - "bb8", "byte-unit", "bytes", "cargo_metadata", @@ -1518,11 +1433,13 @@ dependencies = [ "chrono", "clap", "colored", + "dialoguer", "duct", "duct_sh", "english-to-cron", "fs-err", "futures-util", + "heck 0.4.1", "hyper", "include_dir", "ipnetwork", @@ -1532,9 +1449,7 @@ dependencies = [ "object_store", "rand", "regex", - "requestty", "rrgen", - "rusty-sidekiq", "serde", "serde_json", "serde_variant", @@ -1543,7 +1458,7 @@ dependencies = [ "thiserror", "tokio", "tokio-cron-scheduler", - "tower", + "tower 0.4.13", "tower-http", "tracing", "tracing-appender", @@ -1627,18 +1542,6 @@ dependencies = [ "adler", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "mio" version = "1.0.1" @@ -1697,16 +1600,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" version = "0.36.2" @@ -1718,9 +1611,9 @@ dependencies = [ [[package]] name = "object_store" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6da452820c715ce78221e8202ccc599b4a52f3e1eb3eedb487b680c81a8e3f3" +checksum = "25a0c4b3a0e31f8b66f71ad8064521efa773910196e2cde791436f13409f3b45" dependencies = [ "async-trait", "bytes", @@ -2047,44 +1940,13 @@ dependencies = [ "getrandom", ] -[[package]] -name = "redis" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa8455fa3621f6b41c514946de66ea0531f57ca017b2e6c7cc368035ea5b46df" -dependencies = [ - "async-trait", - "bytes", - "combine", - "futures-util", - "itoa", - "percent-encoding", - "pin-project-lite", - "ryu", - "sha1_smol", - "tokio", - "tokio-util", - "url", -] - [[package]] name = "redox_syscall" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.6.0", -] - -[[package]] -name = "redox_users" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" -dependencies = [ - "getrandom", - "libredox", - "thiserror", + "bitflags", ] [[package]] @@ -2137,32 +1999,6 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" -[[package]] -name = "requestty" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa883a1f3e288e65187f653e6ba2e84fdf810fe02f4c8074f9c723d1aa26e2ae" -dependencies = [ - "requestty-ui", - "shell-words", - "smallvec", - "tempfile", - "winsplit", -] - -[[package]] -name = "requestty-ui" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7549bab39cf982b629b68e7ec191a5574e85086e95c0ebe514c02d3b42ffe225" -dependencies = [ - "bitflags 1.3.2", - "crossterm", - "once_cell", - "textwrap", - "unicode-segmentation", -] - [[package]] name = "reserve-port" version = "2.0.1" @@ -2274,7 +2110,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.6.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -2329,32 +2165,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" -[[package]] -name = "rusty-sidekiq" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a00db3916faeea070039864f98d4fd759d96fc07722571a4918d996fea5621" -dependencies = [ - "async-trait", - "bb8", - "chrono", - "cron_clock", - "gethostname", - "heck 0.4.1", - "hex", - "num_cpus", - "rand", - "redis", - "serde", - "serde_json", - "sha2", - "slog-term", - "thiserror", - "tokio", - "tracing", - "tracing-subscriber", -] - [[package]] name = "ryu" version = "1.0.18" @@ -2510,12 +2320,6 @@ dependencies = [ "syn 2.0.72", ] -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - [[package]] name = "sha2" version = "0.10.8" @@ -2552,27 +2356,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio 0.8.11", - "signal-hook", -] - [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -2603,25 +2386,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "slog" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" - -[[package]] -name = "slog-term" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e022d0b998abfe5c3782c1f03551a596269450ccd677ea51c56f8b214610e8" -dependencies = [ - "is-terminal", - "slog", - "term", - "thread_local", - "time", -] - [[package]] name = "slug" version = "0.1.5" @@ -2638,32 +2402,25 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "smawk" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" - [[package]] name = "snafu" -version = "0.7.5" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" dependencies = [ - "doc-comment", "snafu-derive", ] [[package]] name = "snafu-derive" -version = "0.7.5" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] @@ -2714,7 +2471,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote", "unicode-ident", ] @@ -2775,28 +2531,6 @@ dependencies = [ "unic-segment", ] -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - -[[package]] -name = "textwrap" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" -dependencies = [ - "smawk", - "unicode-linebreak", - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.63" @@ -2875,15 +2609,14 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.1" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.1", - "parking_lot", + "mio", "pin-project-lite", "signal-hook-registry", "socket2", @@ -2974,14 +2707,30 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower-http" -version = "0.5.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97" dependencies = [ "async-compression", - "bitflags 2.6.0", + "bitflags", "bytes", "futures-core", "futures-util", @@ -3003,15 +2752,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -3189,12 +2938,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - [[package]] name = "unicode-normalization" version = "0.1.23" @@ -3204,12 +2947,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - [[package]] name = "unicode-width" version = "0.1.13" @@ -3235,7 +2972,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", ] @@ -3263,12 +3000,12 @@ dependencies = [ [[package]] name = "validator" -version = "0.16.1" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd" +checksum = "db79c75af171630a3148bd3e6d7c4f42b6a9a014c2945bc5ed0020cbb8d9478e" dependencies = [ - "idna 0.4.0", - "lazy_static", + "idna", + "once_cell", "regex", "serde", "serde_derive", @@ -3279,28 +3016,16 @@ dependencies = [ [[package]] name = "validator_derive" -version = "0.16.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af" +checksum = "df0bcf92720c40105ac4b2dda2a4ea3aa717d4d6a862cc217da653a4bd5c6b10" dependencies = [ - "if_chain", - "lazy_static", + "darling", + "once_cell", "proc-macro-error", "proc-macro2", "quote", - "regex", - "syn 1.0.109", - "validator_types", -] - -[[package]] -name = "validator_types" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3" -dependencies = [ - "proc-macro2", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] @@ -3601,12 +3326,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winsplit" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab703352da6a72f35c39a533526393725640575bb211f61987a2748323ad956" - [[package]] name = "yansi" version = "0.5.1" diff --git a/starters/lightweight-service/Cargo.toml b/starters/lightweight-service/Cargo.toml index b3108021c..861062bba 100644 --- a/starters/lightweight-service/Cargo.toml +++ b/starters/lightweight-service/Cargo.toml @@ -11,10 +11,12 @@ default-run = "loco_starter_template-cli" [dependencies] -loco-rs = { version = "0.9.0", default-features = false, features = ["cli"] } -serde = "*" -serde_json = "*" -tokio = { version = "1.33.0", default-features = false } +loco-rs = { version = "0.10.1", default-features = false, features = ["cli"] } +serde = "1" +serde_json = "1" +tokio = { version = "1.33.0", default-features = false, features = [ + "rt-multi-thread", +] } async-trait = "0.1.74" axum = "0.7.5" tracing = "0.1.40" @@ -33,7 +35,7 @@ required-features = [] [dev-dependencies] serial_test = "3.1.1" rstest = "0.21.0" -loco-rs = { version = "0.9.0", default-features = false, features = [ +loco-rs = { version = "0.10.1", default-features = false, features = [ "testing", "cli", ] } diff --git a/starters/lightweight-service/config/development.yaml b/starters/lightweight-service/config/development.yaml index 3996c8145..cb7330818 100644 --- a/starters/lightweight-service/config/development.yaml +++ b/starters/lightweight-service/config/development.yaml @@ -18,44 +18,3 @@ server: port: 5150 # The UI hostname or IP address that mailers will point to. host: http://localhost - # Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block - middlewares: - # Enable Etag cache header middleware - etag: - enable: true - # Allows to limit the payload size request. payload that bigger than this file will blocked the request. - limit_payload: - # Enable/Disable the middleware. - enable: true - # the limit size. can be b,kb,kib,mb,mib,gb,gib - body_limit: 5mb - # set secure headers - secure_headers: - preset: github - # calculate remote IP based on `X-Forwarded-For` when behind a proxy or load balancer - # use RemoteIP(..) extractor to get the remote IP. - # without this middleware, you'll get the proxy IP instead. - # For more: https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/middleware/remote_ip.rb - # - # NOTE! only enable when under a proxy, otherwise this can lead to IP spoofing vulnerabilities - # trust me, you'll know if you need this middleware. - remote_ip: - enable: false - # # replace the default trusted proxies: - # trusted_proxies: - # - ip range 1 - # - ip range 2 .. - # Generating a unique request ID and enhancing logging with additional information such as the start and completion of request processing, latency, status code, and other request details. - logger: - # Enable/Disable the middleware. - enable: true - # when your code is panicked, the request still returns 500 status code. - catch_panic: - # Enable/Disable the middleware. - enable: true - # Timeout for incoming requests middleware. requests that take more time from the configuration will cute and 408 status code will returned. - timeout_request: - # Enable/Disable the middleware. - enable: false - # Duration time in milliseconds. - timeout: 5000 diff --git a/starters/lightweight-service/config/test.yaml b/starters/lightweight-service/config/test.yaml index 35eb0fe0f..701d146b6 100644 --- a/starters/lightweight-service/config/test.yaml +++ b/starters/lightweight-service/config/test.yaml @@ -18,25 +18,3 @@ server: port: 5150 # The UI hostname or IP address that mailers will point to. host: http://localhost - # Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block - middlewares: - # Allows to limit the payload size request. payload that bigger than this file will blocked the request. - limit_payload: - # Enable/Disable the middleware. - enable: true - # the limit size. can be b,kb,kib,mb,mib,gb,gib - body_limit: 5mb - # Generating a unique request ID and enhancing logging with additional information such as the start and completion of request processing, latency, status code, and other request details. - logger: - # Enable/Disable the middleware. - enable: true - # when your code is panicked, the request still returns 500 status code. - catch_panic: - # Enable/Disable the middleware. - enable: true - # Timeout for incoming requests middleware. requests that take more time from the configuration will cute and 408 status code will returned. - timeout_request: - # Enable/Disable the middleware. - enable: false - # Duration time in milliseconds. - timeout: 5000 diff --git a/starters/lightweight-service/src/app.rs b/starters/lightweight-service/src/app.rs index f34487f23..2e7fff178 100644 --- a/starters/lightweight-service/src/app.rs +++ b/starters/lightweight-service/src/app.rs @@ -1,11 +1,11 @@ use async_trait::async_trait; use loco_rs::{ app::{AppContext, Hooks}, + bgworker::Queue, boot::{create_app, BootResult, StartMode}, controller::AppRoutes, environment::Environment, task::Tasks, - worker::Processor, Result, }; @@ -40,7 +40,9 @@ impl Hooks for App { .add_route(controllers::home::routes()) } - fn connect_workers<'a>(_p: &'a mut Processor, _ctx: &'a AppContext) {} + async fn connect_workers(_ctx: &AppContext, _queue: &Queue) -> Result<()> { + Ok(()) + } #[allow(unused_variables)] fn register_tasks(tasks: &mut Tasks) { diff --git a/starters/rest-api/Cargo.lock b/starters/rest-api/Cargo.lock index b4d3b5345..b30a8dd9a 100644 --- a/starters/rest-api/Cargo.lock +++ b/starters/rest-api/Cargo.lock @@ -165,6 +165,16 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "async-attributes" version = "1.1.2" @@ -397,9 +407,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" -version = "0.7.5" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", @@ -424,7 +434,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", "tracing", @@ -432,9 +442,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -445,7 +455,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", "tracing", @@ -468,7 +478,7 @@ dependencies = [ "mime", "pin-project-lite", "serde", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -476,11 +486,10 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" dependencies = [ - "heck 0.4.1", "proc-macro2", "quote", "syn 2.0.72", @@ -488,12 +497,12 @@ dependencies = [ [[package]] name = "axum-test" -version = "14.10.0" +version = "16.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167294800740b4b6bc7bfbccbf3a1d50a6c6e097342580ec4c11d1672e456292" +checksum = "3254184de359bbae2a8ca10b050870a7a4f1c8332a2c27d53f360b9835bb3911" dependencies = [ "anyhow", - "async-trait", + "assert-json-diff", "auto-future", "axum", "bytes", @@ -511,7 +520,7 @@ dependencies = [ "serde_urlencoded", "smallvec", "tokio", - "tower", + "tower 0.5.1", "url", ] @@ -748,9 +757,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "camino" @@ -941,6 +950,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", + "unicode-width", "windows-sys 0.52.0", ] @@ -1065,31 +1075,6 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" -[[package]] -name = "crossterm" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" -dependencies = [ - "bitflags 1.3.2", - "crossterm_winapi", - "libc", - "mio 0.8.11", - "parking_lot", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "cruet" version = "0.13.3" @@ -1110,6 +1095,41 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.72", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.72", +] + [[package]] name = "der" version = "0.7.9" @@ -1137,6 +1157,19 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "tempfile", + "thiserror", + "zeroize", +] + [[package]] name = "diff" version = "0.1.13" @@ -1176,12 +1209,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "dotenvy" version = "0.15.7" @@ -1829,7 +1856,7 @@ dependencies = [ "pin-project-lite", "socket2 0.5.7", "tokio", - "tower", + "tower 0.4.13", "tower-service", "tracing", ] @@ -1858,14 +1885,10 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.4.0" +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" @@ -1877,12 +1900,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - [[package]] name = "ignore" version = "0.4.22" @@ -2083,7 +2100,7 @@ dependencies = [ "futures-util", "hostname", "httpdate", - "idna 0.5.0", + "idna", "mime", "nom", "percent-encoding", @@ -2160,9 +2177,7 @@ dependencies = [ [[package]] name = "loco-rs" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b58f8834452bee76b822ad42a9042c3d86367805d1cb4c8f3633c73bd4e73c" +version = "0.10.1" dependencies = [ "argon2", "async-trait", @@ -2178,11 +2193,13 @@ dependencies = [ "chrono", "clap", "colored", + "dialoguer", "duct", "duct_sh", "english-to-cron", "fs-err", "futures-util", + "heck 0.4.1", "hyper", "include_dir", "ipnetwork", @@ -2194,7 +2211,6 @@ dependencies = [ "object_store", "rand", "regex", - "requestty", "rrgen", "rusty-sidekiq", "sea-orm", @@ -2203,15 +2219,17 @@ dependencies = [ "serde_json", "serde_variant", "serde_yaml", + "sqlx", "tera", "thiserror", "tokio", "tokio-cron-scheduler", - "tower", + "tower 0.4.13", "tower-http", "tracing", "tracing-appender", "tracing-subscriber", + "ulid", "uuid", "validator", ] @@ -2319,18 +2337,6 @@ dependencies = [ "adler", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "mio" version = "1.0.1" @@ -2478,9 +2484,9 @@ dependencies = [ [[package]] name = "object_store" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6da452820c715ce78221e8202ccc599b4a52f3e1eb3eedb487b680c81a8e3f3" +checksum = "25a0c4b3a0e31f8b66f71ad8064521efa773910196e2cde791436f13409f3b45" dependencies = [ "async-trait", "bytes", @@ -3092,32 +3098,6 @@ dependencies = [ "bytecheck", ] -[[package]] -name = "requestty" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa883a1f3e288e65187f653e6ba2e84fdf810fe02f4c8074f9c723d1aa26e2ae" -dependencies = [ - "requestty-ui", - "shell-words", - "smallvec", - "tempfile", - "winsplit", -] - -[[package]] -name = "requestty-ui" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7549bab39cf982b629b68e7ec191a5574e85086e95c0ebe514c02d3b42ffe225" -dependencies = [ - "bitflags 1.3.2", - "crossterm", - "once_cell", - "textwrap", - "unicode-segmentation", -] - [[package]] name = "reserve-port" version = "2.0.1" @@ -3802,27 +3782,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio 0.8.11", - "signal-hook", -] - [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -3916,32 +3875,25 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "smawk" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" - [[package]] name = "snafu" -version = "0.7.5" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" dependencies = [ - "doc-comment", "snafu-derive", ] [[package]] name = "snafu-derive" -version = "0.7.5" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] @@ -4360,17 +4312,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "textwrap" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" -dependencies = [ - "smawk", - "unicode-linebreak", - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.63" @@ -4449,14 +4390,14 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.1" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.1", + "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -4559,11 +4500,27 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower-http" -version = "0.5.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97" dependencies = [ "async-compression", "bitflags 2.6.0", @@ -4588,15 +4545,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -4709,6 +4666,17 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +[[package]] +name = "ulid" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f903f293d11f31c0c29e4148f6dc0d033a7f80cebc0282bea147611667d289" +dependencies = [ + "getrandom", + "rand", + "web-time", +] + [[package]] name = "unic-char-property" version = "0.9.0" @@ -4780,12 +4748,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - [[package]] name = "unicode-normalization" version = "0.1.23" @@ -4838,7 +4800,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", ] @@ -4873,12 +4835,12 @@ dependencies = [ [[package]] name = "validator" -version = "0.16.1" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd" +checksum = "db79c75af171630a3148bd3e6d7c4f42b6a9a014c2945bc5ed0020cbb8d9478e" dependencies = [ - "idna 0.4.0", - "lazy_static", + "idna", + "once_cell", "regex", "serde", "serde_derive", @@ -4889,28 +4851,16 @@ dependencies = [ [[package]] name = "validator_derive" -version = "0.16.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af" +checksum = "df0bcf92720c40105ac4b2dda2a4ea3aa717d4d6a862cc217da653a4bd5c6b10" dependencies = [ - "if_chain", - "lazy_static", + "darling", + "once_cell", "proc-macro-error", "proc-macro2", "quote", - "regex", - "syn 1.0.109", - "validator_types", -] - -[[package]] -name = "validator_types" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3" -dependencies = [ - "proc-macro2", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] @@ -5050,6 +5000,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.25.4" @@ -5273,12 +5233,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winsplit" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab703352da6a72f35c39a533526393725640575bb211f61987a2748323ad956" - [[package]] name = "wyz" version = "0.5.1" diff --git a/starters/rest-api/Cargo.toml b/starters/rest-api/Cargo.toml index b71365282..b4b5c2a96 100644 --- a/starters/rest-api/Cargo.toml +++ b/starters/rest-api/Cargo.toml @@ -11,7 +11,7 @@ default-run = "loco_starter_template-cli" [dependencies] -loco-rs = { version = "0.9.0" } +loco-rs = { version = "0.10.1" } migration = { path = "migration" } serde = { version = "1", features = ["derive"] } @@ -20,7 +20,7 @@ tokio = { version = "1.33.0", default-features = false } async-trait = "0.1.74" tracing = "0.1.40" chrono = "0.4" -validator = { version = "0.16" } +validator = { version = "0.18" } sea-orm = { version = "1.0.0", features = [ "sqlx-sqlite", "sqlx-postgres", @@ -46,5 +46,5 @@ required-features = [] [dev-dependencies] serial_test = "3.1.1" rstest = "0.21.0" -loco-rs = { version = "0.9.0", features = ["testing"] } +loco-rs = { version = "0.10.1", features = ["testing"] } insta = { version = "1.34.0", features = ["redactions", "yaml", "filters"] } diff --git a/starters/rest-api/config/development.yaml b/starters/rest-api/config/development.yaml index 8d40f0fe6..bdcde4409 100644 --- a/starters/rest-api/config/development.yaml +++ b/starters/rest-api/config/development.yaml @@ -20,60 +20,6 @@ server: port: 5150 # The UI hostname or IP address that mailers will point to. host: http://localhost - # Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block - middlewares: - # Enable Etag cache header middleware - etag: - enable: true - # Allows to limit the payload size request. payload that bigger than this file will blocked the request. - limit_payload: - # Enable/Disable the middleware. - enable: true - # the limit size. can be b,kb,kib,mb,mib,gb,gib - body_limit: 5mb - # set secure headers - secure_headers: - preset: github - # calculate remote IP based on `X-Forwarded-For` when behind a proxy or load balancer - # use RemoteIP(..) extractor to get the remote IP. - # without this middleware, you'll get the proxy IP instead. - # For more: https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/middleware/remote_ip.rb - # - # NOTE! only enable when under a proxy, otherwise this can lead to IP spoofing vulnerabilities - # trust me, you'll know if you need this middleware. - remote_ip: - enable: false - # # replace the default trusted proxies: - # trusted_proxies: - # - ip range 1 - # - ip range 2 .. - # Generating a unique request ID and enhancing logging with additional information such as the start and completion of request processing, latency, status code, and other request details. - logger: - # Enable/Disable the middleware. - enable: true - # when your code is panicked, the request still returns 500 status code. - catch_panic: - # Enable/Disable the middleware. - enable: true - # Timeout for incoming requests middleware. requests that take more time from the configuration will cute and 408 status code will returned. - timeout_request: - # Enable/Disable the middleware. - enable: false - # Duration time in milliseconds. - timeout: 5000 - cors: - enable: true - # Set the value of the [`Access-Control-Allow-Origin`][mdn] header - # allow_origins: - # - https://loco.rs - # Set the value of the [`Access-Control-Allow-Headers`][mdn] header - # allow_headers: - # - Content-Type - # Set the value of the [`Access-Control-Allow-Methods`][mdn] header - # allow_methods: - # - POST - # Set the value of the [`Access-Control-Max-Age`][mdn] header in seconds - # max_age: 3600 # Worker Configuration workers: @@ -129,6 +75,7 @@ database: # Queue Configuration queue: + kind: Redis # Redis connection URI uri: {{ get_env(name="REDIS_URL", default="redis://127.0.0.1") }} # Dangerously flush all data in Redis on startup. dangerous operation, make sure that you using this flag only on dev environments or test mode diff --git a/starters/rest-api/config/test.yaml b/starters/rest-api/config/test.yaml index 29ab51e67..f54d0b610 100644 --- a/starters/rest-api/config/test.yaml +++ b/starters/rest-api/config/test.yaml @@ -18,41 +18,6 @@ server: port: 5150 # The UI hostname or IP address that mailers will point to. host: http://localhost - # Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block - middlewares: - # Allows to limit the payload size request. payload that bigger than this file will blocked the request. - limit_payload: - # Enable/Disable the middleware. - enable: true - # the limit size. can be b,kb,kib,mb,mib,gb,gib - body_limit: 5mb - # Generating a unique request ID and enhancing logging with additional information such as the start and completion of request processing, latency, status code, and other request details. - logger: - # Enable/Disable the middleware. - enable: true - # when your code is panicked, the request still returns 500 status code. - catch_panic: - # Enable/Disable the middleware. - enable: true - # Timeout for incoming requests middleware. requests that take more time from the configuration will cute and 408 status code will returned. - timeout_request: - # Enable/Disable the middleware. - enable: false - # Duration time in milliseconds. - timeout: 5000 - cors: - enable: true - # Set the value of the [`Access-Control-Allow-Origin`][mdn] header - # allow_origins: - # - https://loco.rs - # Set the value of the [`Access-Control-Allow-Headers`][mdn] header - # allow_headers: - # - Content-Type - # Set the value of the [`Access-Control-Allow-Methods`][mdn] header - # allow_methods: - # - POST - # Set the value of the [`Access-Control-Max-Age`][mdn] header in seconds - # max_age: 3600 # Worker Configuration workers: @@ -110,6 +75,7 @@ database: # Queue Configuration queue: + kind: Redis # Redis connection URI uri: {{get_env(name="REDIS_URL", default="redis://127.0.0.1")}} # Dangerously flush all data in Redis on startup. dangerous operation, make sure that you using this flag only on dev environments or test mode diff --git a/starters/rest-api/migration/Cargo.toml b/starters/rest-api/migration/Cargo.toml index 723b73ccf..bf79cfe14 100644 --- a/starters/rest-api/migration/Cargo.toml +++ b/starters/rest-api/migration/Cargo.toml @@ -10,7 +10,7 @@ path = "src/lib.rs" [dependencies] async-std = { version = "1", features = ["attributes", "tokio1"] } -loco-rs = { version = "0.9.0" } +loco-rs = { version = "0.10.1" } [dependencies.sea-orm-migration] version = "1.0.0" diff --git a/starters/rest-api/src/app.rs b/starters/rest-api/src/app.rs index 3f247a1d1..c2f802bcc 100644 --- a/starters/rest-api/src/app.rs +++ b/starters/rest-api/src/app.rs @@ -3,12 +3,12 @@ use std::path::Path; use async_trait::async_trait; use loco_rs::{ app::{AppContext, Hooks}, + bgworker::{BackgroundWorker, Queue}, boot::{create_app, BootResult, StartMode}, controller::AppRoutes, db::{self, truncate_table}, environment::Environment, task::Tasks, - worker::{AppWorker, Processor}, Result, }; use migration::Migrator; @@ -50,8 +50,9 @@ impl Hooks for App { .add_route(controllers::user::routes()) } - fn connect_workers<'a>(p: &'a mut Processor, ctx: &'a AppContext) { - p.register(DownloadWorker::build(ctx)); + async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()> { + queue.register(DownloadWorker::build(ctx)).await?; + Ok(()) } fn register_tasks(tasks: &mut Tasks) { diff --git a/starters/rest-api/src/models/users.rs b/starters/rest-api/src/models/users.rs index 510802e0b..4189157e9 100644 --- a/starters/rest-api/src/models/users.rs +++ b/starters/rest-api/src/models/users.rs @@ -23,7 +23,7 @@ pub struct RegisterParams { pub struct Validator { #[validate(length(min = 2, message = "Name must be at least 2 characters long."))] pub name: String, - #[validate(custom = "validation::is_valid_email")] + #[validate(custom(function = "validation::is_valid_email"))] pub email: String, } diff --git a/starters/rest-api/src/workers/downloader.rs b/starters/rest-api/src/workers/downloader.rs index 42c0bd7a4..fa094d5b4 100644 --- a/starters/rest-api/src/workers/downloader.rs +++ b/starters/rest-api/src/workers/downloader.rs @@ -15,15 +15,12 @@ pub struct DownloadWorkerArgs { pub user_guid: String, } -impl worker::AppWorker for DownloadWorker { +#[async_trait] +impl BackgroundWorker for DownloadWorker { fn build(ctx: &AppContext) -> Self { Self { ctx: ctx.clone() } } -} - -#[async_trait] -impl worker::Worker for DownloadWorker { - async fn perform(&self, args: DownloadWorkerArgs) -> worker::Result<()> { + async fn perform(&self, args: DownloadWorkerArgs) -> Result<()> { // TODO: Some actual work goes here... println!("================================================"); println!("Sending payment report to user {}", args.user_guid); diff --git a/starters/saas/Cargo.lock b/starters/saas/Cargo.lock index 7c197e536..e2c8fd6ea 100644 --- a/starters/saas/Cargo.lock +++ b/starters/saas/Cargo.lock @@ -171,6 +171,16 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "async-attributes" version = "1.1.2" @@ -403,9 +413,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" -version = "0.7.5" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", @@ -430,7 +440,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", "tracing", @@ -438,9 +448,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -451,7 +461,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", "tracing", @@ -474,7 +484,7 @@ dependencies = [ "mime", "pin-project-lite", "serde", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -482,11 +492,10 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" dependencies = [ - "heck 0.4.1", "proc-macro2", "quote", "syn 2.0.72", @@ -494,12 +503,12 @@ dependencies = [ [[package]] name = "axum-test" -version = "14.10.0" +version = "16.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167294800740b4b6bc7bfbccbf3a1d50a6c6e097342580ec4c11d1672e456292" +checksum = "3254184de359bbae2a8ca10b050870a7a4f1c8332a2c27d53f360b9835bb3911" dependencies = [ "anyhow", - "async-trait", + "assert-json-diff", "auto-future", "axum", "bytes", @@ -517,7 +526,7 @@ dependencies = [ "serde_urlencoded", "smallvec", "tokio", - "tower", + "tower 0.5.1", "url", ] @@ -754,9 +763,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "camino" @@ -947,6 +956,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", + "unicode-width", "windows-sys 0.52.0", ] @@ -1071,31 +1081,6 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" -[[package]] -name = "crossterm" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" -dependencies = [ - "bitflags 1.3.2", - "crossterm_winapi", - "libc", - "mio 0.8.11", - "parking_lot", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "cruet" version = "0.13.3" @@ -1116,6 +1101,41 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.72", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.72", +] + [[package]] name = "der" version = "0.7.9" @@ -1143,6 +1163,19 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "tempfile", + "thiserror", + "zeroize", +] + [[package]] name = "diff" version = "0.1.13" @@ -1457,7 +1490,7 @@ dependencies = [ "log", "once_cell", "serde_json", - "snafu", + "snafu 0.7.5", "tera", "unic-langid", ] @@ -1939,7 +1972,7 @@ dependencies = [ "pin-project-lite", "socket2 0.5.7", "tokio", - "tower", + "tower 0.4.13", "tower-service", "tracing", ] @@ -1968,14 +2001,10 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.4.0" +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" @@ -1987,12 +2016,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - [[package]] name = "ignore" version = "0.4.22" @@ -2212,7 +2235,7 @@ dependencies = [ "futures-util", "hostname", "httpdate", - "idna 0.5.0", + "idna", "mime", "nom", "percent-encoding", @@ -2289,9 +2312,7 @@ dependencies = [ [[package]] name = "loco-rs" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b58f8834452bee76b822ad42a9042c3d86367805d1cb4c8f3633c73bd4e73c" +version = "0.10.1" dependencies = [ "argon2", "async-trait", @@ -2307,11 +2328,13 @@ dependencies = [ "chrono", "clap", "colored", + "dialoguer", "duct", "duct_sh", "english-to-cron", "fs-err", "futures-util", + "heck 0.4.1", "hyper", "include_dir", "ipnetwork", @@ -2323,7 +2346,6 @@ dependencies = [ "object_store", "rand", "regex", - "requestty", "rrgen", "rusty-sidekiq", "sea-orm", @@ -2332,15 +2354,17 @@ dependencies = [ "serde_json", "serde_variant", "serde_yaml", + "sqlx", "tera", "thiserror", "tokio", "tokio-cron-scheduler", - "tower", + "tower 0.4.13", "tower-http", "tracing", "tracing-appender", "tracing-subscriber", + "ulid", "uuid", "validator", ] @@ -2450,18 +2474,6 @@ dependencies = [ "adler", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "mio" version = "1.0.1" @@ -2609,9 +2621,9 @@ dependencies = [ [[package]] name = "object_store" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6da452820c715ce78221e8202ccc599b4a52f3e1eb3eedb487b680c81a8e3f3" +checksum = "25a0c4b3a0e31f8b66f71ad8064521efa773910196e2cde791436f13409f3b45" dependencies = [ "async-trait", "bytes", @@ -2621,7 +2633,7 @@ dependencies = [ "itertools", "parking_lot", "percent-encoding", - "snafu", + "snafu 0.8.5", "tokio", "tracing", "url", @@ -3229,32 +3241,6 @@ dependencies = [ "bytecheck", ] -[[package]] -name = "requestty" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa883a1f3e288e65187f653e6ba2e84fdf810fe02f4c8074f9c723d1aa26e2ae" -dependencies = [ - "requestty-ui", - "shell-words", - "smallvec", - "tempfile", - "winsplit", -] - -[[package]] -name = "requestty-ui" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7549bab39cf982b629b68e7ec191a5574e85086e95c0ebe514c02d3b42ffe225" -dependencies = [ - "bitflags 1.3.2", - "crossterm", - "once_cell", - "textwrap", - "unicode-segmentation", -] - [[package]] name = "reserve-port" version = "2.0.1" @@ -3960,27 +3946,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio 0.8.11", - "signal-hook", -] - [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -4074,12 +4039,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "smawk" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" - [[package]] name = "snafu" version = "0.7.5" @@ -4087,7 +4046,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" dependencies = [ "doc-comment", - "snafu-derive", + "snafu-derive 0.7.5", +] + +[[package]] +name = "snafu" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +dependencies = [ + "snafu-derive 0.8.5", ] [[package]] @@ -4102,6 +4070,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "snafu-derive" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "socket2" version = "0.4.10" @@ -4518,17 +4498,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "textwrap" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" -dependencies = [ - "smawk", - "unicode-linebreak", - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.63" @@ -4616,14 +4585,14 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.1" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.1", + "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -4726,11 +4695,27 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower-http" -version = "0.5.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97" dependencies = [ "async-compression", "bitflags 2.6.0", @@ -4755,15 +4740,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -4885,6 +4870,17 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +[[package]] +name = "ulid" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f903f293d11f31c0c29e4148f6dc0d033a7f80cebc0282bea147611667d289" +dependencies = [ + "getrandom", + "rand", + "web-time", +] + [[package]] name = "unic-char-property" version = "0.9.0" @@ -4999,12 +4995,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - [[package]] name = "unicode-normalization" version = "0.1.23" @@ -5057,7 +5047,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", ] @@ -5092,12 +5082,12 @@ dependencies = [ [[package]] name = "validator" -version = "0.16.1" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd" +checksum = "db79c75af171630a3148bd3e6d7c4f42b6a9a014c2945bc5ed0020cbb8d9478e" dependencies = [ - "idna 0.4.0", - "lazy_static", + "idna", + "once_cell", "regex", "serde", "serde_derive", @@ -5108,28 +5098,16 @@ dependencies = [ [[package]] name = "validator_derive" -version = "0.16.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af" +checksum = "df0bcf92720c40105ac4b2dda2a4ea3aa717d4d6a862cc217da653a4bd5c6b10" dependencies = [ - "if_chain", - "lazy_static", + "darling", + "once_cell", "proc-macro-error", "proc-macro2", "quote", - "regex", - "syn 1.0.109", - "validator_types", -] - -[[package]] -name = "validator_types" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3" -dependencies = [ - "proc-macro2", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] @@ -5269,6 +5247,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.25.4" @@ -5492,12 +5480,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winsplit" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab703352da6a72f35c39a533526393725640575bb211f61987a2748323ad956" - [[package]] name = "wyz" version = "0.5.1" diff --git a/starters/saas/Cargo.toml b/starters/saas/Cargo.toml index c4193b7e4..1711f7a90 100644 --- a/starters/saas/Cargo.toml +++ b/starters/saas/Cargo.toml @@ -11,7 +11,7 @@ default-run = "loco_starter_template-cli" [dependencies] -loco-rs = { version = "0.9.0" } +loco-rs = { version = "0.10.1" } migration = { path = "migration" } serde = { version = "1", features = ["derive"] } @@ -20,7 +20,7 @@ tokio = { version = "1.33.0", default-features = false } async-trait = "0.1.74" tracing = "0.1.40" chrono = "0.4" -validator = { version = "0.16" } +validator = { version = "0.18" } sea-orm = { version = "1.0.0", features = [ "sqlx-sqlite", "sqlx-postgres", @@ -51,5 +51,5 @@ required-features = [] [dev-dependencies] serial_test = "3.1.1" rstest = "0.21.0" -loco-rs = { version = "0.9.0", features = ["testing"] } +loco-rs = { version = "0.10.1", features = ["testing"] } insta = { version = "1.34.0", features = ["redactions", "yaml", "filters"] } diff --git a/starters/saas/config/development.yaml b/starters/saas/config/development.yaml index 790655fb7..c20c113f9 100644 --- a/starters/saas/config/development.yaml +++ b/starters/saas/config/development.yaml @@ -22,69 +22,6 @@ server: host: http://localhost # Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block middlewares: - # Fallback file for not found (404) routes - # disable this for production or use your own file - fallback: - enable: true - # use a file if you want a different 404 page - # file: path/to/file.html - - # Enable Etag cache header middleware - etag: - enable: true - # Allows to limit the payload size request. payload that bigger than this file will blocked the request. - limit_payload: - # Enable/Disable the middleware. - enable: true - # the limit size. can be b,kb,kib,mb,mib,gb,gib - body_limit: 5mb - # set secure headers - secure_headers: - preset: github - overrides: - # this allows you to use HTMX, and has unsafe-inline. Remove or consider in production - "Content-Security-Policy": "default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src 'unsafe-inline' 'self' https:; style-src 'self' https: 'unsafe-inline'" - # calculate remote IP based on `X-Forwarded-For` when behind a proxy or load balancer - # use RemoteIP(..) extractor to get the remote IP. - # without this middleware, you'll get the proxy IP instead. - # For more: https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/middleware/remote_ip.rb - # - # NOTE! only enable when under a proxy, otherwise this can lead to IP spoofing vulnerabilities - # trust me, you'll know if you need this middleware. - remote_ip: - enable: false - # # replace the default trusted proxies: - # trusted_proxies: - # - ip range 1 - # - ip range 2 .. - # Generating a unique request ID and enhancing logging with additional information such as the start and completion of request processing, latency, status code, and other request details. - logger: - # Enable/Disable the middleware. - enable: true - # when your code is panicked, the request still returns 500 status code. - catch_panic: - # Enable/Disable the middleware. - enable: true - # Timeout for incoming requests middleware. requests that take more time from the configuration will cute and 408 status code will returned. - timeout_request: - # Enable/Disable the middleware. - enable: false - # Duration time in milliseconds. - timeout: 5000 - cors: - enable: true - # Set the value of the [`Access-Control-Allow-Origin`][mdn] header - # allow_origins: - # - https://loco.rs - # Set the value of the [`Access-Control-Allow-Headers`][mdn] header - # allow_headers: - # - Content-Type - # Set the value of the [`Access-Control-Allow-Methods`][mdn] header - # allow_methods: - # - POST - # Set the value of the [`Access-Control-Max-Age`][mdn] header in seconds - # max_age: 3600 - # ############################################# # Full stack SaaS asset serving # ############################################# @@ -187,6 +124,7 @@ database: # Queue Configuration queue: + kind: Redis # Redis connection URI uri: {{ get_env(name="REDIS_URL", default="redis://127.0.0.1") }} # Dangerously flush all data in Redis on startup. dangerous operation, make sure that you using this flag only on dev environments or test mode diff --git a/starters/saas/config/test.yaml b/starters/saas/config/test.yaml index 7fd03ad0e..f872bff2b 100644 --- a/starters/saas/config/test.yaml +++ b/starters/saas/config/test.yaml @@ -20,41 +20,9 @@ server: host: http://localhost # Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block middlewares: - # Allows to limit the payload size request. payload that bigger than this file will blocked the request. - limit_payload: - # Enable/Disable the middleware. - enable: true - # the limit size. can be b,kb,kib,mb,mib,gb,gib - body_limit: 5mb - # Generating a unique request ID and enhancing logging with additional information such as the start and completion of request processing, latency, status code, and other request details. - logger: - # Enable/Disable the middleware. - enable: true - # when your code is panicked, the request still returns 500 status code. - catch_panic: - # Enable/Disable the middleware. - enable: true - # Timeout for incoming requests middleware. requests that take more time from the configuration will cute and 408 status code will returned. - timeout_request: - # Enable/Disable the middleware. - enable: false - # Duration time in milliseconds. - timeout: 5000 - cors: - enable: true - # Set the value of the [`Access-Control-Allow-Origin`][mdn] header - # allow_origins: - # - https://loco.rs - # Set the value of the [`Access-Control-Allow-Headers`][mdn] header - # allow_headers: - # - Content-Type - # Set the value of the [`Access-Control-Allow-Methods`][mdn] header - # allow_methods: - # - POST - # Set the value of the [`Access-Control-Max-Age`][mdn] header in seconds - # max_age: 3600 static: enable: true + precompressed: false must_exist: false folder: uri: "/" @@ -116,6 +84,7 @@ database: # Queue Configuration queue: + kind: Redis # Redis connection URI uri: {{get_env(name="REDIS_URL", default="redis://127.0.0.1")}} # Dangerously flush all data in Redis on startup. dangerous operation, make sure that you using this flag only on dev environments or test mode diff --git a/starters/saas/migration/Cargo.toml b/starters/saas/migration/Cargo.toml index 723b73ccf..bf79cfe14 100644 --- a/starters/saas/migration/Cargo.toml +++ b/starters/saas/migration/Cargo.toml @@ -10,7 +10,7 @@ path = "src/lib.rs" [dependencies] async-std = { version = "1", features = ["attributes", "tokio1"] } -loco-rs = { version = "0.9.0" } +loco-rs = { version = "0.10.1" } [dependencies.sea-orm-migration] version = "1.0.0" diff --git a/starters/saas/src/app.rs b/starters/saas/src/app.rs index 07d7300ae..62d3c9466 100644 --- a/starters/saas/src/app.rs +++ b/starters/saas/src/app.rs @@ -3,12 +3,12 @@ use std::path::Path; use async_trait::async_trait; use loco_rs::{ app::{AppContext, Hooks, Initializer}, + bgworker::{BackgroundWorker, Queue}, boot::{create_app, BootResult, StartMode}, controller::AppRoutes, db::{self, truncate_table}, environment::Environment, task::Tasks, - worker::{AppWorker, Processor}, Result, }; use migration::Migrator; @@ -55,8 +55,9 @@ impl Hooks for App { .add_route(controllers::user::routes()) } - fn connect_workers<'a>(p: &'a mut Processor, ctx: &'a AppContext) { - p.register(DownloadWorker::build(ctx)); + async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()> { + queue.register(DownloadWorker::build(ctx)).await?; + Ok(()) } fn register_tasks(tasks: &mut Tasks) { diff --git a/starters/saas/src/models/users.rs b/starters/saas/src/models/users.rs index 510802e0b..4189157e9 100644 --- a/starters/saas/src/models/users.rs +++ b/starters/saas/src/models/users.rs @@ -23,7 +23,7 @@ pub struct RegisterParams { pub struct Validator { #[validate(length(min = 2, message = "Name must be at least 2 characters long."))] pub name: String, - #[validate(custom = "validation::is_valid_email")] + #[validate(custom(function = "validation::is_valid_email"))] pub email: String, } diff --git a/starters/saas/src/workers/downloader.rs b/starters/saas/src/workers/downloader.rs index 42c0bd7a4..fa094d5b4 100644 --- a/starters/saas/src/workers/downloader.rs +++ b/starters/saas/src/workers/downloader.rs @@ -15,15 +15,12 @@ pub struct DownloadWorkerArgs { pub user_guid: String, } -impl worker::AppWorker for DownloadWorker { +#[async_trait] +impl BackgroundWorker for DownloadWorker { fn build(ctx: &AppContext) -> Self { Self { ctx: ctx.clone() } } -} - -#[async_trait] -impl worker::Worker for DownloadWorker { - async fn perform(&self, args: DownloadWorkerArgs) -> worker::Result<()> { + async fn perform(&self, args: DownloadWorkerArgs) -> Result<()> { // TODO: Some actual work goes here... println!("================================================"); println!("Sending payment report to user {}", args.user_guid); diff --git a/tests/controller/middlewares.rs b/tests/controller/middlewares.rs index 81553913e..e68ded28a 100644 --- a/tests/controller/middlewares.rs +++ b/tests/controller/middlewares.rs @@ -1,10 +1,12 @@ -use crate::infra_cfg; +use std::{collections::BTreeMap, path::PathBuf}; + use axum::http::StatusCode; use insta::assert_debug_snapshot; use loco_rs::{controller::middleware, prelude::*, tests_cfg}; use rstest::rstest; use serial_test::serial; -use std::{collections::BTreeMap, path::PathBuf}; + +use crate::infra_cfg; macro_rules! configure_insta { ($($expr:expr),*) => { @@ -29,7 +31,8 @@ async fn panic(#[case] enable: bool) { } let mut ctx: AppContext = tests_cfg::app::get_app_context().await; - ctx.config.server.middlewares.catch_panic = middleware::catch_panic::CatchPanic { enable }; + ctx.config.server.middlewares.catch_panic = + Some(middleware::catch_panic::CatchPanic { enable }); let handle = infra_cfg::server::start_with_route(ctx, "/", get(action)).await; let res = reqwest::get(infra_cfg::server::get_base_url()).await; @@ -59,7 +62,7 @@ async fn etag(#[case] enable: bool) { let mut ctx: AppContext = tests_cfg::app::get_app_context().await; - ctx.config.server.middlewares.etag = middleware::etag::Etag { enable }; + ctx.config.server.middlewares.etag = Some(middleware::etag::Etag { enable }); let handle = infra_cfg::server::start_with_route(ctx, "/", get(action)).await; @@ -92,10 +95,10 @@ async fn remote_ip(#[case] enable: bool, #[case] expected: &str) { let mut ctx: AppContext = tests_cfg::app::get_app_context().await; - ctx.config.server.middlewares.remote_ip = middleware::remote_ip::RemoteIpMiddleware { + ctx.config.server.middlewares.remote_ip = Some(middleware::remote_ip::RemoteIpMiddleware { enable, trusted_proxies: Some(vec!["192.1.1.1/8".to_string()]), - }; + }); let handle = infra_cfg::server::start_with_route(ctx, "/", get(action)).await; @@ -129,7 +132,7 @@ async fn timeout(#[case] enable: bool) { let mut ctx: AppContext = tests_cfg::app::get_app_context().await; ctx.config.server.middlewares.timeout_request = - middleware::timeout::TimeOut { enable, timeout: 2 }; + Some(middleware::timeout::TimeOut { enable, timeout: 2 }); let handle = infra_cfg::server::start_with_route(ctx, "/", get(action)).await; @@ -161,14 +164,15 @@ async fn cors( #[case] allow_methods: Option>, #[case] max_age: Option, ) { + use loco_rs::controller::middleware::cors::Cors; + configure_insta!(); let mut ctx: AppContext = tests_cfg::app::get_app_context().await; - let mut middleware = loco_rs::controller::middleware::cors::Cors { - enable, - ..Default::default() - }; + let mut middleware = Cors::empty(); + middleware.enable = enable; + if let Some(allow_headers) = allow_headers { middleware.allow_headers = allow_headers; } @@ -177,7 +181,7 @@ async fn cors( } middleware.max_age = max_age; - ctx.config.server.middlewares.cors = middleware; + ctx.config.server.middlewares.cors = Some(middleware); let handle = infra_cfg::server::start_from_ctx(ctx).await; @@ -220,10 +224,10 @@ async fn limit_payload(#[case] enable: bool) { let mut ctx: AppContext = tests_cfg::app::get_app_context().await; - ctx.config.server.middlewares.limit_payload = middleware::limit_payload::LimitPayload { + ctx.config.server.middlewares.limit_payload = Some(middleware::limit_payload::LimitPayload { enable, body_limit: 0x1B, - }; + }); let handle = infra_cfg::server::start_from_ctx(ctx).await; @@ -263,7 +267,7 @@ async fn static_assets() { let mut ctx: AppContext = tests_cfg::app::get_app_context().await; let base_static_path = static_asset_path.join(base_static_assets_path); - ctx.config.server.middlewares.static_assets = middleware::static_assets::StaticAssets { + ctx.config.server.middlewares.static_assets = Some(middleware::static_assets::StaticAssets { enable: true, must_exist: true, folder: middleware::static_assets::FolderConfig { @@ -272,7 +276,7 @@ async fn static_assets() { }, fallback: base_static_path.join("404.html").display().to_string(), precompressed: false, - }; + }); let handle = infra_cfg::server::start_from_ctx(ctx).await; @@ -314,12 +318,13 @@ async fn secure_headers( let mut ctx: AppContext = tests_cfg::app::get_app_context().await; - ctx.config.server.middlewares.secure_headers = + ctx.config.server.middlewares.secure_headers = Some( loco_rs::controller::middleware::secure_headers::SecureHeader { enable: true, - preset: preset.clone(), + preset: preset.clone().unwrap_or_else(|| "github".to_string()), overrides: overrides.clone(), - }; + }, + ); let handle = infra_cfg::server::start_from_ctx(ctx).await; @@ -388,7 +393,7 @@ async fn fallback( fallback_config.code = code; }; - ctx.config.server.middlewares.fallback = fallback_config; + ctx.config.server.middlewares.fallback = Some(fallback_config); let handle = infra_cfg::server::start_from_ctx(ctx).await; diff --git a/tests/controller/snapshots/secure_headers_[none]_overrides[none]@middlewares.snap b/tests/controller/snapshots/secure_headers_[none]_overrides[none]@middlewares.snap index 13d3d8a2c..7a5a84027 100644 --- a/tests/controller/snapshots/secure_headers_[none]_overrides[none]@middlewares.snap +++ b/tests/controller/snapshots/secure_headers_[none]_overrides[none]@middlewares.snap @@ -2,4 +2,6 @@ source: tests/controller/middlewares.rs expression: policy --- -None +Some( + "default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'", +)