mirror of
https://github.com/kristoferssolo/echoes-of-ascension.git
synced 2025-10-21 18:50:34 +00:00
refactor
This commit is contained in:
parent
74b9b00de1
commit
b626e0a7d7
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -871,6 +871,7 @@ dependencies = [
|
||||
"leptos_router",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"thiserror 2.0.11",
|
||||
"tokio",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@ -20,5 +20,6 @@ codegen-units = 1
|
||||
|
||||
[workspace.lints.clippy]
|
||||
nursery = "warn"
|
||||
pedantic = "warn"
|
||||
unwrap_used = "warn"
|
||||
style = "warn"
|
||||
perf = "warn"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use std::fmt::Display;
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
use secrecy::{ExposeSecret, SecretString};
|
||||
use serde::Deserialize;
|
||||
@ -66,10 +66,10 @@ impl DatabaseSettings {
|
||||
pub fn get_config() -> Result<Settings, config::ConfigError> {
|
||||
let base_path = std::env::current_dir().expect("Failed to determine current directory");
|
||||
let config_directory = base_path.join("backend").join("config");
|
||||
let env: Environment = std::env::var("APP_ENVIRONMENT")
|
||||
let env = std::env::var("APP_ENVIRONMENT")
|
||||
.unwrap_or_else(|_| "local".into())
|
||||
.try_into()
|
||||
.expect("Failed to parse APP_ENVIRONMENT");
|
||||
.parse()
|
||||
.unwrap_or(Environment::Local);
|
||||
|
||||
let env_filename = format!("{}.toml", &env);
|
||||
|
||||
@ -97,7 +97,14 @@ impl Display for Environment {
|
||||
impl TryFrom<String> for Environment {
|
||||
type Error = String;
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
match value.to_lowercase().as_str() {
|
||||
Self::from_str(&value)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Environment {
|
||||
type Err = String;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"local" => Ok(Self::Local),
|
||||
"production" => Ok(Self::Production),
|
||||
other => Err(format!(
|
||||
|
||||
@ -7,16 +7,18 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
leptos = { version = "0.7.0", features = ["nightly"] }
|
||||
leptos_router = { version = "0.7.0", features = ["nightly"] }
|
||||
leptos = { version = "0.7", features = ["nightly"] }
|
||||
leptos_router = { version = "0.7", features = ["nightly"] }
|
||||
axum = { version = "0.7", optional = true }
|
||||
console_error_panic_hook = { version = "0.1", optional = true }
|
||||
leptos_axum = { version = "0.7.0", optional = true }
|
||||
leptos_meta = { version = "0.7.0" }
|
||||
leptos_axum = { version = "0.7", optional = true }
|
||||
leptos_meta = { version = "0.7" }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread"], optional = true }
|
||||
wasm-bindgen = { version = "=0.2.100", optional = true }
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
serde.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate", "dep:console_error_panic_hook", "dep:wasm-bindgen"]
|
||||
|
||||
@ -1,61 +0,0 @@
|
||||
use leptos::prelude::*;
|
||||
use leptos_meta::{provide_meta_context, MetaTags, Stylesheet, Title};
|
||||
use leptos_router::{
|
||||
components::{Route, Router, Routes},
|
||||
StaticSegment,
|
||||
};
|
||||
|
||||
pub fn shell(options: LeptosOptions) -> impl IntoView {
|
||||
view! {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<AutoReload options=options.clone() />
|
||||
<HydrationScripts options />
|
||||
<MetaTags />
|
||||
</head>
|
||||
<body>
|
||||
<App />
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn App() -> impl IntoView {
|
||||
// Provides context that manages stylesheets, titles, meta tags, etc.
|
||||
provide_meta_context();
|
||||
|
||||
view! {
|
||||
// injects a stylesheet into the document <head>
|
||||
// id=leptos means cargo-leptos will hot-reload this stylesheet
|
||||
<Stylesheet id="leptos" href="/pkg/frontend.css" />
|
||||
|
||||
// sets the document title
|
||||
<Title text="Welcome to Leptos" />
|
||||
|
||||
// content for this welcome page
|
||||
<Router>
|
||||
<main>
|
||||
<Routes fallback=|| "Page not found.".into_view()>
|
||||
<Route path=StaticSegment("") view=HomePage />
|
||||
</Routes>
|
||||
</main>
|
||||
</Router>
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the home page of your application.
|
||||
#[component]
|
||||
fn HomePage() -> impl IntoView {
|
||||
// Creates a reactive value to update the button
|
||||
let count = RwSignal::new(0);
|
||||
let on_click = move |_| *count.write() += 1;
|
||||
|
||||
view! {
|
||||
<h1>"Welcome to Leptos!"</h1>
|
||||
<button on:click=on_click>"Click Me: " {count}</button>
|
||||
}
|
||||
}
|
||||
33
frontend/src/components/app.rs
Normal file
33
frontend/src/components/app.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use leptos::prelude::*;
|
||||
use leptos_meta::{provide_meta_context, Stylesheet, Title};
|
||||
use leptos_router::{
|
||||
components::{Route, Router, Routes},
|
||||
StaticSegment,
|
||||
};
|
||||
|
||||
use crate::components::{homepage::HomePage, register::RegisterForm};
|
||||
|
||||
#[component]
|
||||
pub fn App() -> impl IntoView {
|
||||
// Provides context that manages stylesheets, titles, meta tags, etc.
|
||||
provide_meta_context();
|
||||
|
||||
view! {
|
||||
// injects a stylesheet into the document <head>
|
||||
// id=leptos means cargo-leptos will hot-reload this stylesheet
|
||||
<Stylesheet id="leptos" href="/pkg/frontend.css" />
|
||||
|
||||
// sets the document title
|
||||
<Title text="Welcome to Leptos" />
|
||||
|
||||
// content for this welcome page
|
||||
<Router>
|
||||
<main>
|
||||
<Routes fallback=|| "Page not found.".into_view()>
|
||||
<Route path=StaticSegment("") view=HomePage />
|
||||
<Route path=StaticSegment("/register") view=RegisterForm />
|
||||
</Routes>
|
||||
</main>
|
||||
</Router>
|
||||
}
|
||||
}
|
||||
14
frontend/src/components/homepage.rs
Normal file
14
frontend/src/components/homepage.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use leptos::prelude::*;
|
||||
|
||||
/// Renders the home page of your application.
|
||||
#[component]
|
||||
pub fn HomePage() -> impl IntoView {
|
||||
// Creates a reactive value to update the button
|
||||
let count = RwSignal::new(0);
|
||||
let on_click = move |_| *count.write() += 1;
|
||||
|
||||
view! {
|
||||
<h1>"Welcome to Leptos!"</h1>
|
||||
<button on:click=on_click>"Click Me: " {count}</button>
|
||||
}
|
||||
}
|
||||
25
frontend/src/components/mod.rs
Normal file
25
frontend/src/components/mod.rs
Normal file
@ -0,0 +1,25 @@
|
||||
mod app;
|
||||
mod homepage;
|
||||
mod register;
|
||||
pub use app::App;
|
||||
|
||||
use leptos::prelude::*;
|
||||
use leptos_meta::MetaTags;
|
||||
|
||||
pub fn shell(options: LeptosOptions) -> impl IntoView {
|
||||
view! {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<AutoReload options=options.clone() />
|
||||
<HydrationScripts options />
|
||||
<MetaTags />
|
||||
</head>
|
||||
<body>
|
||||
<App />
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
}
|
||||
115
frontend/src/components/register.rs
Normal file
115
frontend/src/components/register.rs
Normal file
@ -0,0 +1,115 @@
|
||||
use leptos::{ev::SubmitEvent, prelude::*};
|
||||
use reqwest::{Client, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FormData {
|
||||
username: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ResponseData {
|
||||
username: String,
|
||||
code: String,
|
||||
}
|
||||
|
||||
#[server]
|
||||
async fn register(payload: FormData) -> Result<ResponseData, ServerFnError> {
|
||||
let client = Client::new();
|
||||
let response = client
|
||||
.post("http://localhost:8000/api/v1/register")
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
match response.status() {
|
||||
StatusCode::CREATED => {
|
||||
let response_data = response.json::<ResponseData>().await?;
|
||||
Ok(response_data)
|
||||
}
|
||||
status => {
|
||||
let error_msg = response.text().await?;
|
||||
Err(ServerFnError::ServerError(format!(
|
||||
"Registration failed: {} - {}",
|
||||
status, error_msg
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn RegisterForm() -> impl IntoView {
|
||||
let username = RwSignal::new(String::new());
|
||||
let register_action = Action::new(|input: &FormData| {
|
||||
let input = input.clone();
|
||||
async move { register(input).await }
|
||||
});
|
||||
let on_submit = move |ev: SubmitEvent| {
|
||||
ev.prevent_default();
|
||||
let form_data = FormData {
|
||||
username: username.get(),
|
||||
};
|
||||
register_action.dispatch(form_data);
|
||||
};
|
||||
let is_submitting = move || register_action.pending().get();
|
||||
|
||||
view! {
|
||||
<form on:submit=on_submit>
|
||||
<div>
|
||||
<label>"Username"</label>
|
||||
<input
|
||||
type="text"
|
||||
on:input=move |ev| username.set(event_target_value(&ev))
|
||||
prop:value=username
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" disabled=is_submitting>
|
||||
{move || { if is_submitting() { "Registering..." } else { "Register" } }}
|
||||
</button>
|
||||
<ErrorBoundary fallback=move |errors| {
|
||||
view! {
|
||||
<div class="error-container">
|
||||
<p class="error-title">"Registration Error:"</p>
|
||||
<ul class="error-list">
|
||||
{move || {
|
||||
errors
|
||||
.get()
|
||||
.into_iter()
|
||||
.map(|(_, e)| {
|
||||
view! { <li class="error-item">{e.to_string()}</li> }
|
||||
})
|
||||
.collect_view()
|
||||
}}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
}>
|
||||
{move || {
|
||||
register_action
|
||||
.value()
|
||||
.get()
|
||||
.map(|result| {
|
||||
match result {
|
||||
Ok(response) => {
|
||||
view! {
|
||||
<div class="success-container">
|
||||
<p class="success-message">"Registration successful!"</p>
|
||||
<div class="registration-details">
|
||||
<p>"Username: " {response.username}</p>
|
||||
<p>"Your Code: " {response.code}</p>
|
||||
<p class="code-notice">
|
||||
"Please save this code. You will need it to login."
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
.into_any()
|
||||
}
|
||||
Err(e) => view! { <span>{e.to_string()}</span> }.into_any(),
|
||||
}
|
||||
})
|
||||
}}
|
||||
</ErrorBoundary>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
pub mod app;
|
||||
pub mod components;
|
||||
|
||||
#[cfg(feature = "hydrate")]
|
||||
#[wasm_bindgen::prelude::wasm_bindgen]
|
||||
pub fn hydrate() {
|
||||
use crate::app::*;
|
||||
use crate::components::*;
|
||||
console_error_panic_hook::set_once();
|
||||
leptos::mount::hydrate_body(App);
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
use axum::Router;
|
||||
use frontend::app::*;
|
||||
use frontend::components::*;
|
||||
use leptos::logging::log;
|
||||
use leptos::prelude::*;
|
||||
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||
|
||||
@ -43,4 +43,4 @@ done
|
||||
DATABASE_URL="postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}"
|
||||
export DATABASE_URL
|
||||
sqlx database create
|
||||
sqlx migrate run
|
||||
sqlx migrate run --source backend/migrations
|
||||
|
||||
Loading…
Reference in New Issue
Block a user