This commit is contained in:
Adrian Woźniak 2022-07-15 09:04:43 +02:00
parent ce997cb7c8
commit 2f7e6358e8
No known key found for this signature in database
GPG Key ID: 0012845A89C7352B
7 changed files with 81 additions and 46 deletions

View File

@ -36,7 +36,7 @@
<ow-nav>
{% if page.is_public() %}
<ow-path path="/" selected="{{ page.select_index() }}">Lokalne Usługi</ow-path>
<ow-path path="/" selected="{{ page.select_marketplace() }}">Targ</ow-path>
<ow-path path="/marketplace" selected="{{ page.select_marketplace() }}">Targ</ow-path>
<ow-path path="/news" selected="{{ page.select_news() }}">Aktualności</ow-path>
<ow-path path="/account" selected="{{ page.select_account() }}">Konto</ow-path>
{% match account.as_ref() %}
@ -44,8 +44,11 @@
<ow-path path="/account/business-items" selected="{{ page.select_business_items() }}">Moje usługi</ow-path>
{% when None %}
{% endmatch %}
{% else if page.is_admin() %}
{% if h.is_admin(account) %}
<ow-path path="/admin" selected="{{ page.select_admin_news() }}">Admin</ow-path>
{% endif %}
{% else if page.is_admin() %}
<ow-path path="/">Home</ow-path>
<ow-path path="/admin/news" selected="{{ page.select_admin_news() }}">News</ow-path>
<ow-path path="/admin/businesses" selected="{{ page.select_admin_businesses() }}">Localne Usługi</ow-path>
{% endif %}

View File

