Application can now run as Docker container.

main
Josef Rokos 1 day ago
parent 3f0136e1c2
commit 87d1b33d7c

1
Cargo.lock generated

@ -3235,6 +3235,7 @@ dependencies = [
"lettre", "lettre",
"log", "log",
"pwhash", "pwhash",
"rand",
"regex", "regex",
"rust_decimal", "rust_decimal",
"serde", "serde",

@ -41,6 +41,7 @@ leptos-captcha = "0.2.0"
charts-rs = { version = "0.3.5", optional = true} charts-rs = { version = "0.3.5", optional = true}
#image = { version = "0.24.8", optional = true } #image = { version = "0.24.8", optional = true }
base64 = "0.22.0" base64 = "0.22.0"
rand = "0.8.5"
[features] [features]
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"] csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]

@ -0,0 +1,51 @@
FROM rust:1.86.0-bookworm AS builder
# Install cargo-binstall, which makes it easier to install other
# cargo extensions like cargo-leptos
RUN wget https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz
RUN tar -xvf cargo-binstall-x86_64-unknown-linux-musl.tgz
RUN cp cargo-binstall /usr/local/cargo/bin
# Install required tools
RUN apt-get update -y \
&& apt-get install -y --no-install-recommends clang
# Install cargo-leptos
RUN cargo binstall cargo-leptos -y
RUN rustup default stable
# Add the WASM target
RUN rustup target add wasm32-unknown-unknown
#RUN rustup target add wasm32-unknown-unknown --toolchain nightly
# Make an /app dir, which everything will eventually live in
RUN mkdir -p /app
WORKDIR /app
COPY . .
# Build the app
#RUN cargo leptos build --release -vv
RUN LEPTOS_OUTPUT_NAME="rezervator-$(tr -dc a-z0-9 </dev/urandom | head -c 10)" cargo leptos build -r -P
FROM debian:bookworm-slim AS runtime
WORKDIR /app
RUN apt-get update -y \
&& apt-get install -y --no-install-recommends openssl ca-certificates \
&& apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*
# Copy the server binary to the /app directory
COPY --from=builder /app/target/release/rezervator /app/
# /target/site contains our JS/WASM/CSS, etc.
COPY --from=builder /app/target/site /app/target/site
# Set any required env variables and
EXPOSE 3000
# -- NB: update binary name from "leptos_start" to match your app name in Cargo.toml --
# Run the server
CMD ["/app/rezervator", "-c /app/target/site/data/config.toml"]

@ -0,0 +1 @@
ALTER TABLE appearance ADD css_name VARCHAR;

@ -6,6 +6,7 @@ use crate::components::data_form::ForValidation;
cfg_if! { if #[cfg(feature = "ssr")] { cfg_if! { if #[cfg(feature = "ssr")] {
use std::fs;
use actix_web::{post, Responder}; use actix_web::{post, Responder};
use actix_multipart::Multipart; use actix_multipart::Multipart;
use actix_session::Session; use actix_session::Session;
@ -20,6 +21,8 @@ cfg_if! { if #[cfg(feature = "ssr")] {
use actix_web::web::Data; use actix_web::web::Data;
use crate::backend::AppData; use crate::backend::AppData;
use regex::Regex; use regex::Regex;
use rand::Rng;
use rand::distributions::Alphanumeric;
pub async fn check_appearance(pool: &PgPool) -> Result<(), AppError> { pub async fn check_appearance(pool: &PgPool) -> Result<(), AppError> {
let count: (i64,) = query_as("SELECT COUNT(id) FROM appearance") let count: (i64,) = query_as("SELECT COUNT(id) FROM appearance")
@ -32,6 +35,11 @@ cfg_if! { if #[cfg(feature = "ssr")] {
.await?; .await?;
} }
let app = query_as::<_, Appearance>("SELECT * FROM appearance").fetch_one(pool).await?;
if let None = app.css_name {
query("UPDATE appearance SET css_name = 'banner.css'").execute(pool).await?;
}
Ok(()) Ok(())
} }
@ -44,8 +52,8 @@ cfg_if! { if #[cfg(feature = "ssr")] {
Ok(()) Ok(())
} }
async fn modify_style(file_name: &str) -> Result<(), AppError> { async fn modify_style(file_name: &str, pool: &PgPool) -> Result<(), AppError> {
let mut css_file = File::open("target/site/banner.css")?; let mut css_file = File::open("target/site/data/banner.css")?;
let mut css_str= String::new(); let mut css_str= String::new();
css_file.read_to_string(&mut css_str)?; css_file.read_to_string(&mut css_str)?;
@ -56,8 +64,26 @@ cfg_if! { if #[cfg(feature = "ssr")] {
css_str = re.replace(&css_str, &format!("background-image: url('{}')", file_name)).to_string(); css_str = re.replace(&css_str, &format!("background-image: url('{}')", file_name)).to_string();
} }
let mut css_file = File::create("target/site/banner.css")?; let old_css: (String,) = query_as("SELECT css_name FROM appearance")
.fetch_one(pool)
.await?;
if old_css.0 != "banner.css" {
fs::remove_file(format!("target/site/data/{}", old_css.0))?;
}
let s: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(5)
.map(char::from)
.collect();
let css_name = format!("banner-{}.css", s);
let mut css_file = File::create(format!("target/site/data/{}", css_name))?;
css_file.write_all(css_str.as_bytes())?; css_file.write_all(css_str.as_bytes())?;
query("UPDATE appearance SET css_name = $1")
.bind(css_name)
.execute(pool)
.await?;
Ok(()) Ok(())
} }
@ -84,14 +110,14 @@ cfg_if! { if #[cfg(feature = "ssr")] {
return Redirect::to("/admin/appearance").see_other(); return Redirect::to("/admin/appearance").see_other();
} }
let mut file = File::create(format!("target/site/{}", file_name)).unwrap(); let mut file = File::create(format!("target/site/data/{}", file_name)).unwrap();
let _name = field.name(); let _name = field.name();
while let Some(chunk) = field.next().await { while let Some(chunk) = field.next().await {
let c = chunk.unwrap(); let c = chunk.unwrap();
let _ = file.write_all(&c); let _ = file.write_all(&c);
} }
let _ = set_banner_name(&file_name, &app_data.db_pool).await; let _ = set_banner_name(&file_name, &app_data.db_pool).await;
let _ = modify_style(&file_name).await; let _ = modify_style(&file_name, &app_data.db_pool).await;
} }
Redirect::to("/admin/appearance").see_other() Redirect::to("/admin/appearance").see_other()
@ -149,7 +175,7 @@ pub async fn delete_banner() -> Result<ApiResponse<()>, ServerFnError> {
.await?; .await?;
if let Some(f) = appearance.banner { if let Some(f) = appearance.banner {
fs::remove_file(format!("target/site/{}", f))?; fs::remove_file(format!("target/site/data/{}", f))?;
} }
Ok(ApiResponse::Data(())) Ok(ApiResponse::Data(()))

@ -719,7 +719,8 @@ pub struct Appearance {
id: i32, id: i32,
pub banner: Option<String>, pub banner: Option<String>,
pub text: Option<String>, pub text: Option<String>,
pub title: Option<String> pub title: Option<String>,
pub css_name: Option<String>
} }
impl Appearance { impl Appearance {

@ -1,13 +1,14 @@
use leptos::*; use leptos::*;
use leptos_meta::*; use leptos_meta::*;
use crate::app::DialogHelper; use crate::app::DialogHelper;
use crate::backend::appearance::get_appearance;
use crate::components::user_menu::MenuOpener; use crate::components::user_menu::MenuOpener;
#[component] #[component]
pub fn Header() -> impl IntoView { pub fn Header() -> impl IntoView {
let drawer = use_context::<MenuOpener>().expect("No drawer opener"); let drawer = use_context::<MenuOpener>().expect("No drawer opener");
let dlg_helper = use_context::<DialogHelper>().expect("No dialog helper"); let dlg_helper = use_context::<DialogHelper>().expect("No dialog helper");
//let banner_css = create_signal(String::new()); let appearance = create_blocking_resource(||(), |_| get_appearance());
view! { view! {
<Html <Html
@ -39,7 +40,14 @@ pub fn Header() -> impl IntoView {
<Link rel="stylesheet" href="/vendor/css/core.css" /> <Link rel="stylesheet" href="/vendor/css/core.css" />
<Link rel="stylesheet" href="/vendor/css/theme-default.css" /> <Link rel="stylesheet" href="/vendor/css/theme-default.css" />
<Link rel="stylesheet" href="/css/demo.css" /> <Link rel="stylesheet" href="/css/demo.css" />
<Link rel="stylesheet" href="/banner.css" /> <Transition fallback=move || view! {""}>
{
appearance.get().map(|a| match a {
Ok(a) => view! {<Link rel="stylesheet" href={format!("/data/{}", a.css_name.unwrap_or_default())} />},
Err(_) => view! {<Link rel="stylesheet" href="/data/banner.css" />}
})
}
</Transition>
<Link rel="stylesheet" href="/vendor/css/control.css" /> <Link rel="stylesheet" href="/vendor/css/control.css" />
//<!-- Vendors CSS --> //<!-- Vendors CSS -->

@ -43,7 +43,7 @@ async fn main() -> std::io::Result<()> {
Pow::init_random().expect("Cannot init captcha"); Pow::init_random().expect("Cannot init captcha");
let cfg_path = matches.opt_str("c").unwrap_or("config.toml".to_string()); let cfg_path = matches.opt_str("c").unwrap_or("config.toml".to_string());
let srv_conf = load_config(&cfg_path); let srv_conf = load_config(cfg_path.trim());
env_logger::Builder::from_env(Env::default().default_filter_or(srv_conf.logging().severity())).init(); env_logger::Builder::from_env(Env::default().default_filter_or(srv_conf.logging().severity())).init();
info!("Starting server"); info!("Starting server");

Loading…
Cancel
Save