Application can now run as Docker container.
This commit is contained in:
Generated
+1
@@ -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"]
|
||||||
|
|||||||
+51
@@ -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(()))
|
||||||
|
|||||||
+2
-1
@@ -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 -->
|
||||||
|
|||||||
+1
-1
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user