wulf / create-rust-app Goto Github PK
View Code? Open in Web Editor NEWSet up a modern rust+react web app by running one command.
Home Page: https://create-rust-app.dev
License: Apache License 2.0
Set up a modern rust+react web app by running one command.
Home Page: https://create-rust-app.dev
License: Apache License 2.0
I did create-rust-app
and then went into the directory and did cargo run
.
At first I got some errors regarding postgres like:
[: FATAL: password authentication failed for user "postgres"](https://stackoverflow.com/questions/74481804/psql-error-connection-to-server-at-localhost-1-port-5432-failed-fatal)
I went into /var/lib/pgsql/data/pg_hba.conf
and changed all values with 'ident' to 'trust' (I don't know if its a good practice but went with it just to make the app work) and its fixed
but now when I do cargo run
I'm stuck here
What should I do?
I ran create-rust-app myapp
and selected sqlite and poem. Setup went smoothly, but when running cargo fullstack
I ran into some compiler errors in poem_utils.rs
:
Compiling tera v1.17.1
Compiling poem-derive v1.3.48
Compiling lettre_email v0.9.4
Compiling diesel_migrations v2.0.0
Compiling poem v1.3.48
Compiling create-rust-app v8.0.2
error[E0432]: unresolved import `http`
--> C:\Users\lynn\.cargo\registry\src\github.com-1ecc6299db9ec823\create-rust-app-8.0.2\src\util\poem_utils.rs:1:5
|
1 | use http::{StatusCode, Uri};
| ^^^^ help: a similar path exists: `poem::http`
error[E0433]: failed to resolve: use of undeclared crate or module `tokio`
--> C:\Users\lynn\.cargo\registry\src\github.com-1ecc6299db9ec823\create-rust-app-8.0.2\src\util\poem_utils.rs:119:16
|
119 | let file = tokio::fs::read(path).await.unwrap();
| ^^^^^ use of undeclared crate or module `tokio`
Some errors have detailed explanations: E0432, E0433.
For more information about an error, try `rustc --explain E0432`.
error: could not compile `create-rust-app` due to 2 previous errors
I'm using create-rust-app v8.0.2 and poem v1.3.48, and I'm running Windows 10.
> cargo --version
cargo 1.64.0 (387270bc7 2022-09-16)
> rustc --version
rustc 1.64.0 (a55dd71d5 2022-09-19)
The task here is to add CRUD features to the admin dashboard.
Currently, we only read information from the database but don't have means of modifying it.
Generate forms based on column types. Initially, we can support the following types:
We can disable the creation feature for tables which contain unsupported column types.
Similar to creation, except we should add a WHERE clause that updates the row which matches all column values. Before this, we should check that only 1 column exists for those particular values -- otherwise we won't be able to update only one row for tables without candidate keys.
We can easily add deletion based by deleting ONE row where all columns are equivalent to the one that the user wanted to delete.
BackendFramework
enummain.rs
which starts the server in create-rust-app_cli/template/src/
/api/todos
endpoints (see todo.rs
below)#[cfg(not(debug_assertions))]
) serves files from ./frontend/build
with the index.html
as the defaulttodo.rs
which serves the CRUD endpoints for the example 'todo' service in create-rust-app_cli/template/src/services
GET /
: returns a JSON list of all TODO itemsGET /id
: return a single JSON TODO itemPOST /
: creates and returns a single JSON TODO itemPUT /:id
: updates and returns a single JSON TODO itemDELETE /:id
: deletes a single item, returns 200 status code(we can get to these later)
/api/auth
routesfiles.rs
) Compiling rust_app v0.1.0 (/rust_app)
error: linking with `cc` failed: exit status: 1
= note: /usr/bin/ld: cannot find -lpq: No such file or directory
As said in the title, the dependencies in the generated cargo.toml file are out of date, I'll soon be making a pull request that updates the dsync and tsync versions (as those are the most critical), and I can make another one that updates the rest of the dependencies too if that'd be helpful
I created another service and model, then wired the frontend. Basically, I made a copy of todo with different data fields/struct (only slightly) diesel creates the database and runs the creates the schema, and no compile errors show in the rust or ts code.... The add button in my app (think the todo add button) stays disabled, so may not be connecting to the database. But in the console this appears when I click the button to navigate to that part of the webapp.
[backend] TEMPLATE_FILE /api/vulns => index.html
[backend] [2022-10-29T10:05:00Z INFO actix_web::middleware::logger] 127.0.0.1 "GET /api/vulns?page=0&page_size=5 HTTP/1.1" 200 646 "http://127.0.0.1:3000/vulns"
Any ideas or help would be appreciated.
https://docs.rs/utoipa/latest/utoipa/
https://docs.rs/utoipa-swagger-ui/3.0.1/utoipa_swagger_ui/index.html
these two crates allow developers to create a playground for their RestAPI's, making api's easy to test and play around with.
the documentation itself would look like this: (this example documents the actixweb Auth endpoints)
use actix_http::StatusCode;
use actix_web::cookie::{Cookie, SameSite};
use actix_web::{delete, get, post, web, Error as AWError, Result};
use actix_web::{
web::{Data, Json, Path, Query},
HttpRequest, HttpResponse,
};
use serde_json::json;
use utoipa::OpenApi;
use crate::pagination::PaginationParams;
use crate::services::auth::{
controller,
controller::{
ActivationInput, ChangeInput, ForgotInput, LoginInput, RegisterInput, ResetInput,
COOKIE_NAME,
},
Auth, AuthMessageResponse, AuthTokenResponse, UserSessionJson, UserSessionResponse,
};
use crate::services::JwtSecurityAddon;
use create_rust_app::Database;
use create_rust_app::Mailer;
/// handler for GET requests at the .../sessions endpoint,
///
/// requires auth
///
/// queries [`db`](`Database`) for all sessions owned by the User
/// associated with [`auth`](`Auth`)
///
/// breaks up the results of that query as defined by [`info`](`PaginationParams`)
#[utoipa::path(
context_path = "/api/auth",
params(PaginationParams),
responses(
(status = 200, description = "success, returns a json payload with all the sessions belonging to the authenticated user", body = UserSessionResponse),
(status = 401, description = "Error: Unauthorized"),
(status = 500, description = "Could not fetch sessions."),
),
tag = "Sessions",
security ( ("JWT" = []))
)]
#[get("/sessions")]
async fn sessions(
db: Data<Database>,
auth: Auth,
Query(info): Query<PaginationParams>,
) -> Result<HttpResponse> {
let result =
web::block(move || controller::get_sessions(db.into_inner().as_ref(), &auth, &info))
.await?;
match result {
Ok(sessions) => Ok(HttpResponse::Ok().json(sessions)),
Err((status_code, error_message)) => Ok(HttpResponse::build(
StatusCode::from_u16(status_code as u16).unwrap(),
)
.body(json!({ "message": error_message }).to_string())),
}
}
/// handler for DELETE requests at the .../sessions/{id} endpoint.
///
/// requires auth
///
/// deletes the entry in the `user_session` with the specified [`item_id`](`ID`) from
/// [`db`](`Database`) if it's owned by the User associated with [`auth`](`Auth`)
#[utoipa::path(
context_path = "/api/auth",
responses(
(status = 200, description = "Deleted", body = AuthMessageResponse),
(status = 401, description = "User not authenticated"),
(status = 404, description = "User session could not be found, or does not belong to authenticated user.", body = AuthMessageResponse),
(status = 500, description = "Internal Error.", body = AuthMessageResponse),
(status = 500, description = "Could not delete session.", body = AuthMessageResponse),
),
tag = "Sessions",
security ( ("JWT" = []))
)]
#[delete("/sessions/{id}")]
async fn destroy_session(
db: Data<Database>,
item_id: Path<i32>,
auth: Auth,
) -> Result<HttpResponse> {
let result =
web::block(move || controller::destroy_session(&db, &auth, item_id.into_inner())).await?;
match result {
Ok(_) => Ok(
HttpResponse::build(StatusCode::OK).body(json!({"message": "Deleted."}).to_string())
),
Err((status_code, error_message)) => Ok(HttpResponse::build(
StatusCode::from_u16(status_code as u16).unwrap(),
)
.body(json!({ "message": error_message }).to_string())),
}
}
/// handler for DELETE requests at the .../sessions enpoint
///
/// requires auth
///
/// destroys all entries in the `user_session` table in [`db`](`Database`) owned
/// by the User associated with [`auth`](`Auth`)
#[utoipa::path(
context_path = "/api/auth",
responses(
(status = 200, description = "Deleted", body = AuthMessageResponse),
(status = 401, description = "User not authenticated"),
(status = 500, description = "Could not delete sessions.", body = AuthMessageResponse),
),
tag = "Sessions",
security ( ("JWT" = []))
)]
#[delete("/sessions")]
async fn destroy_sessions(db: Data<Database>, auth: Auth) -> Result<HttpResponse, AWError> {
let result = web::block(move || controller::destroy_sessions(&db, &auth)).await?;
match result {
Ok(_) => Ok(
HttpResponse::build(StatusCode::OK).body(json!({"message": "Deleted."}).to_string())
),
Err((status_code, error_message)) => Ok(HttpResponse::build(
StatusCode::from_u16(status_code as u16).unwrap(),
)
.body(json!({ "message": error_message }).to_string())),
}
}
/// handler for POST requests at the .../login endpoint
///
/// creates a user session for the user associated with [`item`](`LoginInput`)
/// in the request body (have the `content-type` header set to `application/json` and content that can be deserialized into [`LoginInput`])
#[utoipa::path(
context_path = "/api/auth",
request_body(content = LoginInput, content_type = "application/json"),
responses(
(status = 200, description = "Deleted", body = AuthTokenResponse),
(status = 400, description = "'device' cannot be longer than 256 characters.", body = AuthMessageResponse),
(status = 400, description = "Account has not been activated.", body = AuthMessageResponse),
(status = 401, description = "Invalid credentials.", body = AuthMessageResponse),
(status = 500, description = "An internal server error occurred.", body = AuthMessageResponse),
(status = 500, description = "Could not create a session.", body = AuthMessageResponse),
),
tag = "Sessions",
)]
#[post("/login")]
async fn login(db: Data<Database>, Json(item): Json<LoginInput>) -> Result<HttpResponse, AWError> {
let result = web::block(move || controller::login(&db, &item)).await?;
match result {
Ok((access_token, refresh_token)) => Ok(HttpResponse::build(StatusCode::OK)
.cookie(
Cookie::build(COOKIE_NAME, refresh_token)
.secure(true)
.http_only(true)
.same_site(SameSite::Strict)
.finish(),
)
.body(json!({ "access_token": access_token }).to_string())),
Err((status_code, message)) => Ok(HttpResponse::build(
StatusCode::from_u16(status_code as u16).unwrap(),
)
.body(json!({ "message": message }).to_string())),
}
}
/// handler for POST requests to the .../logout endpount
///
/// If this is successful, delete the cookie storing the refresh token
///
/// TODO: document that it creates a refresh_token
#[utoipa::path(
context_path = "/api/auth",
responses(
(status = 200, description = "deletes the \"refresh_token\" cookie"),
(status = 401, description = "Invalid session.", body = AuthMessageResponse),
(status = 401, description = "Could not delete session.", body = AuthMessageResponse),
),
tag = "Sessions",
)]
#[post("/logout")]
async fn logout(db: Data<Database>, req: HttpRequest) -> Result<HttpResponse, AWError> {
let refresh_token = req
.cookie(COOKIE_NAME)
.map(|cookie| String::from(cookie.value()));
let result =
web::block(move || controller::logout(&db, refresh_token.as_ref().map(|t| t.as_ref())))
.await?;
match result {
Ok(_) => {
let mut cookie = Cookie::named(COOKIE_NAME);
cookie.make_removal();
Ok(HttpResponse::Ok().cookie(cookie).finish())
}
Err((status_code, message)) => Ok(HttpResponse::build(
StatusCode::from_u16(status_code as u16).unwrap(),
)
.body(json!({ "message": message }).to_string())),
}
}
/// handler for POST requests to the .../refresh endpoint
///
/// refreshes the user session associated with the clients refresh_token cookie
///
/// TODO: document that it needs a refresh_token cookie
#[utoipa::path(
context_path = "/api/auth",
responses(
(status = 200, description = "uses the \"refresh_token\" cookie to give the user a new session", body=AuthTokenResponse),
(status = 401, description = "Invalid session.", body = AuthMessageResponse),
(status = 401, description = "Invalid token.", body = AuthMessageResponse),
),
tag = "Sessions",
)]
#[post("/refresh")]
async fn refresh(db: Data<Database>, req: HttpRequest) -> Result<HttpResponse, AWError> {
let refresh_token = req
.cookie(COOKIE_NAME)
.map(|cookie| String::from(cookie.value()));
let result =
web::block(move || controller::refresh(&db, refresh_token.as_ref().map(|t| t.as_ref())))
.await?;
match result {
Ok((access_token, refresh_token)) => Ok(HttpResponse::build(StatusCode::OK)
.cookie(
Cookie::build(COOKIE_NAME, refresh_token)
.secure(true)
.http_only(true)
.same_site(SameSite::Strict)
.finish(),
)
.body(json!({ "access_token": access_token }).to_string())),
Err((status_code, message)) => Ok(HttpResponse::build(
StatusCode::from_u16(status_code as u16).unwrap(),
)
.body(json!({ "message": message }).to_string())),
}
}
/// handler for POST requests to the .../register endpoint
///
/// creates a new User with the information in [`item`](`RegisterInput`)
///
/// sends an email, using [`mailer`](`Mailer`), to the email address in [`item`](`RegisterInput`)
/// that contains a unique link that allows the recipient to activate the account associated with
/// that email address
#[utoipa::path(
context_path = "/api/auth",
request_body(content = RegisterInput, content_type = "application/json"),
responses(
(status = 200, description = "Success, sends an email to the user with a link that will let them activate their account", body=AuthMessageResponse),
(status = 400, description = "Already registered.", body = AuthMessageResponse),
),
tag = "Users",
)]
#[post("/register")]
async fn register(
db: Data<Database>,
Json(item): Json<RegisterInput>,
mailer: Data<Mailer>,
) -> Result<HttpResponse, AWError> {
let result = controller::register(&db, &item, &mailer);
match result {
Ok(()) => Ok(HttpResponse::build(StatusCode::OK)
.body("{ \"message\": \"Registered! Check your email to activate your account.\" }")),
Err((status_code, message)) => Ok(HttpResponse::build(
StatusCode::from_u16(status_code as u16).unwrap(),
)
.body(json!({ "message": message }).to_string())),
}
}
/// handler for GET requests to the .../activate endpoint
///
/// activates the account associated with the token in [`item`](`ActivationInput`)
#[utoipa::path(
context_path = "/api/auth",
params(ActivationInput),
responses(
(status = 200, description = "Success, account associated with activation_token is activated", body=AuthMessageResponse),
(status = 200, description = "Already activated.", body = AuthMessageResponse),
(status = 400, description = "Invalid token.", body = AuthMessageResponse),
(status = 401, description = "Invalid token", body = AuthMessageResponse),
(status = 500, description = "Could not activate user. ", body = AuthMessageResponse),
),
tag = "Users",
)]
#[get("/activate")]
async fn activate(
db: Data<Database>,
Query(item): Query<ActivationInput>,
mailer: Data<Mailer>,
) -> Result<HttpResponse, AWError> {
let result = controller::activate(&db, &item, &mailer);
match result {
Ok(()) => Ok(HttpResponse::build(StatusCode::OK).body("{ \"message\": \"Activated!\" }")),
Err((status_code, message)) => Ok(HttpResponse::build(
StatusCode::from_u16(status_code as u16).unwrap(),
)
.body(json!({ "message": message }).to_string())),
}
}
/// handler for POST requests to the .../forgot endpoint
///
/// sends an email to the email in the ['ForgotInput'] Json in the request body
/// that will allow the user associated with that email to change their password
///
/// sends an email, using [`mailer`](`Mailer`), to the email address in [`item`](`RegisterInput`)
/// that contains a unique link that allows the recipient to reset the password
/// of the account associated with that email address (or create a new account if there is
/// no accound accosiated with the email address)
#[utoipa::path(
context_path = "/api/auth",
request_body(content = ForgotInput, content_type = "application/json"),
responses(
(status = 200, description = "Success, password reset email is sent to users email", body=AuthMessageResponse),
),
tag = "Users",
)]
#[post("/forgot")]
async fn forgot_password(
db: Data<Database>,
Json(item): Json<ForgotInput>,
mailer: Data<Mailer>,
) -> Result<HttpResponse, AWError> {
let result = controller::forgot_password(&db, &item, &mailer);
match result {
Ok(()) => Ok(HttpResponse::build(StatusCode::OK)
.body("{ \"message\": \"Please check your email.\" }")),
Err((status_code, message)) => Ok(HttpResponse::build(
StatusCode::from_u16(status_code as u16).unwrap(),
)
.body(json!({ "message": message }).to_string())),
}
}
/// handler for POST requests to the .../change endpoint
///
/// requires auth
///
/// change the password of the User associated with [`auth`](`Auth`)
/// from [`item.old_password`](`ChangeInput`) to [`item.new_password`](`ChangeInput`)
#[utoipa::path(
context_path = "/api/auth",
request_body(content = ChangeInput, content_type = "application/json"),
responses(
(status = 200, description = "Success, password changed", body=AuthMessageResponse),
(status = 400, description = "Missing password.", body=AuthMessageResponse),
(status = 400, description = "The new password must be different.", body=AuthMessageResponse),
(status = 400, description = "Account has not been activated.", body=AuthMessageResponse),
(status = 400, description = "Invalid credentials.", body=AuthMessageResponse),
(status = 500, description = "Could not find user.", body=AuthMessageResponse),
(status = 500, description = "Could not update password.", body=AuthMessageResponse),
),
tag = "Users",
security ( ("JWT" = []))
)]
#[post("/change")]
async fn change_password(
db: Data<Database>,
Json(item): Json<ChangeInput>,
auth: Auth,
mailer: Data<Mailer>,
) -> Result<HttpResponse, AWError> {
let result = controller::change_password(&db, &item, &auth, &mailer);
match result {
Ok(()) => Ok(HttpResponse::build(StatusCode::OK)
.body(json!({"message": "Password changed."}).to_string())),
Err((status_code, message)) => Ok(HttpResponse::build(
StatusCode::from_u16(status_code as u16).unwrap(),
)
.body(json!({ "message": message }).to_string())),
}
}
/// handler for POST requests to the .../check endpoint
///
/// requires auth, but doesn't match it to a user
///
/// see [`controller::check`]
///
/// # Responses
/// | StatusCode | content |
/// |:------------|---------|
/// | 200 | ()
#[utoipa::path(
context_path = "/api/auth",
responses(
(status = 200, description = "Success"),
),
tag = "Users",
security ( ("JWT" = []))
)]
#[post("/check")]
async fn check(auth: Auth) -> HttpResponse {
controller::check(&auth);
HttpResponse::Ok().finish()
}
/// handler for POST requests to the .../reset endpoint
///
/// changes the password of the user associated with [`item.reset_token`](`ResetInput`)
/// to [`item.new_password`](`ResetInput`)
#[utoipa::path(
context_path = "/api/auth",
request_body(content = ResetInput, content_type = "application/json"),
responses(
(status = 200, description = "Password changed.", body=AuthMessageResponse),
(status = 400, description = "Invalid token.", body=AuthMessageResponse),
(status = 400, description = "Account has not been activated.", body=AuthMessageResponse),
(status = 400, description = "The new password must be different.", body=AuthMessageResponse),
(status = 401, description = "Invalid token.", body=AuthMessageResponse),
(status = 500, description = "Could not update password.", body=AuthMessageResponse),
),
tag = "Users",
)]
#[post("/reset")]
async fn reset_password(
db: Data<Database>,
Json(item): Json<ResetInput>,
mailer: Data<Mailer>,
) -> Result<HttpResponse, AWError> {
let result = controller::reset_password(&db, &item, &mailer);
match result {
Ok(()) => Ok(HttpResponse::build(StatusCode::OK)
.body(json!({"message": "Password reset"}).to_string())),
Err((status_code, message)) => Ok(HttpResponse::build(
StatusCode::from_u16(status_code as u16).unwrap(),
)
.body(json!({ "message": message }).to_string())),
}
}
/// returns the endpoints for the Auth service
pub fn endpoints(scope: actix_web::Scope) -> actix_web::Scope {
scope
.service(sessions)
.service(destroy_session)
.service(destroy_sessions)
.service(login)
.service(logout)
.service(check)
.service(refresh)
.service(register)
.service(activate)
.service(forgot_password)
.service(change_password)
.service(reset_password)
}
// swagger
#[derive(OpenApi)]
#[openapi(
paths(sessions, destroy_session, destroy_sessions, login, logout, refresh, register, activate, forgot_password, change_password, check, reset_password),
components(
schemas(UserSessionResponse, UserSessionJson, AuthMessageResponse, AuthTokenResponse, LoginInput, RegisterInput, ForgotInput, ChangeInput, ResetInput)
),
tags(
(name = "Auth", description = "users and user_sessions management endpoints"),
(name = "Sessions", description = "Endpoints for user_sessions management"),
(name = "Users", description = "Endpoints for useres management"),
),
modifiers(&JwtSecurityAddon)
)]
pub struct ApiDoc;
and serving the ui involves adding
use utoipa::OpenApi;
use utoipa_swagger_ui::{SwaggerUi, Url};
app = app.service(SwaggerUi::new("/swagger-ui/{_:.*}").urls(vec![
(
Url::new("auth", "/api-doc/openapi_auth.json"),
services::auth::ApiDoc::openapi(),
),
...
]));
to the main #[cfg(debug_assertions)]
block in a projects main.rs file
the resulting UI looks something like this
the UI also shows all the documented responses and requests, the format of said requests, and even allows you to directly interact with the API with the "Try it out" button:
Check the CLI's upstream version and inform the end user to upgrade accordingly.
Just saw this project and thought that it would be cool if there was an option to generate a Rust WASM project that interfaces with the React app? So React is only UI and all the logic is in the WASM app and it can share code with the backend?
We should update the project to support diesel 2
Hi,
I followed the steps to install the app and started a project. My steps -
poem
as a backendContainer Plugin: dockerize your app
and Storage Plugin: adds S3 file storage capabilities
cargo fullstack
I was able to register. But, the activation link http://localhost:8080/activate?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTI1MzQ0MjYsInN1YiI6MSwidG9rZW5fdHlwZSI6ImFjdGl2YXRpb25fdG9rZW4ifQ.-CMs1vftV31TRVFd1ZotDU5yWpRlR2DbUJGvMg9eN2o' when pasted in browser gave
not found` response. So, I changed the port to 3000 and pasted the token in the input box.
Then, I clicked on forgot password. An email was dispatched and printed on the console. I copied the link from the console. My link was
http://localhost:8080/reset?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTAwMjgyNjUsInN1YiI6MSwidG9rZW5fdHlwZSI6InJlc2V0X3Rva2VuIn0.QhSunPvvFm2QVmK38_KLZJW0X7QxtN-1_jWwzO9ovyo
I clicked the link and response was not found
So, I tried
http://127.0.0.1:3000/reset?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTAwMjgyNjUsInN1YiI6MSwidG9rZW5fdHlwZSI6InJlc2V0X3Rva2VuIn0.QhSunPvvFm2QVmK38_KLZJW0X7QxtN-1_jWwzO9ovyo
. It worked!
So, how to fix this issue?
BackendFramework
enummain.rs
which starts the server in create-rust-app_cli/template/src/
/api/todos
endpoints (see todo.rs
below)#[cfg(not(debug_assertions))]
) serves files from ./frontend/build
with the index.html
as the defaulttodo.rs
which serves the CRUD endpoints for the example 'todo' service in create-rust-app_cli/template/src/services
GET /
: returns a JSON list of all TODO itemsGET /id
: return a single JSON TODO itemPOST /
: creates and returns a single JSON TODO itemPUT /:id
: updates and returns a single JSON TODO itemDELETE /:id
: deletes a single item, returns 200 status code(we can get to these later)
/api/auth
routesfiles.rs
)After following the instruction to the point where I used cargo fullstack
I encountered this:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { kind: NotFound, message: "program not found" }', .cargo/bin/fullstack.rs:17:10
I found out the problem probably has to do something with this:
〉which yarn
╭───┬──────┬──────────────────────────────────┬──────────╮
│ # │ arg │ path │ built-in │
├───┼──────┼──────────────────────────────────┼──────────┤
│ 0 │ yarn │ C:\Program Files\nodejs\yarn.CMD │ false │
╰───┴──────┴──────────────────────────────────┴──────────╯
After some research I found some fitting issues..
#issuecomment-368300622
rust-lang/rust#94743
For now the only idea I have to fix this, would be some Windows specific code to check for .CMD
or maybe use which
, which should support that?
DROP TABLE table_name CASCADE ALL;
doesn't seem to be valid syntax. The ALL
at the end is too much.
Found this out while trying to do diesel migration revert
.
Example: migrations/00000000000002_todos/down.sql+database_postgres
create-rust-app/create-rust-app/src/auth/controller.rs
Lines 556 to 564 in 5008413
As said in the subject, the hard-coded base URL ("http://localhost:3000/") should be set by an environment variable (maybe PUBLIC_BASE_URL
or something like that) because in a production environment the recipient of the email, an end-user, will not be running the web server on their local machine, the server will (probably) be hosted on a cloud service provider like Amazon AWS, Linode, etc. and that the user connects to through a browser.
I haven't checked the whole codebase yet, but almost all references to https://localhost:3000 should instead read from an environment variable.
Also, the second link in the code snippet above would be https://localhost:3000, not https://localhost:300
Hey, I did not how to contact you, so I decided to open a Issue. I'd love to contribute to this project because I was looking for something like, I even thought about building one myself, but I found this projects which is quite advance. Maybe we could talk send me a message...
BackendFramework
enummain.rs
which starts the server in create-rust-app_cli/template/src/
/api/todos
endpoints (see todo.rs
below)#[cfg(not(debug_assertions))]
) serves files from ./frontend/build
with the index.html
as the defaulttodo.rs
which serves the CRUD endpoints for the example 'todo' service in create-rust-app_cli/template/src/services
GET /
: returns a JSON list of all TODO itemsGET /id
: return a single JSON TODO itemPOST /
: creates and returns a single JSON TODO itemPUT /:id
: updates and returns a single JSON TODO itemDELETE /:id
: deletes a single item, returns 200 status code(we can get to these later)
/api/auth
routesfiles.rs
)The demo screencast shows the same error without it being obvious how to fix it. Have tried various changes to the "proxy" settings in frontend/package.json without success.
Great project though- thanks!
https://github.com/tvallotton/models
this tool will generate migrations for structs as long as they are annotated with a derive model macro and macros to describe the constraints on the sql fields.
this are the migrations generated by diesel
this is the migration generated by the models_cli tool with the models generate -r
command
create-rust-app/create-rust-app/src/auth/controller.rs
Lines 188 to 195 in 5008413
device is never set, so will always be stored as Null
PR with a fix is coming soon
When running this on windows, chmod
is used, causing the instalation to fail sometwhat (Some files are still copied).
Run command as tutorial video in readme: https://github.com/Wulf/create-rust-app/blob/main/docs/create-rust-app-v2.mp4
[backend] [2022-10-12T01:47:43Z INFO actix_web::middleware::logger] 192.168.124.1 "POST /api/auth/refresh HTTP/1.1" 401 49 "http://192.168.124.111:3000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" 0.002163
[backend] ASSET_FILE /src/images/plus.svg => ./frontend/src/images/plus.svg
[backend] ASSET_FILE /src/images/logo.svg => ./frontend/src/images/logo.svg
[backend] [2022-10-12T01:47:43Z INFO actix_web::middleware::logger] 192.168.124.1 "GET /src/images/plus.svg HTTP/1.1" 304 0 "http://192.168.124.111:3000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" 0.000662
[backend] [2022-10-12T01:47:43Z INFO actix_web::middleware::logger] 192.168.124.1 "GET /src/images/logo.svg HTTP/1.1" 304 0 "http://192.168.124.111:3000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" 0.000610
[backend] [2022-10-12T01:47:43Z INFO actix_web::middleware::logger] 192.168.124.1 "GET /api/development/health HTTP/1.1" 200 41 "http://192.168.124.111:3000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" 0.000834
[backend] [2022-10-12T01:47:43Z INFO actix_web::middleware::logger] 192.168.124.1 "GET /api/development/db/needs-migration HTTP/1.1" 200 25 "http://192.168.124.111:3000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" 0.002727
[backend] PUBLIC_FILE /pwa.json => ./frontend/public/pwa.json
[backend] [2022-10-12T01:47:43Z INFO actix_web::middleware::logger] 192.168.124.1 "GET /pwa.json HTTP/1.1" 304 0 "http://192.168.124.111:3000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" 0.000776
[backend] The vite dev server seems to be down...
[backend] [2022-10-12T01:47:45Z INFO actix_web::middleware::logger] 192.168.124.1 "GET /__vite_ping HTTP/1.1" 404 20 "http://192.168.124.111:3000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" 0.000735
[backend] The vite dev server seems to be down...
[backend] [2022-10-12T01:47:46Z INFO actix_web::middleware::logger] 192.168.124.1 "GET /__vite_ping HTTP/1.1" 404 20 "http://192.168.124.111:3000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" 0.000742
[backend] The vite dev server seems to be down...
It seems that "vite_ping" is fetched by vite plugin from "localhost:3000",which will derectly go to backend default endpoints. It seems like a bug. Should create-rust-app put index.html in frontend and query "localhost:21012" in browser?
async function waitForSuccessfulPing(ms = 1000) {
// eslint-disable-next-line no-constant-condition
while (true) {
try {
const pingResponse = await fetch(`${base}__vite_ping`)
// success - 2xx status code
if (pingResponse.ok) break
// failure - non-2xx status code
else throw new Error()
} catch (e) {
// wait ms before attempting to ping again
await new Promise((resolve) => setTimeout(resolve, ms))
}
}
}
hey, I noticed that you use create-react-app
to develop frontend, do you have interest switch that to vite, not only vite is lightly, but also it has a awesome plugin can compile you web into wasm vite-plugin-rsw
Backend
redis-rs
to connect to a local redis instanceauth
plugin is enabled, cache auth checks in redisGreat project, have enjoyed using it - thanks!
I've got a query about some of the mailer settings that are used in the project:
It looks like there's an environment variable, SEND_EMAIL
checked here:
But further down there is some validation that looks for a different environment variable SEND_MAIL
:
Was it intended for there to be two different settings to control separate behaviours? The check also appears to print an error message (that at face value sounds contradictory) if SEND_MAIL
is set to true.
❯ cargo fullstack
Updating crates.io index
error: failed to select a version for `create-rust-app`.
... required by package `my-todo-app v0.1.0 (/home/teresa/dev/rust/my-todo-app)`
versions that meet the requirements `^8.0.0` are: 8.0.0
the package `my-todo-app` depends on `create-rust-app`, with features: `database_sqlite` but `create-rust-app` does not have these features.
failed to select a version for `create-rust-app` which could resolve this conflict
here's the cargo.toml:
[[bin]]
name = "fullstack"
path = ".cargo/bin/fullstack.rs"
[[bin]]
name = "tsync"
path = ".cargo/bin/tsync.rs"
[[bin]]
name = "my-todo-app"
path = "backend/main.rs"
[dependencies]
actix-files = "0.6.0"
actix-http = "3.0.0"
actix-multipart = "0.4.0"
actix-web = "4.0.1"
futures-util = "0.3.21"
create-rust-app = {version="8.0.0", features=["plugin_dev", "plugin_storage", "database_sqlite", "backend_actix-web"]}
serde_json = "1.0.79"
tsync = "1.2.1"
[dependencies.chrono]
features = ["serde"]
version = "0.4.19"
[dependencies.diesel]
default-features = false
features = ["sqlite", "r2d2", "chrono"]
version = "2.0.0-rc.1"
[dependencies.serde]
features = ["derive"]
version = "1.0.133"
[dependencies.tokio]
features = ["full"]
version = "1"
[package]
default-run = "my-todo-app"
edition = "2021"
name = "my-todo-app"
publish = false
version = "0.1.0"
[profile.dev]
debug-assertions = true
The first step here is to create an outline for documentation sections such. From there, we can create more issues to populate individual sections.
Here are some sections we can't miss:
After running create-rust-app my-todo-app
, and choosing postgres, actix-web, and all plugins I get
Compiling create-rust-app v8.1.0
Finished dev [unoptimized + debuginfo] target(s) in 1m 06s
Running `.cargo/.build/debug/dsync`
thread 'main' panicked at '`backend/schema.rs` is empty or not present', .cargo/bin/dsync.rs:42:9
This is on Ubuntu 22.04 running in WSL.
Backend
POST /api/graphql
GET /api/graphql/ws
GET /api/graphql/playground
Frontend
We can use the async-graphql implementation.
We can embed something like this into the development plugin:
https://github.com/mjmlio/mjml
It should help write/maintain emails.
cargo run --release
......
thread 'main' panicked at 'No SECRET_KEY environment variable set!', /home/xxx/.cargo/registry/src/github.com-1ecc6299db9ec823/create-rust-app-8.0.2/src/lib.rs:65:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Although the .env does exist and has SECRET_KEY set and working fine with cargo run in dev mode.
Thanks for this project, it's super handy.
One option i'd like to see, especially when just learning, is the option to have no Database in the backend and just have the bare minimum to get the server going with the base create-react-app page loaded.
Two small fixes we need to make to the generated front-end:
?token=
query parameter.
I love to see Redis, Hadoop, Kafka etc added as plugins
also I'd love to talk about the project and how I could help out on discord
Instead of serving the dashboard at all routes on the development backend, we should serve it on a configurable route like /admin
because then users will be able to use it in production environments as well.
I tried to use sqlite, but it didn't work. How do I switch to sqlite?
thanks
Received this error on a brand new project created today.
error[E0432]: unresolved import `http`
--> /Users/username/.cargo/registry/src/github.com-1ecc6299db9ec823/create-rust-app-6.0.1/src/util/actix_web_utils.rs:3:5
|
3 | use http::StatusCode;
| ^^^^ help: a similar path exists: `actix_web::http`
Cargo.toml
[[bin]]
name = "fullstack"
path = ".cargo/bin/fullstack.rs"
[[bin]]
name = "tsync"
path = ".cargo/bin/tsync.rs"
[[bin]]
name = "my-project"
path = "backend/main.rs"
[dependencies]
actix-files = "0.6.0"
actix-http = "3.0.0"
actix-multipart = "0.4.0"
actix-web = "4.0.1"
async-graphql = "3.0.38"
futures-util = "0.3.21"
jsonwebtoken = "8.1.0"
async-graphql-actix-web = "3.0.38"
serde_json = "1.0.79"
tsync = "1.2.1"
[dependencies.chrono]
features = ["serde"]
version = "0.4.19"
[dependencies.create-rust-app]
features = ["plugin_dev", "plugin_auth", "plugin_container", "plugin_graphql", "backend_actix-web"]
version = "6.0.1"
[dependencies.diesel]
default-features = false
features = ["postgres", "r2d2", "chrono"]
version = "1.4.8"
[dependencies.serde]
features = ["derive"]
version = "1.0.133"
[dependencies.tokio]
features = ["full"]
version = "1"
[package]
default-run = "my-project"
edition = "2021"
name = "my-project"
publish = false
version = "0.1.0"
[profile.dev]
debug-assertions = true
cargo --version
cargo 1.60.0 (d1fd9fe2c 2022-03-01
Some projects may require queues -- for them, it might help to have lapin
setup and connected with a local RabbitMQ instance.
Backend
lapin
Add test stubs for poem-web
Some users prefer not to have the initial template which showcases functionality and plugins. We should add a --blank
option to the CLI which generates a project that doesn't have the example "TODO" service, modal, migration, and front-end view.
Related #12
Add test stubs for actix-web
BackendFramework
enummain.rs
which starts the server in create-rust-app_cli/template/src/
/api/todos
endpoints (see todo.rs
below)#[cfg(not(debug_assertions))]
) serves files from ./frontend/build
with the index.html
as the defaulttodo.rs
which serves the CRUD endpoints for the example 'todo' service in create-rust-app_cli/template/src/services
GET /
: returns a JSON list of all TODO itemsGET /id
: return a single JSON TODO itemPOST /
: creates and returns a single JSON TODO itemPUT /:id
: updates and returns a single JSON TODO itemDELETE /:id
: deletes a single item, returns 200 status code(we can get to these later)
/api/auth
routesfiles.rs
)The todo.rs within models and services complains that failed to resolve: could not find todos in schema
which makes sense as the file is empty.
Is this expected behaviour? Are we meant to build the schema ourselves? :)
This prevents cargo from building.
I followed the quick start to create a new app.
When I run cargo fullstack
or cargo build
, I receive the following error:
Compiling create-rust-app v8.1.1
error[E0433]: failed to resolve: use of undeclared crate or module `argonautica`
--> /Users/rohit/.cargo/registry/src/github.com-1ecc6299db9ec823/create-rust-app-8.1.1/src/dev/endpoints/service_poem.rs:1:5
|
1 | use argonautica::config::Version::_0x10;
| ^^^^^^^^^^^ use of undeclared crate or module `argonautica`
Originates here:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.