@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use chrono::NaiveDateTime;
use chrono::{NaiveDateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Type};
use uuid::Uuid;
@ -29,8 +29,9 @@ pub enum Role {
Admin,
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, Type)]
#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize, Type)]
pub enum LocalBusinessState {
#[default]
Pending,
Approved,
Banned,
@ -88,7 +89,7 @@ pub struct Token {
pub role: Role,
}
#[derive(Debug, Serialize, Deserialize, FromRow)]
#[derive(Debug, Default, Serialize, Deserialize, FromRow)]
pub struct LocalBusiness {
pub id: i32,
pub owner_id: i32,
@ -117,6 +118,19 @@ pub struct NewsArticle {
pub created_at: NaiveDateTime,
}
impl Default for NewsArticle {
fn default() -> Self {
Self {
id: 0,
title: "".to_string(),
body: "".to_string(),
status: NewsStatus::Pending,
published_at: None,
created_at: Utc::now().naive_utc(),
}
}
}
#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct CreateNewsArticleInput {
pub title: String,

View File

@ -2,8 +2,9 @@ use serde::{Deserialize, Serialize};
use crate::model::db;
#[derive(Debug)]
#[derive(Debug, Default)]
pub enum Page {
#[default]
LocalBusinesses,
News,
Account,

View File

@ -9,7 +9,9 @@ use crate::routes::{Identity, Result};
pub mod admin;
mod business_item;
#[derive(Debug, Template)]
use crate::view::Helper;
#[derive(Debug, Default, Template)]
#[template(path = "business-items.html")]
struct BusinessItemsTemplate {
page: view::Page,
@ -17,6 +19,7 @@ struct BusinessItemsTemplate {
account: Option<db::Account>,
items: Vec<db::LocalBusinessItem>,
business: db::LocalBusiness,
h: Helper,
}
#[macro_export]
@ -71,10 +74,10 @@ async fn handle_business_items_page(
let items: Vec<db::LocalBusinessItem> = queries::account_items(t, account.id).await;
let page = BusinessItemsTemplate {
page: view::Page::BusinessItems,
error: None,
account: Some(account),
items,
business,
..Default::default()
};
Ok(HttpResponse::Ok()
.content_type("text/html")

View File

@ -6,14 +6,14 @@ use sqlx::PgPool;
use crate::model::view::{Page, SetStateBusinessInput};
use crate::model::{db, view};
use crate::routes::{Identity, JsonResult, Result};
use crate::view::filters;
use crate::view::{filters, Helper};
use crate::{authorize, queries};
#[macro_export]
macro_rules! require_admin {
($t: expr, $id: expr) => {{
let account = authorize!(&mut $t, $id);
if account.account_type == crate::model::db::AccountType::Admin {
if account.account_type != crate::model::db::AccountType::Admin {
return Err(crate::routes::Error::Forbidden);
}
account
@ -27,13 +27,14 @@ macro_rules! require_admin {
}};
}
#[derive(Debug, Template)]
#[derive(Debug, Default, Template)]
#[template(path = "admin/news.html")]
struct AdminNewsTemplate {
page: view::Page,
error: Option<String>,
account: Option<db::Account>,
news: Vec<db::NewsArticle>,
h: Helper,
}
#[get("")]
@ -60,22 +61,22 @@ async fn admin_news(db: Data<PgPool>, id: Identity) -> Result<HttpResponse> {
Ok(HttpResponse::Ok().content_type("text/html").body(
AdminNewsTemplate {
page: Page::AdminNews,
error: None,
account: None,
news,
..Default::default()
}
.render()
.unwrap(),
))
}
#[derive(Debug, Template)]
#[derive(Debug, Default, Template)]
#[template(path = "admin/edit.html")]
struct EditTemplate {
page: view::Page,
page: Page,
error: Option<String>,
account: Option<db::Account>,
article: db::NewsArticle,
h: Helper,
}
#[get("/{id}")]
@ -102,9 +103,8 @@ async fn edit_news_article(
Ok(HttpResponse::Ok().content_type("text/html").body(
EditTemplate {
page: Page::AdminNews,
error: None,
account: None,
article,
..Default::default()
}
.render()
.unwrap(),
@ -139,8 +139,7 @@ async fn create_news_article(
AdminNewsTemplate {
page: Page::AdminCreateNews,
error: Some("Failed".into()),
account: None,
news: vec![],
..Default::default()
}
.render()
.unwrap(),
@ -242,13 +241,14 @@ async fn news_article_upload(
crate::routes::uploads::hande_upload(payload, Some(account.id), "news").await
}
#[derive(Debug, Template)]
#[derive(Debug, Default, Template)]
#[template(path = "admin/businesses.html")]
struct AdminBusinessesTemplate {
page: view::Page,
page: Page,
error: Option<String>,
account: Option<db::Account>,
businesses: Vec<view::LocalBusiness>,
h: Helper,
}
#[get("")]
@ -278,9 +278,8 @@ async fn admin_businesses(db: Data<PgPool>, id: Identity) -> Result<HttpResponse
Ok(HttpResponse::Ok().content_type("text/html").body(
AdminBusinessesTemplate {
page: Page::AdminBusinesses,
error: None,
account: None,
businesses,
..Default::default()
}
.render()
.unwrap(),

View File

@ -11,26 +11,25 @@ use tracing::*;
use crate::model::db;
use crate::model::view::{self, Page};
use crate::routes::{Identity, JsonResult, Result};
use crate::view::filters;
use crate::view::{filters, Helper};
use crate::{not_xss, queries, routes, utils};
#[derive(Template)]
#[derive(Default, Template)]
#[template(path = "index.html")]
pub struct IndexTemplate {
services: Vec<view::LocalBusiness>,
account: Option<db::Account>,
error: Option<String>,
page: Page,
h: Helper,
}
#[tracing::instrument]
pub async fn render_index() -> HttpResponse {
HttpResponse::NotFound().content_type("text/html").body(
IndexTemplate {
services: vec![],
account: None,
error: None,
page: Page::LocalBusinesses,
..Default::default()
}
.render()
.unwrap(),
@ -69,8 +68,8 @@ pub async fn index(db: Data<PgPool>, id: Identity) -> Result<HttpResponse> {
let body = IndexTemplate {
services,
account: record,
error: None,
page: Page::LocalBusinesses,
..Default::default()
}
.render()
.unwrap();
@ -80,12 +79,13 @@ pub async fn index(db: Data<PgPool>, id: Identity) -> Result<HttpResponse> {
Ok(HttpResponse::Ok().content_type("text/html").body(body))
}
#[derive(Template)]
#[derive(Default, Template)]
#[template(path = "account.html")]
struct AccountTemplate {
account: Option<db::Account>,
error: Option<String>,
page: Page,
h: Helper,
}
#[get("/account")]
@ -104,8 +104,8 @@ async fn account_page(id: Identity, db: Data<PgPool>) -> Result<HttpResponse> {
Ok(HttpResponse::Ok().body(
AccountTemplate {
account,
error: None,
page: Page::Account,
..Default::default()
}
.render()
.unwrap(),
@ -199,9 +199,9 @@ async fn register(
t.rollback().await.unwrap();
return Ok(HttpResponse::BadRequest().body(
AccountTemplate {
account: None,
error: Some("Zapisanie hasła nie powiodło się".into()),
page: Page::Register,
..Default::default()
}
.render()
.unwrap(),
@ -234,9 +234,9 @@ RETURNING id, login, email, pass, facebook_id, account_type
t.rollback().await.unwrap();
return Ok(HttpResponse::BadRequest().body(
AccountTemplate {
account: None,
error: Some("Problem z utworzeniem konta".into()),
page: Page::Register,
..Default::default()
}
.render()
.unwrap(),
@ -272,9 +272,9 @@ RETURNING id, owner_id, name, description, state
t.rollback().await.unwrap();
return Ok(HttpResponse::BadRequest().body(
AccountTemplate {
account: None,
error: Some("Problem z utworzeniem konta".into()),
page: Page::Register,
..Default::default()
}
.render()
.unwrap(),
@ -308,11 +308,11 @@ RETURNING id, local_business_id, name, price, item_order, picture_url
t.rollback().await.unwrap();
return Ok(HttpResponse::BadRequest().content_type("text/html").body(
AccountTemplate {
account: None,
error: Some(
"Problem z utworzeniem konta. Nie można zapisać zdjęcia.".into(),
),
page: Page::Register,
..Default::default()
}
.render()
.unwrap(),
@ -325,11 +325,11 @@ RETURNING id, local_business_id, name, price, item_order, picture_url
t.rollback().await.unwrap();
return Ok(HttpResponse::BadRequest().content_type("text/html").body(
AccountTemplate {
account: None,
error: Some(
"Problem z utworzeniem konta. Nie można zapisać zdjęcia.".into(),
),
page: Page::Register,
..Default::default()
}
.render()
.unwrap(),
@ -350,9 +350,9 @@ RETURNING id, local_business_id, name, price, item_order, picture_url
t.rollback().await.unwrap();
return Ok(HttpResponse::BadRequest().content_type("text/html").body(
AccountTemplate {
account: None,
error: Some("Problem z utworzeniem konta".into()),
page: Page::Register,
..Default::default()
}
.render()
.unwrap(),
@ -369,8 +369,8 @@ RETURNING id, local_business_id, name, price, item_order, picture_url
.body(
AccountTemplate {
account: Some(account),
error: None,
page: Page::Register,
..Default::default()
}
.render()
.unwrap(),
@ -385,10 +385,8 @@ async fn logout(id: Identity) -> HttpResponse {
.append_header(("Location", "/"))
.body(
IndexTemplate {
services: vec![],
account: None,
error: None,
page: Page::LocalBusinesses,
..Default::default()
}
.render()
.unwrap(),
@ -415,9 +413,9 @@ async fn login(form: web::Form<LoginForm>, db: Data<PgPool>, id: Identity) -> Re
t.rollback().await.ok();
return Ok(HttpResponse::Ok().body(
AccountTemplate {
account: None,
error: Some("Nie znaleziono konta".into()),
page: Page::Login,
..Default::default()
}
.render()
.unwrap(),
@ -430,9 +428,9 @@ async fn login(form: web::Form<LoginForm>, db: Data<PgPool>, id: Identity) -> Re
t.rollback().await.ok();
return Ok(HttpResponse::BadRequest().body(
AccountTemplate {
account: None,
error: Some("Hasło i/lub adres e-mail są nieprawidłowe".into()),
page: Page::Login,
..Default::default()
}
.render()
.unwrap(),
@ -444,8 +442,9 @@ async fn login(form: web::Form<LoginForm>, db: Data<PgPool>, id: Identity) -> Re
Ok(HttpResponse::Ok().content_type("text/html").body(
AccountTemplate {
account: Some(record),
error: None,
page: Page::Login,
..Default::default()
}
.render()
.unwrap(),
@ -468,13 +467,14 @@ async fn upload(
routes::uploads::hande_upload(payload, id, "accounts").await
}
#[derive(Template)]
#[derive(Default, Template)]
#[template(path = "news.html")]
pub struct NewsTemplate {
account: Option<db::Account>,
error: Option<String>,
page: Page,
news: Vec<db::NewsArticle>,
h: Helper,
}
#[get("/news")]
@ -492,9 +492,9 @@ async fn news(id: Identity, db: Data<PgPool>) -> Result<HttpResponse> {
Ok(HttpResponse::Ok().content_type("text/html").body(
NewsTemplate {
account,
error: None,
page: Page::News,
news,
..Default::default()
}
.render()
.unwrap(),

View File

@ -1,7 +1,22 @@
pub mod filters {
pub fn opt_time(s: &Option<chrono::NaiveDateTime>) -> ::askama::Result<String> {
Ok(s.as_ref()
.map(|t| serde_json::to_string(t).unwrap_or_default())
.unwrap_or_default())
}
}
#[derive(Default, Debug)]
pub struct Helper;
impl Helper {
pub fn is_admin(&self, account: &Option<crate::model::db::Account>) -> bool {
use crate::model::db::AccountType;
account
.as_ref()
.map(|a| a.account_type == AccountType::Admin)
.unwrap_or_default()
}
}