From 3b2b102a869b81db3f25063f68b0b171c38e0313 Mon Sep 17 00:00:00 2001 From: jonaro00 <54029719+jonaro00@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:27:57 +0100 Subject: [PATCH 1/3] update examples for .dev (#303) * examples fixes * fix: Shuttle.toml assets --- examples/actix-cookie-authentication.mdx | 17 ++++++++--------- examples/actix-postgres.mdx | 5 ++--- examples/actix-static-files.mdx | 14 ++++++++------ examples/actix-websocket-actorless.mdx | 21 +++++++++++---------- examples/actix.mdx | 2 +- examples/axum-jwt-authentication.mdx | 15 ++++++--------- examples/axum-postgres.mdx | 5 ++--- examples/axum-static-files.mdx | 12 +++++++----- examples/axum-websockets.mdx | 19 +++++++++++-------- examples/axum.mdx | 4 ++-- examples/rocket-jwt-authentication.mdx | 13 ++++++------- examples/rocket-postgres.mdx | 5 ++--- examples/rocket-static-files.mdx | 10 ++++++---- examples/rocket.mdx | 2 +- integrations/custom-resources.mdx | 5 ++--- templates/tutorials/url-shortener.mdx | 2 +- 16 files changed, 76 insertions(+), 75 deletions(-) diff --git a/examples/actix-cookie-authentication.mdx b/examples/actix-cookie-authentication.mdx index 838bd9f..50bfefa 100644 --- a/examples/actix-cookie-authentication.mdx +++ b/examples/actix-cookie-authentication.mdx @@ -8,10 +8,9 @@ description: "Explore how you can secure your Actix Web application by using coo This example shows how to use authentication within actix-web with cookies, assisted by actix-identity and actix-session. The idea is that all requests authenticate first at the login route to get a cookie, then the cookie is sent with all requests requiring authentication using the HTTP cookie header. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash -shuttle init --from shuttle-hq/shuttle-examples \ - --subfolder actix-web/cookie-authentication +shuttle init --from shuttle-hq/shuttle-examples --subfolder actix-web/cookie-authentication ``` Three Actix Web routes are registered in this file: @@ -33,8 +32,8 @@ edition = "2021" actix-identity = "0.6.0" actix-session = { version = "0.8.0", features = ["cookie-session"] } actix-web = "4.3.1" -shuttle-actix-web = "0.27.0" -shuttle-runtime = "0.27.0" +shuttle-actix-web = "0.48.0" +shuttle-runtime = "0.48.0" tokio = "1.26.0" ``` Your `main.rs` should look like this: @@ -127,25 +126,25 @@ Once you've cloned this example, launch it locally by using `shuttle run`. Once First, we should be able to access the public endpoint without any authentication using: ```sh -$ curl http://localhost:8000/public +curl http://localhost:8000/public ``` But trying to access the private endpoint will return "Hello anonymous": ```sh -$ curl http://localhost:8000/private +curl http://localhost:8000/private ``` So let's get a cookie from the login route first: ```sh -$ curl http://localhost:8000/login +curl http://localhost:8000/login ``` Accessing the private endpoint with the token will now succeed: ```sh -$ curl --header "Authorization: Bearer " http://localhost:8000/private +curl --header "Authorization: Bearer " http://localhost:8000/private ``` The token is set to expire in 5 minutes, so wait a while and try to access the private endpoint again. Once the token has expired, a user will need to get a new token from login. diff --git a/examples/actix-postgres.mdx b/examples/actix-postgres.mdx index ae5f4e8..02ce9be 100644 --- a/examples/actix-postgres.mdx +++ b/examples/actix-postgres.mdx @@ -10,10 +10,9 @@ The following routes are provided: - GET `/todos/` - Get a to-do item by ID. - POST `/todos` - Create a to-do item. Takes "note" as a JSON body parameter. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash -shuttle init --from shuttle-hq/shuttle-examples \ - --subfolder actix-web/postgres +shuttle init --from shuttle-hq/shuttle-examples --subfolder actix-web/postgres ``` ## Code diff --git a/examples/actix-static-files.mdx b/examples/actix-static-files.mdx index 7b71db7..2b473d2 100644 --- a/examples/actix-static-files.mdx +++ b/examples/actix-static-files.mdx @@ -9,10 +9,9 @@ This example has one route at `/` where the homepage is served and shows you how Note that static assets are declared in the `Shuttle.toml` file. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash -shuttle init --from shuttle-hq/shuttle-examples \ - --subfolder actix-web/static-files +shuttle init --from shuttle-hq/shuttle-examples --subfolder actix-web/static-files ``` ## Code @@ -59,13 +58,16 @@ edition = "2021" [dependencies] actix-web = "4.3.1" actix-files = "0.6.2" -shuttle-actix-web = "0.27.0" -shuttle-runtime = "0.27.0" +shuttle-actix-web = "0.48.0" +shuttle-runtime = "0.48.0" tokio = "1.26.0" ``` ```toml Shuttle.toml -assets = ["assets/*"] +[build] +assets = [ + "assets/*", +] ``` diff --git a/examples/actix-websocket-actorless.mdx b/examples/actix-websocket-actorless.mdx index 2c6c6f8..6c44a62 100644 --- a/examples/actix-websocket-actorless.mdx +++ b/examples/actix-websocket-actorless.mdx @@ -7,10 +7,9 @@ description: "Learn how websockets can upgrade your web service by providing liv This example shows how to use a WebSocket to show the live status of the Shuttle API on a web page. The app also provides an echo service and notifies when the number of connected users change. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash -shuttle init --from shuttle-hq/shuttle-examples \ - --subfolder actix-web/websocket-actorless +shuttle init --from shuttle-hq/shuttle-examples --subfolder actix-web/websocket-actorless ``` ## Code @@ -34,7 +33,7 @@ use std::{ use tokio::sync::{mpsc, watch}; const PAUSE_SECS: u64 = 15; -const STATUS_URI: &str = "https://api.shuttle.dev"; +const STATUS_URI: &str = "https://api.shuttle.dev/.healthz"; type AppState = ( mpsc::UnboundedSender, @@ -120,12 +119,11 @@ async fn websocket( async fn index() -> impl Responder { NamedFile::open_async("./static/index.html") .await - .map_err(|e| actix_web::error::ErrorInternalServerError(e)) + .map_err(actix_web::error::ErrorInternalServerError) } #[shuttle_runtime::main] -async fn actix_web( -) -> ShuttleActixWeb { +async fn main() -> ShuttleActixWeb { // We're going to use channels to communicate between threads. // api state channel let (tx_api_state, rx_api_state) = watch::channel(ApiStateMessage::default()); @@ -187,7 +185,7 @@ async fn actix_web( if let Err(e) = shared_tx_api_state2.send(ApiStateMessage { client_count, - origin: format!("ws_update"), + origin: "ws_update".to_string(), date_time: Utc::now(), is_up, }) { @@ -211,7 +209,7 @@ async fn actix_web( async fn get_api_status(client: &reqwest::Client) -> bool { let response = client.get(STATUS_URI).send().await; - response.is_ok() + response.is_ok_and(|r| r.status().is_success()) } ``` @@ -414,7 +412,10 @@ tracing = "0.1" ``` ```toml Shuttle.toml -assets = ["static/*"] +[build] +assets = [ + "static/*", +] ``` diff --git a/examples/actix.mdx b/examples/actix.mdx index 81eca69..fce7bfc 100644 --- a/examples/actix.mdx +++ b/examples/actix.mdx @@ -11,7 +11,7 @@ This section revolves around simple Actix examples you can get quickly started w If you are looking for step-by-step guides, check out our [Tutorials](/templates/tutorials) section. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash shuttle init --template actix-web ``` diff --git a/examples/axum-jwt-authentication.mdx b/examples/axum-jwt-authentication.mdx index 2986ce6..07a73fc 100644 --- a/examples/axum-jwt-authentication.mdx +++ b/examples/axum-jwt-authentication.mdx @@ -16,11 +16,10 @@ Three Axum routes are registered in this file: - `/login`: a route for posting a JSON object with a username and password to get a JWT. - `/private`: a route that can only be accessed with a valid JWT. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash -shuttle init --from shuttle-hq/shuttle-examples \ - --subfolder axum/jwt-authentication +shuttle init --from shuttle-hq/shuttle-examples --subfolder axum/jwt-authentication ``` ## Code @@ -235,28 +234,26 @@ Once you've cloned this example, launch it locally by using `shuttle run`. Once First, we should be able to access the public endpoint without any authentication using: ```sh -$ curl http://localhost:8000/public +curl http://localhost:8000/public ``` But trying to access the private endpoint will fail with a 403 forbidden: ```sh -$ curl http://localhost:8000/private +curl http://localhost:8000/private ``` So let's get a JWT from the login route first: ```sh -$ curl --header "Content-Type: application/json" --request POST \ - --data '{"client_id": "foo", "client_secret": "bar"}' \ - http://localhost:8000/login +curl -X POST --header "Content-Type: application/json" --data '{"client_id": "foo", "client_secret": "bar"}' http://localhost:8000/login ``` Accessing the private endpoint with the token will now succeed: ```sh -$ curl --header "Authorization: Bearer " http://localhost:8000/private +curl --header "Authorization: Bearer " http://localhost:8000/private ``` The token is set to expire in 5 minutes, so wait a while and try to access the private endpoint again. Once the token has expired, a user will need to get a new token from login. diff --git a/examples/axum-postgres.mdx b/examples/axum-postgres.mdx index 5fb69fd..31e9e64 100644 --- a/examples/axum-postgres.mdx +++ b/examples/axum-postgres.mdx @@ -10,11 +10,10 @@ The following routes are provided: - GET `/todos/` - Get a to-do item by ID. - POST `/todos` - Create a to-do item. Takes "note" as a JSON body parameter. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash -shuttle init --from shuttle-hq/shuttle-examples \ - --subfolder axum/postgres +shuttle init --from shuttle-hq/shuttle-examples --subfolder axum/postgres ``` ## Code diff --git a/examples/axum-static-files.mdx b/examples/axum-static-files.mdx index d39f634..3389643 100644 --- a/examples/axum-static-files.mdx +++ b/examples/axum-static-files.mdx @@ -7,13 +7,12 @@ description: "This article walks you through setting up static files with Axum, This example has one route at `/` where the homepage is served and shows you how you can serve HTML or other types of files with Actix Web. -Note that static assets are declared in the `Shuttle.toml` file. +Note that build assets are declared in `Shuttle.toml`. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash -shuttle init --from shuttle-hq/shuttle-examples \ - --subfolder axum/static-files +shuttle init --from shuttle-hq/shuttle-examples --subfolder axum/static-files ``` ## Code @@ -65,7 +64,10 @@ tower-http = { version = "0.5.0", features = ["fs"] } ``` ```toml Shuttle.toml -assets = ["assets/*"] +[build] +assets = [ + "assets", +] ``` diff --git a/examples/axum-websockets.mdx b/examples/axum-websockets.mdx index 3980067..30b2dc8 100644 --- a/examples/axum-websockets.mdx +++ b/examples/axum-websockets.mdx @@ -11,11 +11,10 @@ There are a few routes available: - `/` - the homepage route where you can find the `index.html` page. - `/ws` - the route that handles websockets. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash -shuttle init --from shuttle-hq/shuttle-examples \ - --subfolder axum/websocket +shuttle init --from shuttle-hq/shuttle-examples --subfolder axum/websocket ``` ## Code @@ -49,7 +48,7 @@ struct State { } const PAUSE_SECS: u64 = 15; -const STATUS_URI: &str = "https://api.shuttle.dev"; +const STATUS_URI: &str = "https://api.shuttle.dev/.healthz"; #[derive(Serialize)] struct Response { @@ -60,7 +59,7 @@ struct Response { } #[shuttle_runtime::main] -async fn axum() -> ShuttleAxum { +async fn main() -> ShuttleAxum { let (tx, rx) = watch::channel(Message::Text("{}".to_string())); let state = Arc::new(Mutex::new(State { @@ -74,8 +73,9 @@ async fn axum() -> ShuttleAxum { let duration = Duration::from_secs(PAUSE_SECS); loop { - let is_up = reqwest::get(STATUS_URI).await; - let is_up = is_up.is_ok(); + let is_up = reqwest::get(STATUS_URI) + .await + .is_ok_and(|r| r.status().is_success()); let response = Response { clients_count: state_send.lock().await.clients_count, @@ -273,7 +273,10 @@ tower-http = { version = "0.5.0", features = ["fs"] } ``` ```toml Shuttle.toml -assets = ["static/*"] +[build] +assets = [ + "static", +] ``` diff --git a/examples/axum.mdx b/examples/axum.mdx index 7280744..ba1b860 100644 --- a/examples/axum.mdx +++ b/examples/axum.mdx @@ -12,7 +12,7 @@ This section revolves around simple Axum examples you can get quickly started wi If you are looking for step-by-step guides, check out our [Tutorials](/templates/tutorials) section. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash shuttle init --template axum @@ -42,7 +42,7 @@ version = "0.1.0" edition = "2021" [dependencies] -axum = "0.7.3" +axum = "0.7.4" shuttle-axum = "0.48.0" shuttle-runtime = "0.48.0" tokio = "1.28.2" diff --git a/examples/rocket-jwt-authentication.mdx b/examples/rocket-jwt-authentication.mdx index 5770dcc..977a74e 100644 --- a/examples/rocket-jwt-authentication.mdx +++ b/examples/rocket-jwt-authentication.mdx @@ -12,10 +12,9 @@ Then the JWT is sent with all requests requiring authentication using the HTTP h This example uses the [`jsonwebtoken`](https://github.com/Keats/jsonwebtoken) which supports symmetric and asymmetric secret encoding, built-in validations, and most JWT algorithms. However, this example only makes use of symmetric encoding and validation on the expiration claim. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash -shuttle init --from shuttle-hq/shuttle-examples \ - --subfolder rocket/jwt-authentication +shuttle init --from shuttle-hq/shuttle-examples --subfolder rocket/jwt-authentication ``` Three Rocket routes are registered in this file: @@ -275,26 +274,26 @@ Once you've cloned this example, launch it locally by using `shuttle run`. Once First, we should be able to access the public endpoint without any authentication using: ```sh -$ curl https:///public +curl https:///public ``` But trying to access the private endpoint will fail with a 403 forbidden: ```sh -$ curl https:///private +curl https:///private ``` So let's get a JWT from the login route first: ```sh -$ curl --request POST --data '{"username": "username", "password": "password"}' https:///login +curl -X POST --data '{"username": "username", "password": "password"}' https:///login ``` Accessing the private endpoint with the token will now succeed: ```sh -$ curl --header "Authorization: Bearer " https:///private +curl --header "Authorization: Bearer " https:///private ``` The token is set to expire in 5 minutes, so wait a while and try to access the private endpoint again. Once the token has expired, a user will need to get a new token from login. diff --git a/examples/rocket-postgres.mdx b/examples/rocket-postgres.mdx index 6b7311e..40224ee 100644 --- a/examples/rocket-postgres.mdx +++ b/examples/rocket-postgres.mdx @@ -11,10 +11,9 @@ The following routes are provided: - GET `/todo/` - Get a to-do item by ID. - POST `/todo` - Create a to-do item. Takes "note" as a JSON body parameter. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash -shuttle init --from shuttle-hq/shuttle-examples \ - --subfolder rocket/postgres +shuttle init --from shuttle-hq/shuttle-examples --subfolder rocket/postgres ``` ## Code diff --git a/examples/rocket-static-files.mdx b/examples/rocket-static-files.mdx index 7d7b5f7..5b727d8 100644 --- a/examples/rocket-static-files.mdx +++ b/examples/rocket-static-files.mdx @@ -9,10 +9,9 @@ This example has one route at `/` where the homepage is served and shows you how Note that static assets are declared in the `Shuttle.toml` file. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash -shuttle init --from shuttle-hq/shuttle-examples \ - --subfolder rocket/static-files +shuttle init --from shuttle-hq/shuttle-examples --subfolder rocket/static-files ``` ## Code @@ -82,7 +81,10 @@ tokio = "1.26.0" ``` ```toml Shuttle.toml -assets = ["assets/*"] +[build] +assets = [ + "assets", +] ``` diff --git a/examples/rocket.mdx b/examples/rocket.mdx index f869cbb..7e33d17 100644 --- a/examples/rocket.mdx +++ b/examples/rocket.mdx @@ -12,7 +12,7 @@ This section revolves around simple Rocket examples you can get quickly started If you are looking for step-by-step guides, check out our [Tutorials](/templates/tutorials) section. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash shuttle init --template rocket ``` diff --git a/integrations/custom-resources.mdx b/integrations/custom-resources.mdx index 21748a9..91f675c 100644 --- a/integrations/custom-resources.mdx +++ b/integrations/custom-resources.mdx @@ -9,11 +9,10 @@ The example resource we'll be making will be a Plain Data Object (which we will We are using the Axum framework in `main.rs` so we can showcase the resource in action, but the implementation is entirely separate from what web framework you use so you can add your custom resource to any Shuttle-hosted resource. -You can clone the example below by running the following (you'll need `cargo-shuttle` installed): +You can clone the example below by running the following (you'll need `shuttle` CLI installed): ```bash -shuttle init --from https://github.com/shuttle-hq/shuttle-examples \ - --subfolder custom-resource/pdo +shuttle init --from https://github.com/shuttle-hq/shuttle-examples --subfolder custom-resource/pdo ``` diff --git a/templates/tutorials/url-shortener.mdx b/templates/tutorials/url-shortener.mdx index f764909..db65b78 100644 --- a/templates/tutorials/url-shortener.mdx +++ b/templates/tutorials/url-shortener.mdx @@ -314,7 +314,7 @@ we're live. As soon as the `DEPLOYED` dialog came up, I instantly tested it out: ```bash $ curl -X POST -d "https://google.com" https://url-shortener.shuttle.app -https://s.shuttle.app/XDlrTB⏎ +https://s.shuttle.app/XDlrTB ``` I then copy/pasted the shortened URL to my browser and, lo an behold, was From d3070d974cc0873cdee55d4c0852083216945645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Guinzburg?= Date: Wed, 30 Oct 2024 13:53:33 +0100 Subject: [PATCH 2/3] docs: local-run.mdx still suggests running shuttle as cargo command instead of shell command (#304) * Update local-run.mdx Local run docs still use `cargo watch -x 'shuttle run'`, which translates to `cargo shuttle run`; instead of using `cargo watch-s 'shuttle run'`, which translates to `shuttle run`. * nit --------- Co-authored-by: jonaro00 <54029719+jonaro00@users.noreply.github.com> --- docs/local-run.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/local-run.mdx b/docs/local-run.mdx index 5a8fd52..359f8c1 100644 --- a/docs/local-run.mdx +++ b/docs/local-run.mdx @@ -62,10 +62,10 @@ Here are some small tips and tricks to boost your productivity when developing l To live reload your Shuttle app when you save a file, you can use [cargo-watch](https://github.com/watchexec/cargo-watch): ```bash -# This will e(x)ecute `shuttle run` when you save a file. -cargo watch -x 'shuttle run' +# This will execute `shuttle run` when you save a file. +cargo watch -s 'shuttle run' # This will also (q)uietly (c)lear the console between runs. -cargo watch -qcx 'shuttle run' +cargo watch -qcs 'shuttle run' # There are many other helpful options, see `cargo watch --help` ``` From 8b079d3272d3805ad5d275c6aa78a9bce2442b4c Mon Sep 17 00:00:00 2001 From: Joshua Mo <102877324+joshua-mo-143@users.noreply.github.com> Date: Fri, 8 Nov 2024 15:02:25 +0000 Subject: [PATCH 3/3] feat: Add updated migration docs (#305) * feat: add updated migration docs * fix: broken link * fix: broken links * refactor: seperate platform migration, add migrations intro * fix: broken links --- introduction/docs.mdx | 4 +- migrations/frameworks/actix-web.mdx | 60 ++++++++++ migrations/frameworks/axum.mdx | 58 ++++++++++ migrations/frameworks/custom-service.mdx | 105 ++++++++++++++++++ migrations/frameworks/rocket.mdx | 49 ++++++++ migrations/introduction.mdx | 54 +++++++++ migrations/migrating-checklist.mdx | 32 ++++++ .../migrating-to-shuttle.mdx | 4 +- migrations/resources/postgres.mdx | 78 +++++++++++++ migrations/resources/secrets.mdx | 69 ++++++++++++ mint.json | 71 +++++++----- .../migration.mdx | 10 +- .../platform-update.mdx | 3 +- 13 files changed, 561 insertions(+), 36 deletions(-) create mode 100644 migrations/frameworks/actix-web.mdx create mode 100644 migrations/frameworks/axum.mdx create mode 100644 migrations/frameworks/custom-service.mdx create mode 100644 migrations/frameworks/rocket.mdx create mode 100644 migrations/introduction.mdx create mode 100644 migrations/migrating-checklist.mdx rename {guides => migrations}/migrating-to-shuttle.mdx (98%) create mode 100644 migrations/resources/postgres.mdx create mode 100644 migrations/resources/secrets.mdx rename introduction/platform-migration.mdx => platform-update/migration.mdx (90%) rename {introduction => platform-update}/platform-update.mdx (99%) diff --git a/introduction/docs.mdx b/introduction/docs.mdx index d4f35a7..8b0bc2e 100644 --- a/introduction/docs.mdx +++ b/introduction/docs.mdx @@ -4,7 +4,7 @@ description: "Begin your Shuttle journey with our comprehensive documentation. L icon: "hand-wave" --- - + Shuttle is launching the new and improved version of our platform. Click to read more! @@ -54,7 +54,7 @@ icon: "hand-wave" Migrate an existing project to Shuttle diff --git a/migrations/frameworks/actix-web.mdx b/migrations/frameworks/actix-web.mdx new file mode 100644 index 0000000..aac06c5 --- /dev/null +++ b/migrations/frameworks/actix-web.mdx @@ -0,0 +1,60 @@ +--- +title: "Actix Web" +description: "How to migrate a web service that uses Actix Web to Shuttle" + +--- + +## Reference + +### Quickstart +Shuttle has a native integration for Actix Web by default with the `shuttle-actix-web` crate. + +See the following code comparison below: + + +```rust main.rs (before) +#[actix_web::main] // or #[tokio::main] +async fn main() -> std::io::Result<()> { + HttpServer::new(|| { + App::new() + }) + .bind(("127.0.0.1", 8080))? + .run() + .await +} +``` +```rust main.rs (after) +#[shuttle_runtime::main] +async fn actix_web() -> shuttle_actix_web::ShuttleActixWeb { + let config = move |cfg: &mut ServiceConfig| { + cfg.service(hello_world); + }; + + Ok(config.into()) +} +``` +```toml Cargo.toml (before) +[dependencies] +actix-web = 0.4 +tokio = 1 +``` +```toml Cargo.toml (after) +[dependencies] +actix-web = 0.4 +tokio = 1 + +shuttle-runtime = 0.48 +shuttle-actix-web = 0.48 +``` + + +## How does it work? +The Shuttle runtime exposes a trait, `shuttle_runtime::Service`, that services are required to implement before being able to run on Shuttle. + +The `shuttle-actix-web` crate exposes an aliased Result type (`shuttle_axum::ShuttleActixWeb`) which simply wraps your router and implements `From` which allows use of `.into()` on your `ServiceConfig`. This integration includes serving the router itself. + +Normally one would configure an application with Actix Web using the [App](https://docs.rs/actix-web/latest/actix_web/struct.App.html) struct. However, Shuttle needs to move the users configuration across threads to start the server on our backend, and the App struct is !Send and !Sync. + +That means that for Shuttle to support Actix Web, we need to use the [ServiceConfig](https://docs.rs/actix-web/latest/actix_web/web/struct.ServiceConfig.html) struct. You should be able to configure your application like you normally would, but some steps may be a bit different. If you do you find something that you would expect to be possible not working, please reach out and let us know. + +If you want more custom behavior that is not supported by the native Shuttle integration, you can create a struct with your own custom implementation of `shuttle_runtime::Service`. diff --git a/migrations/frameworks/axum.mdx b/migrations/frameworks/axum.mdx new file mode 100644 index 0000000..4fe78b5 --- /dev/null +++ b/migrations/frameworks/axum.mdx @@ -0,0 +1,58 @@ +--- +title: "Axum" +description: "How to migrate a web service that uses Axum to Shuttle" + +--- + +## Reference + +### Quickstart +Shuttle has a native integration for Axum by default with the `shuttle-axum` crate. + +See the following code comparison below: + + +```rust main.rs (before) +#[tokio::main] +async fn main() -> Result<(), Box> { + // whatever router you have here + let router = axum::Router::new(); + + let tcp_listener = tokio::net::TcpListener::bind("0.0.0.0:8000").await?; + + axum::serve(tcp_listener, router).await?; + + Ok(()) +} +``` +```rust main.rs (after) +#[shuttle_runtime::main] +async fn main() -> shuttle_axum::ShuttleAxum { + let router = axum::Router::new(); + + Ok(router.into()) +} +``` +```toml Cargo.toml (before) +[dependencies] +axum = 0.7.5 +tokio = 1 +``` +```toml Cargo.toml (after) +[dependencies] +axum = 0.7.5 +tokio = 1 + +shuttle-runtime = 0.48 +shuttle-axum = 0.48 +``` + + + + +## How does it work? +The Shuttle runtime exposes a trait, `shuttle_runtime::Service`, that services are required to implement before being able to run on Shuttle. + +The `shuttle-axum` crate exposes an aliased Result type (`shuttle_axum::ShuttleAxum`) which simply wraps your router and implements `From` which allows use of `.into()` on your Router. This integration includes serving the router itself. + +If you want more custom behavior that is not supported by the native Shuttle integration, you can create a struct with your own custom implementation of `shuttle_runtime::Service`. diff --git a/migrations/frameworks/custom-service.mdx b/migrations/frameworks/custom-service.mdx new file mode 100644 index 0000000..ac8d82d --- /dev/null +++ b/migrations/frameworks/custom-service.mdx @@ -0,0 +1,105 @@ +--- +title: "Custom" +description: "How to migrate a service to Shuttle without a native integration" + +--- + +## Reference + +### Quickstart + +To get started, you can use the following code below: + + +```rust data.rs +pub struct DataStruct; + +impl shuttle_runtime::Service for DataStruct { + async fn bind( + mut self, + addr: std::net::SocketAddr + ) -> Result<(), shuttle_runtime::Error> { + // whatever code you want goes here + Ok(()) + } +} +``` + +```rust main.rs +mod data; +use data::DataStruct; + +#[shuttle_runtime::main] +async fn main() -> Result { + Ok(DataStruct) +} +``` + + +Note that you can add fields to your struct for any annotation macros you'd like to add as extra data (for example: adding a Postgres connection or pool to your application). + +### Run a non-HTTP framework service +Note that the code that goes in the `bind()` function **must** eventually use a looping mechanism or be awaiting some kind of HTTP request, otherwise the function will finish and end the service. + + +```rust Looping +loop { + // your code goes here +} +``` +```rust Looping with time intervals +// Note that although you can use `sleep()` here, it is not as accurate +// as using tokio::time::interval because the interval will stick to a strict time +let mut interval = tokio::time::interval(std::Duration::from_millis(10)); + +loop { + interval.tick().await; +} +``` + + +### Run multiple tasks at once +If you want to run multiple services at once, it is relatively easy to do so. We store the variable without awaiting the future. +```rust data.rs +pub struct DataStruct; + +async fn loop() { + let mut interval = tokio::time::interval(std::Duration::from_secs(10)); + + loop { + println!("Hello world from the loop function!"); + interval.tick().await; + } +} + +impl shuttle_runtime::Service for DataStruct { + async fn bind( + mut self, + addr: std::net::SocketAddr + ) -> Result<(), shuttle_runtime::Error> { + let router = axum::Router::new(); + let tcp_listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); + + let serve = axum::serve(tcp_listener, router); + + tokio::join!( + serve, + loop() + ); + + Ok(()) + } +} +``` +The advantage of this over simply spawning a `tokio::Task` in a service with a Shuttle integration is primarily that both functions run concurrently on the same task. + +## How does it work? + +The Shuttle runtime exposes a trait `shuttle_runtime::Service` which you can implement on a struct you own to be able to augment the runtime behavior of your application. + +You may find this useful in the following cases: +- You need custom launch behavior for your web application +- You need to run multiple things at the same time (ie, a Discord bot and an Actix Web server) in a Tokio task +- You need to run a HTTP client but don't need a web server (ie, a cron job that uses `serenity` with only the HTTP feature enabled to send some requests to Discord) + +Note that only one HTTP service can be bound at a time - if you require more than one address per project, feel free to reach out and let us know! diff --git a/migrations/frameworks/rocket.mdx b/migrations/frameworks/rocket.mdx new file mode 100644 index 0000000..fba7885 --- /dev/null +++ b/migrations/frameworks/rocket.mdx @@ -0,0 +1,49 @@ +--- +title: "Rocket" +description: "How to migrate a web service that uses Rocket to Shuttle" +--- + +## Reference + +### Quickstart +Shuttle has a native integration for Rocket by default with the `shuttle-rocket` crate. + +See the following code comparison below: + + +```rust main.rs (before) +#[launch] +fn rocket() -> _ { + rocket::build() +} +``` +```rust main.rs (after) +#[shuttle_runtime::main] +async fn rocket() -> shuttle_rocket::ShuttleRocket { + let rocket = rocket::build(); + + Ok(rocket.into()) +} +``` +```toml Cargo.toml (before) +[dependencies] +rocket = 0.5 +tokio = 1 +``` +```toml Cargo.toml (after) +[dependencies] +rocket = 0.5 +tokio = 1 + +shuttle-runtime = 0.48 +shuttle-rocket = 0.48 +``` + + + +## How does it work? +The Shuttle runtime exposes a trait, `shuttle_runtime::Service`, that services are required to implement before being able to run on Shuttle. + +The `shuttle-rocket` crate exposes an aliased Result type (`shuttle_rocket::ShuttleRocket`) which simply wraps your router and implements `From` which allows use of `.into()` on your Router. This integration includes serving the router itself. + +If you want more custom behavior that is not supported by the native Shuttle integration, you can create a struct with your own custom implementation of `shuttle_runtime::Service`. You can find more about this [here](/migrations/frameworks/rocket) diff --git a/migrations/introduction.mdx b/migrations/introduction.mdx new file mode 100644 index 0000000..2183d21 --- /dev/null +++ b/migrations/introduction.mdx @@ -0,0 +1,54 @@ +--- +title: "How to migrate to Shuttle" +description: "Four simple steps to migrate to Shuttle" +--- + +## Introduction +If you are migrating from another platform to Shuttle (or simply want to add the Shuttle runtime to your project), here are four simple steps you can take to fully migrate your project over to Shuttle. + +Note that this page assumes you've already installed the `cargo-shuttle` CLI - if you haven't yet, you can learn how to do so [here.](/getting-started/installation) + +## How to Migrate +### 1. Check your service can be migrated +Before investing any time into migrating your project, you may find it helpful to have a checklist on hand for what can be migrated. Check out our [migration checklist](/migrations/migrating-checklist) for more details. +### 2. Add the Shuttle runtime +Adding the Shuttle runtime is as easy as adding `shuttle_runtime` to your project and the additional Shuttle framework integration if your project uses it, then using the given macro. + +To use, simply add the `#[shuttle_runtime::main]` macro on top of your main function. This macro abstracts over `#[tokio::main]` and can be used in exactly the same way - just replace it and add the type annotation from your integration crate: + +```rust main.rs +#[shuttle_runtime::main] +async fn main() -> shuttle_axum::ShuttleAxum { + // .. your code goes here +} +``` + +For more in depth explanations, check out out the migration documentation for your framework. + +For frameworks that don't currently have migration examples, check out our [examples](/examples) where we have examples for all supported frameworks. + +Note that if the framework you want to use isn't listed here or you want custom launch behavior, you will need to [create a custom service.](/migrations/frameworks/custom-service) + +### 3. Add your infrastructure annotations +Now we need to add our infrastructure. You can do this by passing in macros as function parameters into your new main function. An example has been provided below: +```rust main.rs +#[shuttle_runtime::main] +async fn main( + #[shuttle_shared_db::Postgres] conn_string: String, + #[shuttle_runtime::Secrets] secrets: SecretStore, +) -> shuttle_axum::ShuttleAxum { + // .. your code goes here +} +``` +In the above snippet, we have a main function where we get a database and secrets (that can be turned into environment variables), all from code (pulling in `shuttle_shared_db` with the `postgres` feature as a crate dependency). + +For more information, check out the following documentation: +- [Postgres](/migrations/resources/postgres) +- [Secrets](/migrations/resources/secrets) + +If there is a resource you want that we don't support, feel free to reach out and let us know [on Discord](https://discord.gg/shuttle) or [via our contact form](https://shuttle.dev/contact)! + +### 4. Deploy +Now we need to deploy. Simply use `shuttle deploy` (with `--allow-dirty` flag if on a dirty Git branch) and watch the magic happen! + +Once deployed, the deployment will output a URL link to where your newly deployed web service now lives. You will also receive the database connection string and list of secrets that your web service has access to - which you can view again with `shuttle resource list` should you need it. diff --git a/migrations/migrating-checklist.mdx b/migrations/migrating-checklist.mdx new file mode 100644 index 0000000..0c27223 --- /dev/null +++ b/migrations/migrating-checklist.mdx @@ -0,0 +1,32 @@ +--- +title: "Will my project run on Shuttle?" +description: "How to know if your project will migrate to Shuttle" + +--- + +## Types of services Shuttle supports +Shuttle currently supports any kind of service that carries out HTTP traffic. This includes but is not limited to: +- HTTP frameworks (Axum, Actix Web, Rocket...) +- Discord bots +- Telegram bots +- Web services that connect to RPC nodes + +We also additionally support hosted services that can carry out scheduled tasks (or cronjobs). + +If there is a service you'd like to run but there is no native Shuttle integration service, you can [create your own custom service](/migrations/frameworks/custom-service) to run it on Shuttle. + +## Supported resources +Natively speaking, we also support the following resources/infrastructure: +- Postgres +- Secrets (which you can set as environment variables during runtime) + +Note that resources will be provisioned **at runtime** - you will need to account for this when running on Shuttle. If your web service is spawned from a CLI command (or a method that does not allow spawning directly from `async fn main()`), you may want to consider adding a standalone binary for specifically spawning your web server. You can check out our example that shows how to do that [here.](https://github.com/shuttle-hq/shuttle-examples/tree/main/other/standalone-binary) + +We also additionally support the following resources (although not natively, so you will still need to provide your own credentials): +- [Turso](/integrations/shuttle-turso) +- [Qdrant](/integrations/shuttle-qdrant) +- [Apache OpenDAL](/integrations/shuttle-opendal) +- [OpenAI](/integrations/shuttle-openai) + +## My resource isn't listed! +If your listed resource isn't here, you can still connect to it normally through HTTP. If there is anything you'd like to see officially supported, reach out to us! diff --git a/guides/migrating-to-shuttle.mdx b/migrations/migrating-to-shuttle.mdx similarity index 98% rename from guides/migrating-to-shuttle.mdx rename to migrations/migrating-to-shuttle.mdx index f4d4a49..bcfdb82 100644 --- a/guides/migrating-to-shuttle.mdx +++ b/migrations/migrating-to-shuttle.mdx @@ -1,7 +1,7 @@ --- -title: "Migrating to Shuttle" +title: "Migration quickstart example" description: "Short guide on how to migrate your existing project over to Shuttle" -icon: "dove" + --- This example shows how to migrate an Axum app with a Postgres database. diff --git a/migrations/resources/postgres.mdx b/migrations/resources/postgres.mdx new file mode 100644 index 0000000..dcecb5e --- /dev/null +++ b/migrations/resources/postgres.mdx @@ -0,0 +1,78 @@ +--- +title: "Postgres" +description: "How to migrate a web service that uses Postgres to Shuttle" + +--- + +Shuttle has a native resource integration for Postgres by default with the `shuttle-shared-db` crate. + +## Reference + +### Quickstart +See the following code comparison below (using Axum and SQLx): + + +```rust main.rs (before) +#[tokio::main] +async fn main() -> Result<(), Box> { + let conn_string = std::env::var("DATABASE_URL") + .expect("DATABASE_URL env var to be present"); + + let pool = sqlx::PgPool::connect(&conn_string).await?; + // whatever router you have here + let router = axum::Router::new().with_state(pool); + + let tcp_listener = tokio::net::TcpListener::bind("0.0.0.0:8000").await?; + + axum::serve(tcp_listener, router).await?; + + Ok(()) +} +``` +```rust main.rs (after) +#[shuttle_runtime::main] +async fn main( + #[shuttle_shared_db::Postgres] conn_string: String, +) -> shuttle_axum::ShuttleAxum { + let pool = sqlx::PgPool::connect(&conn_string).await.unwrap(); + let router = axum::Router::new(); + + Ok(router.into()) +} +``` +```toml Cargo.toml (before) +[dependencies] +axum = 0.7.5 +tokio = 1 +sqlx = 0.8 +``` +```toml Cargo.toml (after) +[dependencies] +axum = 0.7.5 +tokio = 1 +sqlx = 0.8 + +shuttle-runtime = 0.48 +shuttle-axum = 0.48 +shuttle-shared-db = { version = 0.48, features = ["postgres"]} +``` + + +### Shared database + +You can find out more about shared databases [here.](/resources/shuttle-shared-db) + +By default, the resource annotation will output the connection string for your database. You can augment this by adding relevant feature flags: +- `diesel-async` (enables outputting `diesel_async::AsyncPgConnection` from the resource) +- `diesel-async-bb8` (enables outputting `diesel_bb8::Pool` from the resource) +- `diesel-async-deadpool`(enables outputting `diesel_deadpool::Pool` from the resource) +- `sqlx` (enables outputting `sqlx::PgPool` from the resource) + +Note that the relevant crates will need to be added manually (the relevant Diesel crate as noted in the type signatures, or SQLx if using it). + +## How does it work? +The Shuttle runtime analyzes the given macro annotations and does one of two things: +- If your service is run locally, attempt to spin up a local Postgres container via Docker (unless `local_addr` is provided then it will use ) +- If your service is deployed, the Shuttle runtime will provision a Postgres database to you. + +If you need to view your Postgres connection string, you can do so with `shuttle resource list`. diff --git a/migrations/resources/secrets.mdx b/migrations/resources/secrets.mdx new file mode 100644 index 0000000..7a52535 --- /dev/null +++ b/migrations/resources/secrets.mdx @@ -0,0 +1,69 @@ +--- +title: "Secrets" +description: "How to migrate a web service that uses environment variables or secrets to Shuttle" +--- + +## Reference + +### Quickstart + +You'll need to create a `Secrets.toml` file in your project root (if in a workspace, in the workspace root). + +It should look like this: + +```toml Secrets.toml +MY_API_KEY = 'the contents of my API key' +MY_OTHER_SECRET = 'some other secret' +``` +```toml Secrets.dev.toml (used locally if present) +MY_API_KEY = 'the contents of my API key' +MY_OTHER_SECRET = 'some other secret' +``` + + +Now you can annotate your Shuttle main function like so (using Axum as the example frameowrk): + +```rust main.rs (before) +#[shuttle_runtime::main] +async fn main() -> shuttle_axum::ShuttleAxum { + let api_key = std::env::var("MY_API_KEY").unwrap(); + + // rest of your code goes here +} +``` +```rust main.rs (after) +#[shuttle_runtime::main] +async fn main( + #[shuttle_runtime::Secrets] secrets: shuttle_runtime::SecretStore +) -> shuttle_axum::ShuttleAxum { + let api_key = secrets.get("MY_API_KEY").unwrap(); + + // rest of your code goes here +} +``` + + +### Set your secrets as environment variables + +If you are migrating from another platform to Shuttle, it is likely you are using a lot of environment variables. + +`shuttle_runtime::SecretStore` implements `IntoIter` which allows you to iterate over the store. You can use this to set all of your environment variables like so: + +```rust main.rs +#[shuttle_runtime::main] +async fn main( + #[shuttle_runtime::Secrets] secrets: shuttle_runtime::SecretStore +) -> shuttle_axum::ShuttleAxum { + secrets.into_iter().for_each(|(key, val)| { + std::env::set_var(key, val); + }); + + // rest of your code goes here +} +``` + +Note that each entry in `SecretStore` is a key-value pair, hence the destructuring. + + +## How does it work? +At runtime, the secrets are read from the file (or from Shuttle's servers if deployed) and provided as an immutable key-value store. diff --git a/mint.json b/mint.json index 7d0a918..15f7b5c 100644 --- a/mint.json +++ b/mint.json @@ -16,12 +16,12 @@ "href": "https://www.shuttle.dev/" }, "layout": "topnav", - "sidebar": {"items": "border"}, + "sidebar": { "items": "border" }, "versions": [ - {"name": "🚀 shuttle.dev (NEW)", "url": "https://docs.shuttle.dev/"}, - {"name": "⚪ shuttle.rs", "url": "https://docs.shuttle.rs/"} + { "name": "🚀 shuttle.dev (NEW)", "url": "https://docs.shuttle.dev/" }, + { "name": "⚪ shuttle.rs", "url": "https://docs.shuttle.rs/" } ], - "background": {"style": "windows"}, + "background": { "style": "windows" }, "favicon": "/favicon.png", "colors": { "background": { @@ -33,9 +33,11 @@ "dark": "#f91" }, "topbarLinks": [ - {"name": "Support", "url": "https://docs.shuttle.dev/support/support"} + { "name": "Support", "url": "https://docs.shuttle.dev/support/support" } ], - "topbarCtaButton": {"style": "roundedRectangle","type": "link", + "topbarCtaButton": { + "style": "roundedRectangle", + "type": "link", "name": "Log In", "url": "https://console.shuttle.dev/" }, @@ -77,16 +79,45 @@ "introduction/docs", "introduction/what-is-shuttle", "introduction/how-shuttle-works", - "introduction/platform-update", - "introduction/platform-migration" + { + "group": "Platform Update", + "icon": "layer-group", + "pages": [ + "platform-update/platform-update", + "platform-update/migration" + ] + }, + { + "group": "Migrating to Shuttle", + + "icon": "dove", + "pages": [ + "migrations/introduction", + "migrations/migrating-checklist", + "migrations/migrating-to-shuttle", + { + "group": "Frameworks", + "pages": [ + "migrations/frameworks/axum", + "migrations/frameworks/actix-web", + "migrations/frameworks/rocket", + "migrations/frameworks/custom-service" + ] + }, + { + "group": "Infrastructure/Resources", + "pages": [ + "migrations/resources/postgres", + "migrations/resources/secrets" + ] + } + ] + } ] }, { "group": "Getting Started", - "pages": [ - "getting-started/installation", - "getting-started/quick-start" - ] + "pages": ["getting-started/installation", "getting-started/quick-start"] }, { "group": "Shuttle", @@ -109,9 +140,7 @@ { "group": "Databases", "icon": "database", - "pages": [ - "resources/shuttle-shared-db" - ] + "pages": ["resources/shuttle-shared-db"] } ] }, @@ -134,19 +163,11 @@ }, { "group": "Guides", - "pages": [ - "guides/cli", - "guides/upgrade", - "guides/migrating-to-shuttle" - ] + "pages": ["guides/cli", "guides/upgrade"] }, { "group": "Support", - "pages": [ - "support/support", - "support/faq", - "support/troubleshooting" - ] + "pages": ["support/support", "support/faq", "support/troubleshooting"] }, { "group": "Community", diff --git a/introduction/platform-migration.mdx b/platform-update/migration.mdx similarity index 90% rename from introduction/platform-migration.mdx rename to platform-update/migration.mdx index 8fd39f3..c337fa7 100644 --- a/introduction/platform-migration.mdx +++ b/platform-update/migration.mdx @@ -1,10 +1,10 @@ --- -title: Platform migration guide -description: How to migrate projects from shuttle.rs to shuttle.dev -icon: dove +title: Transfer to the new platform +description: How to transfer projects from shuttle.rs to shuttle.dev + --- -This guide is for migration old projects from **shuttle.rs** to the new **shuttle.dev** platform. +This guide is for transferring old projects from **shuttle.rs** to the new **shuttle.dev** platform. If you are a new Shuttle user, you can ignore this document and start from [Installation](/getting-started/installation). ## 1. Check platform features & policies @@ -12,7 +12,7 @@ If you are a new Shuttle user, you can ignore this document and start from [Inst Due to the large re-write of the Shuttle platform, some features have been dropped or moved to the feature roadmap. -Make sure you have read the [Platform Update Changelog](/introduction/platform-update#changelog) to verify that it is currently possible to migrate your project. +Make sure you have read the [Platform Update Changelog](/platform-update/platform-update#changelog) to verify that it is currently possible to migrate your project. Notably: - Migrating **Shuttle Persist** is not currently possible as there is no drop-in replacement yet. diff --git a/introduction/platform-update.mdx b/platform-update/platform-update.mdx similarity index 99% rename from introduction/platform-update.mdx rename to platform-update/platform-update.mdx index 6e53ed7..f8cdad4 100644 --- a/introduction/platform-update.mdx +++ b/platform-update/platform-update.mdx @@ -1,7 +1,6 @@ --- -title: Platform update +title: Platform Update description: In Q4 2024, Shuttle is launching a new and improved platform -icon: layer-group --- Introducing the new Shuttle platform! We've supercharged what developers love about Shuttle, combining our powerful developer experience with enterprise-grade infrastructure. For developers, we've kept it simple and intuitive - no complex configs, just focus on your Rust code. On the production side, we've implemented VM-level isolation, increased reliability and scalability to meet real-world demands. From solo developers to enterprise teams, Shuttle now offers the perfect blend of ease and production-ready robustness.