settings: add base url, WIP flash system
This commit is contained in:
parent
f2bea92272
commit
c670201b86
18 changed files with 190 additions and 68 deletions
|
|
@ -9,6 +9,7 @@ rocket_db_pools = { version = "0.1.0-rc.2", features = ["sqlx_sqlite"] }
|
|||
rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["tera"] }
|
||||
infer = { version = "0.12.0", default-features = false }
|
||||
erased-serde = "0.3"
|
||||
url = { workspace = true }
|
||||
|
||||
# local crates
|
||||
database_pool = { path = "../database_pool" }
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
mod content;
|
||||
mod flash;
|
||||
mod responder;
|
||||
mod template;
|
||||
|
||||
use self::content::*;
|
||||
use erased_serde::Serialize;
|
||||
|
||||
pub use flash::FlashKind;
|
||||
|
||||
pub enum Page {
|
||||
Error(Error),
|
||||
Setup,
|
||||
|
|
|
|||
29
crates/ezidam/src/page/flash.rs
Normal file
29
crates/ezidam/src/page/flash.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
|
||||
pub enum FlashKind {
|
||||
Success,
|
||||
Info,
|
||||
Warning,
|
||||
Danger,
|
||||
}
|
||||
|
||||
impl Display for FlashKind {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
FlashKind::Success => "success",
|
||||
FlashKind::Info => "info",
|
||||
FlashKind::Warning => "warning",
|
||||
FlashKind::Danger => "danger",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for FlashKind {
|
||||
fn into(self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
use super::Page;
|
||||
use rocket::request::FlashMessage;
|
||||
use rocket::serde::Serialize;
|
||||
use rocket_dyn_templates::Template;
|
||||
|
||||
|
|
@ -7,20 +8,32 @@ use rocket_dyn_templates::Template;
|
|||
struct TemplateContent<S: Serialize> {
|
||||
title: &'static str,
|
||||
version: &'static str,
|
||||
flash: Option<(String, String)>,
|
||||
|
||||
#[serde(flatten)]
|
||||
content: S,
|
||||
}
|
||||
|
||||
fn render(p: Page, flash: Option<(String, String)>) -> Template {
|
||||
Template::render(
|
||||
p.template_name(),
|
||||
TemplateContent {
|
||||
title: p.page_title(),
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
flash,
|
||||
content: p.content(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
impl From<Page> for Template {
|
||||
fn from(p: Page) -> Self {
|
||||
Self::render(
|
||||
p.template_name(),
|
||||
TemplateContent {
|
||||
title: p.page_title(),
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
content: p.content(),
|
||||
},
|
||||
)
|
||||
render(p, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Page {
|
||||
pub fn with_flash(self, flash: FlashMessage) -> Template {
|
||||
render(self, Some(flash.into_inner()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,19 @@ pub(self) mod prelude {
|
|||
pub use crate::error::Error;
|
||||
pub use crate::file_from_bytes::FileFromBytes;
|
||||
pub use crate::guards::*;
|
||||
pub use crate::page::Page;
|
||||
pub use crate::page::{FlashKind, Page};
|
||||
pub use hash::Password;
|
||||
pub use id::UserID;
|
||||
pub use rocket::form::Form;
|
||||
pub use rocket::request::FlashMessage;
|
||||
pub use rocket::response::Flash;
|
||||
pub use rocket::response::Redirect;
|
||||
pub use rocket::tokio::task;
|
||||
pub use rocket::FromForm;
|
||||
pub use rocket::{routes, uri, Either, Route};
|
||||
pub use rocket_db_pools::sqlx::Acquire;
|
||||
pub use rocket_db_pools::Connection;
|
||||
pub use rocket_dyn_templates::Template;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use super::prelude::*;
|
||||
use rocket::{get, post};
|
||||
use settings::Settings;
|
||||
use url::Url;
|
||||
use users::User;
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
|
|
@ -13,14 +14,18 @@ async fn setup_completed(_setup: CompletedSetup) -> Redirect {
|
|||
}
|
||||
|
||||
#[get("/", rank = 2)]
|
||||
async fn setup() -> Page {
|
||||
Page::Setup
|
||||
async fn setup(flash: Option<FlashMessage<'_>>) -> Template {
|
||||
// TODO: show flash on html page
|
||||
flash
|
||||
.map(|flash| Page::with_flash(Page::Setup, flash))
|
||||
.unwrap_or_else(|| Page::Setup.into())
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct CreateFirstAccount<'r> {
|
||||
pub username: &'r str,
|
||||
pub password: &'r str,
|
||||
pub url: &'r str,
|
||||
}
|
||||
|
||||
#[post("/", data = "<form>")]
|
||||
|
|
@ -28,9 +33,18 @@ async fn create_first_account(
|
|||
form: Form<CreateFirstAccount<'_>>,
|
||||
_setup: NeedSetup,
|
||||
mut db: Connection<Database>,
|
||||
) -> Result<Redirect> {
|
||||
) -> Result<Either<Redirect, Flash<Redirect>>> {
|
||||
let form = form.into_inner();
|
||||
|
||||
// Parse url
|
||||
return Ok(Either::Right(Flash::new(
|
||||
Redirect::to(uri!(self::setup)),
|
||||
FlashKind::Danger,
|
||||
"Failed to parse url".to_string(),
|
||||
)));
|
||||
|
||||
let url = Url::parse(form.url).unwrap();
|
||||
|
||||
// Generate UserID
|
||||
let user_id = task::spawn_blocking(UserID::default).await?;
|
||||
|
||||
|
|
@ -40,7 +54,7 @@ async fn create_first_account(
|
|||
|
||||
let mut transaction = db.begin().await?;
|
||||
|
||||
// Insert in database
|
||||
// Insert user in database
|
||||
User::insert(
|
||||
&mut transaction,
|
||||
&user_id,
|
||||
|
|
@ -53,11 +67,14 @@ async fn create_first_account(
|
|||
// Store UserID in settings
|
||||
Settings::set_first_admin(&mut transaction, &user_id).await?;
|
||||
|
||||
// Store URL in settings
|
||||
Settings::set_url(&mut transaction, &url).await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
// TODO: login with openid/oauth
|
||||
|
||||
Ok(Redirect::to(uri!("/")))
|
||||
Ok(Either::Left(Redirect::to(uri!("/"))))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -77,10 +94,9 @@ mod test {
|
|||
let create_account = client
|
||||
.post(uri!("/setup"))
|
||||
.header(ContentType::Form)
|
||||
.body(r#"username=phil&password=password"#)
|
||||
.body(r#"username=phil&password=password&url=https://example.com"#)
|
||||
.dispatch();
|
||||
assert_ne!(create_account.status(), Status::UnprocessableEntity);
|
||||
assert_ne!(create_account.status(), Status::InternalServerError);
|
||||
assert_eq!(create_account.status(), Status::SeeOther);
|
||||
|
||||
// Make request again, make sure its not OK
|
||||
let setup_page_after_creation = client.get(uri!("/setup")).dispatch();
|
||||
|
|
|
|||
|
|
@ -16,27 +16,35 @@
|
|||
<h1 class="">Welcome to Ezidam!</h1>
|
||||
<p class="text-muted">Initial setup</p>
|
||||
</div>
|
||||
|
||||
<!-- First admin account -->
|
||||
<div class="hr-text hr-text-center hr-text-spaceless">first admin account</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label required" for="username">Username</label>
|
||||
<input name="username" id="username" type="text" class="form-control" required>
|
||||
<input name="username" id="username" type="text" placeholder="Enter a username" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label required" for="password">Password</label>
|
||||
<div class="input-group input-group-flat">
|
||||
<input name="password" id="password" type="password" class="form-control" autocomplete="off" required>
|
||||
<input name="password" id="password" type="password" placeholder="Enter password" class="form-control" autocomplete="off" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings -->
|
||||
<div class="hr-text hr-text-center hr-text-spaceless">settings</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label required" for="url">Base URL</label>
|
||||
<input name="url" id="url" type="url" placeholder="https://example.com" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-center mt-3">
|
||||
<div class="col">
|
||||
<div class="btn-list justify-content-end">
|
||||
<button type="submit" class="btn btn-primary">Create account</button>
|
||||
<!-- <a href="#" class="btn btn-primary">-->
|
||||
<!-- Create account-->
|
||||
<!-- </a>-->
|
||||
<button type="submit" class="btn btn-primary">Finish setup</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue