Implemented public form appearance settings.
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
use cfg_if::cfg_if;
|
||||
use leptos::*;
|
||||
use validator::Validate;
|
||||
use crate::backend::data::{ApiResponse, Appearance};
|
||||
use crate::components::data_form::ForValidation;
|
||||
|
||||
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||
|
||||
use actix_web::{post, Responder};
|
||||
use actix_multipart::Multipart;
|
||||
use actix_session::Session;
|
||||
use actix_web::web::Redirect;
|
||||
use std::fs::File;
|
||||
use std::io::{Write, Read};
|
||||
use futures_util::{StreamExt, TryStreamExt};
|
||||
use crate::backend::data::User;
|
||||
use crate::error::AppError;
|
||||
use sqlx::{query, query_as, PgPool};
|
||||
use crate::backend::get_pool;
|
||||
use actix_web::web::Data;
|
||||
use crate::backend::AppData;
|
||||
use regex::Regex;
|
||||
|
||||
pub async fn check_appearance(pool: &PgPool) -> Result<(), AppError> {
|
||||
let count: (i64,) = query_as("SELECT COUNT(id) FROM appearance")
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
if count.0 == 0 {
|
||||
query("INSERT INTO appearance(title) VALUES('Rezervovator')")
|
||||
.execute(pool)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_banner_name(file_name: &str, pool: &PgPool) -> Result<(), AppError> {
|
||||
query("UPDATE appearance SET banner = $1")
|
||||
.bind(file_name)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn modify_style(file_name: &str) -> Result<(), AppError> {
|
||||
let mut css_file = File::open("target/site/banner.css")?;
|
||||
let mut css_str= String::new();
|
||||
css_file.read_to_string(&mut css_str)?;
|
||||
|
||||
if css_str.contains("/*bg-img*/") {
|
||||
css_str = css_str.replace("/*bg-img*/", &format!("background-image: url('{}');", file_name));
|
||||
} else {
|
||||
let re = Regex::new(r#"background-image: url\('[aA-zZ._0-9\-]+'\)"#).unwrap();
|
||||
css_str = re.replace(&css_str, &format!("background-image: url('{}')", file_name)).to_string();
|
||||
}
|
||||
|
||||
let mut css_file = File::create("target/site/banner.css")?;
|
||||
css_file.write_all(css_str.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[post("/upload")]
|
||||
pub async fn upload(mut data: Multipart, session: Session, app_data: Data<AppData>) -> impl Responder {
|
||||
let user = session.get::<User>("user").unwrap_or(None);
|
||||
|
||||
if user.is_none() {
|
||||
return Redirect::to("/login").see_other();
|
||||
}
|
||||
|
||||
if let Some(u) = user {
|
||||
if !u.admin {
|
||||
return Redirect::to("/admin/appearance").see_other();
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(Some(mut field)) = data.try_next().await {
|
||||
let content_disp = field.content_disposition().clone();
|
||||
let file_name = content_disp.get_filename().unwrap();
|
||||
|
||||
if file_name.is_empty() {
|
||||
return Redirect::to("/admin/appearance").see_other();
|
||||
}
|
||||
|
||||
let mut file = File::create(format!("target/site/{}", file_name)).unwrap();
|
||||
let _name = field.name();
|
||||
while let Some(chunk) = field.next().await {
|
||||
let c = chunk.unwrap();
|
||||
let _ = file.write_all(&c);
|
||||
}
|
||||
let _ = set_banner_name(file_name, &app_data.db_pool).await;
|
||||
let _ = modify_style(file_name).await;
|
||||
}
|
||||
|
||||
Redirect::to("/admin/appearance").see_other()
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
#[server]
|
||||
pub async fn get_appearance() -> Result<Appearance, ServerFnError> {
|
||||
let pool = get_pool().await?;
|
||||
let appearance = query_as::<_, Appearance>("SELECT * FROM appearance")
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
|
||||
Ok(appearance)
|
||||
}
|
||||
|
||||
#[server]
|
||||
pub async fn update_appearance(appearance: Appearance) -> Result<ApiResponse<()>, ServerFnError> {
|
||||
use crate::perm_check;
|
||||
|
||||
perm_check!(is_admin);
|
||||
|
||||
let pool = get_pool().await?;
|
||||
let id = appearance.id();
|
||||
query("UPDATE appearance SET title = $1, text = $2 WHERE id = $3")
|
||||
.bind(appearance.title)
|
||||
.bind(appearance.text)
|
||||
.bind(id)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
Ok(ApiResponse::Data(()))
|
||||
}
|
||||
|
||||
impl ForValidation for UpdateAppearance {
|
||||
fn entity(&self) -> &dyn Validate {
|
||||
&self.appearance
|
||||
}
|
||||
}
|
||||
|
||||
#[server]
|
||||
pub async fn delete_banner() -> Result<ApiResponse<()>, ServerFnError> {
|
||||
use std::fs;
|
||||
use crate::perm_check;
|
||||
|
||||
perm_check!(is_admin);
|
||||
|
||||
let appearance = get_appearance().await?;
|
||||
let pool = get_pool().await?;
|
||||
query("UPDATE appearance SET banner = $1 WHERE id = $2")
|
||||
.bind(None::<String>)
|
||||
.bind(appearance.id())
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
if let Some(f) = appearance.banner {
|
||||
fs::remove_file(format!("target/site/{}", f))?;
|
||||
}
|
||||
|
||||
Ok(ApiResponse::Data(()))
|
||||
}
|
||||
@@ -622,3 +622,18 @@ pub struct ChartData {
|
||||
pub count: i64,
|
||||
pub period: f64
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, Validate)]
|
||||
#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))]
|
||||
pub struct Appearance {
|
||||
id: i32,
|
||||
pub banner: Option<String>,
|
||||
pub text: Option<String>,
|
||||
pub title: Option<String>
|
||||
}
|
||||
|
||||
impl Appearance {
|
||||
pub fn id(&self) -> i32 {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ pub mod property;
|
||||
pub mod reservation;
|
||||
pub mod customer;
|
||||
pub mod mail;
|
||||
pub mod appearance;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! perm_check {
|
||||
|
||||
Reference in New Issue
Block a user