Fix login

This commit is contained in:
eraden 2022-07-18 08:23:20 +02:00
parent f9d71754e7
commit 883aa37e7c
6 changed files with 143 additions and 62 deletions

View File

@ -8,17 +8,17 @@ customElements.define('login-form', class extends Component {
* { font-family: 'Noto Sans', sans-serif; } * { font-family: 'Noto Sans', sans-serif; }
${ FORM_STYLE } ${ FORM_STYLE }
</style> </style>
<form> <form action="/login" method="post">
<div> <div>
<label>Login</label> <label>Login</label>
<input name="login" placeholder="Login" type="text" required /> <input name="email" placeholder="E-Mail" type="email" required />
</div> </div>
<div> <div>
<label>Hasło</label> <label>Hasło</label>
<input name="pass" placeholder="Hasło" type="password" required /> <input name="password" placeholder="Hasło" type="password" required />
</div> </div>
<div> <div>
<input type="button" value="Zaloguj" /> <input type="submit" value="Zaloguj" />
</div> </div>
</form> </form>
`); `);

View File

@ -49,6 +49,11 @@ customElements.define('account-view', class extends Component {
<label>Powiązane konto Facebook</label> <label>Powiązane konto Facebook</label>
<input id="facebook_id" name="facebook_id" readonly /> <input id="facebook_id" name="facebook_id" readonly />
</div> </div>
<div>
<form action="/logout" method="post">
<input value="Wyloguj" type="submit" />
</form>
</div>
`); `);
this.addEventListener('facebook:available', ev => { this.addEventListener('facebook:available', ev => {
ev.preventDefault(); ev.preventDefault();

View File

@ -60,7 +60,7 @@ customElements.define('register-user-form', class extends Component {
</svg> </svg>
<form id="form" method="post" action="/register"> <form id="form" method="post" action="/register">
<input type="hidden" id="account_type" name="account_type"> <input type="hidden" id="account_type" name="account_type" value="User">
<input type="hidden" id="facebook_id" name="facebook_id"> <input type="hidden" id="facebook_id" name="facebook_id">
<div> <div>
<label>E-Mail</label> <label>E-Mail</label>

View File

@ -200,3 +200,12 @@ pub struct UpdateContactInput {
pub contact_type: String, pub contact_type: String,
pub content: String, pub content: String,
} }
#[derive(Debug)]
pub struct CreateAccountInput {
pub login: String,
pub email: String,
pub pass: String,
pub facebook_id: Option<String>,
pub account_type: AccountType,
}

View File

@ -3,7 +3,7 @@ use std::cmp::Ordering;
use tracing::error; use tracing::error;
use crate::model::db; use crate::model::db;
use crate::model::db::NewsArticle; use crate::model::db::{LocalBusiness, NewsArticle};
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -12,6 +12,11 @@ pub enum Error {
item_id: i32, item_id: i32,
idx: i32, idx: i32,
}, },
CreateLocalBusiness {
name: String,
description: String,
owner_id: i32,
},
UpdateBusiness { UpdateBusiness {
input: db::UpdateLocalBusinessInput, input: db::UpdateLocalBusinessInput,
}, },
@ -40,6 +45,12 @@ pub enum Error {
AccountByEmail { AccountByEmail {
email: String, email: String,
}, },
CreateAccount {
input: db::CreateAccountInput,
},
AccountTaken {
input: db::CreateAccountInput,
},
Item { Item {
item_id: i32, item_id: i32,
}, },
@ -904,3 +915,67 @@ RETURNING
Error::DeleteContact { id } Error::DeleteContact { id }
}) })
} }
#[tracing::instrument]
pub async fn create_local_business(
t: &mut T<'_>,
name: String,
owner_id: i32,
description: String,
) -> Result<LocalBusiness> {
sqlx::query_as(
r#"
INSERT INTO local_businesses (name, owner_id, description)
VALUES ($1, $2, $3)
RETURNING id, owner_id, name, description, state
"#,
)
.bind(&name)
.bind(owner_id)
.bind(&description)
.fetch_one(t)
.await
.map_err(|e| {
error!("{e}");
dbg!(e);
Error::CreateLocalBusiness {
name,
description,
owner_id,
}
})
}
#[tracing::instrument]
pub async fn create_account(t: &mut T<'_>, input: db::CreateAccountInput) -> Result<db::Account> {
sqlx::query_as(
r#"
INSERT INTO accounts (login, email, pass, facebook_id, account_type)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, login, email, pass, facebook_id, account_type
"#,
)
.bind(&input.login)
.bind(&input.email)
.bind(&input.pass)
.bind(&input.facebook_id)
.bind(input.account_type)
.fetch_one(t)
.await
.map_err(|e| {
error!("{e}");
dbg!(&e);
match e {
sqlx::Error::Database(e) => {
if e.message()
== "duplicate key value violates unique constraint \"accounts_email_key\""
{
Error::AccountTaken { input }
} else {
Error::CreateAccount { input }
}
}
_ => Error::CreateAccount { input },
}
})
}

View File

@ -14,6 +14,32 @@ use crate::routes::{Identity, JsonResult, Result};
use crate::view::Helper; use crate::view::Helper;
use crate::{not_xss, queries, routes, utils}; use crate::{not_xss, queries, routes, utils};
#[derive(Default, Template)]
#[template(path = "account.html")]
struct AccountTemplate {
account: Option<db::Account>,
error: Option<String>,
page: Page,
h: Helper,
}
impl AccountTemplate {
pub fn error<Error: Into<String>>(error: Error, page: Page) -> Self {
AccountTemplate {
error: Some(error.into()),
page,
..Default::default()
}
}
pub fn bad_request<Error: Into<String>>(
error: Error,
page: Page,
) -> routes::Result<HttpResponse> {
Ok(HttpResponse::BadRequest().body(AccountTemplate::error(error, page).render().unwrap()))
}
}
#[post("/register")] #[post("/register")]
#[tracing::instrument] #[tracing::instrument]
async fn register( async fn register(
@ -35,53 +61,39 @@ async fn register(
let pass = match utils::encrypt(&form.password) { let pass = match utils::encrypt(&form.password) {
Ok(pass) => pass, Ok(pass) => pass,
Err(e) => { Err(e) => {
tracing::error!("{:?}", e); error!("{:?}", e);
dbg!(e); dbg!(e);
t.rollback().await.unwrap(); t.rollback().await.unwrap();
return Ok(HttpResponse::BadRequest().body( return AccountTemplate::bad_request(
AccountTemplate { "Zapisanie hasła nie powiodło się",
error: Some("Zapisanie hasła nie powiodło się".into()), Page::Register,
page: Page::Register, );
..Default::default()
}
.render()
.unwrap(),
));
} }
}; };
let res: sqlx::Result<db::Account> = sqlx::query_as( let res = queries::create_account(
r#" &mut t,
INSERT INTO accounts (login, email, pass, facebook_id, account_type) db::CreateAccountInput {
VALUES ($1, $2, $3, $4, $5) login: form.login,
RETURNING id, login, email, pass, facebook_id, account_type email: form.email,
"#, pass,
facebook_id: form.facebook_id,
account_type: form.account_type,
},
) )
.bind(form.login)
.bind(form.email)
.bind(pass)
.bind(form.facebook_id)
.bind(form.account_type)
.fetch_one(&mut t)
.await; .await;
let account = match res { let account = match res {
Ok(res) => { Ok(res) => {
id.remember(format!("{}", res.id)); id.remember(format!("{}", res.id));
res res
} }
Err(queries::Error::AccountTaken { .. }) => {
return AccountTemplate::bad_request("Adres e-mail jest zajęty", Page::Register);
}
Err(e) => { Err(e) => {
tracing::error!("{e}");
dbg!(e); dbg!(e);
t.rollback().await.unwrap(); t.rollback().await.unwrap();
return Ok(HttpResponse::BadRequest().body( return AccountTemplate::bad_request("Problem z utworzeniem konta", Page::Register);
AccountTemplate {
error: Some("Problem z utworzeniem konta".into()),
page: Page::Register,
..Default::default()
}
.render()
.unwrap(),
));
} }
}; };
@ -93,22 +105,11 @@ RETURNING id, login, email, pass, facebook_id, account_type
not_xss!(name, t); not_xss!(name, t);
not_xss!(description, t); not_xss!(description, t);
let res: sqlx::Result<db::LocalBusiness> = sqlx::query_as( let res =
r#" queries::create_local_business(&mut t, name.into(), owner_id, description.into()).await;
INSERT INTO local_businesses (name, owner_id, description)
VALUES ($1, $2, $3)
RETURNING id, owner_id, name, description, state
"#,
)
.bind(name)
.bind(owner_id)
.bind(description)
.fetch_one(&mut t)
.await;
let business = match res { let business = match res {
Ok(business) => business, Ok(business) => business,
Err(e) => { Err(e) => {
tracing::error!("{e}");
dbg!(e); dbg!(e);
t.rollback().await.unwrap(); t.rollback().await.unwrap();
return Ok(HttpResponse::BadRequest().body( return Ok(HttpResponse::BadRequest().body(
@ -249,7 +250,7 @@ async fn login(form: web::Form<LoginForm>, db: Data<PgPool>, id: Identity) -> Re
let record: db::Account = match queries::account_by_email(&mut t, form.email).await { let record: db::Account = match queries::account_by_email(&mut t, form.email).await {
Ok(record) => record, Ok(record) => record,
Err(e) => { Err(e) => {
tracing::error!("{e:?}"); error!("{e:?}");
dbg!(e); dbg!(e);
t.rollback().await.ok(); t.rollback().await.ok();
return Ok(HttpResponse::Ok().body( return Ok(HttpResponse::Ok().body(
@ -264,7 +265,7 @@ async fn login(form: web::Form<LoginForm>, db: Data<PgPool>, id: Identity) -> Re
} }
}; };
if let Err(e) = utils::validate(&form.password, &record.pass) { if let Err(e) = utils::validate(&form.password, &record.pass) {
tracing::error!("{e}"); error!("{e}");
dbg!(e); dbg!(e);
t.rollback().await.ok(); t.rollback().await.ok();
return Ok(HttpResponse::BadRequest().body( return Ok(HttpResponse::BadRequest().body(
@ -308,15 +309,6 @@ async fn upload(
routes::uploads::hande_upload(payload, id, "accounts").await routes::uploads::hande_upload(payload, id, "accounts").await
} }
#[derive(Default, Template)]
#[template(path = "account.html")]
struct AccountTemplate {
account: Option<db::Account>,
error: Option<String>,
page: Page,
h: Helper,
}
#[get("/account")] #[get("/account")]
#[tracing::instrument] #[tracing::instrument]
async fn account_page(id: Identity, db: Data<PgPool>) -> Result<HttpResponse> { async fn account_page(id: Identity, db: Data<PgPool>) -> Result<HttpResponse> {