summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWriterV2 <primary-v2@protonmail.com>2024-11-05 16:33:19 +0100
committerWriterV2 <primary-v2@protonmail.com>2024-11-05 16:33:19 +0100
commitdab09c4e4d1e4b21216b630bb44e57f597dfe85b (patch)
tree5c01c51c49ba4b64b03ffe9ca65c1d0ac5cd1eef
parent56233352feb78ec6da39d8ab7734102abae1e4ed (diff)
Feat: auto file (pdf + epub) creation and synchronization for story
-rw-r--r--.gitignore2
-rw-r--r--.sqlx/query-40e5b26d2df5aa2cc92c1a45c47560092b4cbb50dd1aa24320afc6a729f16cfe.json44
-rw-r--r--.sqlx/query-710176c919e6c108a8c0c982821a9c17e71ea118e5b67265136e04e2a59a5df9.json32
-rw-r--r--Dockerfile6
-rw-r--r--src/db/story.rs28
-rw-r--r--src/handlers/admin.rs11
-rw-r--r--src/main.rs6
7 files changed, 126 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index a52003f..f096822 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
/target
*.db*
.env
+static/*.pdf
+static/*.epub
diff --git a/.sqlx/query-40e5b26d2df5aa2cc92c1a45c47560092b4cbb50dd1aa24320afc6a729f16cfe.json b/.sqlx/query-40e5b26d2df5aa2cc92c1a45c47560092b4cbb50dd1aa24320afc6a729f16cfe.json
new file mode 100644
index 0000000..331604f
--- /dev/null
+++ b/.sqlx/query-40e5b26d2df5aa2cc92c1a45c47560092b4cbb50dd1aa24320afc6a729f16cfe.json
@@ -0,0 +1,44 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT * FROM product",
+ "describe": {
+ "columns": [
+ {
+ "name": "id",
+ "ordinal": 0,
+ "type_info": "Integer"
+ },
+ {
+ "name": "name",
+ "ordinal": 1,
+ "type_info": "Text"
+ },
+ {
+ "name": "description",
+ "ordinal": 2,
+ "type_info": "Text"
+ },
+ {
+ "name": "uploaddate",
+ "ordinal": 3,
+ "type_info": "Integer"
+ },
+ {
+ "name": "updatedate",
+ "ordinal": 4,
+ "type_info": "Integer"
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false,
+ false,
+ false,
+ false,
+ false
+ ]
+ },
+ "hash": "40e5b26d2df5aa2cc92c1a45c47560092b4cbb50dd1aa24320afc6a729f16cfe"
+}
diff --git a/.sqlx/query-710176c919e6c108a8c0c982821a9c17e71ea118e5b67265136e04e2a59a5df9.json b/.sqlx/query-710176c919e6c108a8c0c982821a9c17e71ea118e5b67265136e04e2a59a5df9.json
new file mode 100644
index 0000000..c0d5519
--- /dev/null
+++ b/.sqlx/query-710176c919e6c108a8c0c982821a9c17e71ea118e5b67265136e04e2a59a5df9.json
@@ -0,0 +1,32 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT p.name, s.pdf, s.epub FROM story s INNER JOIN product p ON s.pid = p.id",
+ "describe": {
+ "columns": [
+ {
+ "name": "name",
+ "ordinal": 0,
+ "type_info": "Text"
+ },
+ {
+ "name": "pdf",
+ "ordinal": 1,
+ "type_info": "Blob"
+ },
+ {
+ "name": "epub",
+ "ordinal": 2,
+ "type_info": "Blob"
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false,
+ false,
+ false
+ ]
+ },
+ "hash": "710176c919e6c108a8c0c982821a9c17e71ea118e5b67265136e04e2a59a5df9"
+}
diff --git a/Dockerfile b/Dockerfile
index 4be7100..765fd1a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
# Planner stage - Compute dependencies and create recipe
-FROM lukemathwalker/cargo-chef:latest-rust-1.75 AS planner
+FROM lukemathwalker/cargo-chef:latest-rust-1.82 AS planner
WORKDIR /app
# Copy the minimal files needed for dependency calculation
COPY Cargo.* ./
@@ -7,13 +7,13 @@ COPY src/ src/
RUN cargo chef prepare --recipe-path recipe.json
# Cache dependencies stage
-FROM lukemathwalker/cargo-chef:latest-rust-1.75 AS cacher
+FROM lukemathwalker/cargo-chef:latest-rust-1.82 AS cacher
WORKDIR /app
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
# Builder stage
-FROM lukemathwalker/cargo-chef:latest-rust-1.75 AS builder
+FROM lukemathwalker/cargo-chef:latest-rust-1.82 AS builder
WORKDIR /app
# Copy the entire project context
diff --git a/src/db/story.rs b/src/db/story.rs
index 686c33f..0680e94 100644
--- a/src/db/story.rs
+++ b/src/db/story.rs
@@ -1,3 +1,7 @@
+use std::ffi::OsStr;
+use std::fs::{read_dir, remove_file};
+use std::io::{BufWriter, Write};
+
use axum::async_trait;
use maud::html;
use sqlx::SqlitePool;
@@ -25,6 +29,30 @@ impl ProductMarker for Story {
}
}
+pub async fn synchronize_story_files(pool: &SqlitePool) -> Result<(), anyhow::Error> {
+ for entry in read_dir("static")? {
+ let path = entry?.path();
+ if path.extension().is_some_and(|ext| ext == OsStr::new("pdf") || ext == OsStr::new("epub")) {
+ remove_file(path)?;
+ }
+ }
+ let results = sqlx::query!("SELECT p.name, s.pdf, s.epub FROM story s INNER JOIN product p ON s.pid = p.id").fetch_all(pool).await?;
+ for result in results.iter() {
+ let mut pdf_filename = format!("{}.pdf", result.name).to_lowercase();
+ pdf_filename.retain(|c| !c.is_whitespace());
+
+ let file = std::fs::File::create_new(format!("static/{}", pdf_filename))?;
+ BufWriter::new(file).write_all(&result.pdf.to_vec())?;
+
+ let mut epub_filename = format!("{}.epub", result.name).to_lowercase();
+ epub_filename.retain(|c| !c.is_whitespace());
+
+ let file = std::fs::File::create_new(format!("static/{}", epub_filename))?;
+ BufWriter::new(file).write_all(&result.epub.to_vec())?;
+ }
+ Ok(())
+}
+
#[async_trait]
impl ProductDatabaseHandler for Story {
async fn get_all(pool: &SqlitePool) -> Result<Vec<Self>, AppError>
diff --git a/src/handlers/admin.rs b/src/handlers/admin.rs
index af7aa42..6cfe133 100644
--- a/src/handlers/admin.rs
+++ b/src/handlers/admin.rs
@@ -1,4 +1,5 @@
use std::collections::HashMap;
+use std::io::{BufWriter, Write};
use anyhow::Context;
use axum::body::Bytes;
@@ -94,12 +95,22 @@ pub async fn upload_story(
};
let pdf = if let Some(pdf_value) = form.get("pdf") {
+ let mut filename = format!("{}.pdf", name).to_lowercase();
+ filename.retain(|c| !c.is_whitespace());
+
+ let file = std::fs::File::create_new(format!("static/{}", filename))?;
+ BufWriter::new(file).write_all(pdf_value)?;
pdf_value.to_vec()
} else {
return Ok(StatusCode::BAD_REQUEST.into_response());
};
let epub = if let Some(epub_value) = form.get("epub") {
+ let mut filename = format!("{}.epub", name).to_lowercase();
+ filename.retain(|c| !c.is_whitespace());
+
+ let file = std::fs::File::create_new(format!("static/{}", filename))?;
+ BufWriter::new(file).write_all(epub_value)?;
epub_value.to_vec()
} else {
return Ok(StatusCode::BAD_REQUEST.into_response());
diff --git a/src/main.rs b/src/main.rs
index ffdce27..dfe1447 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,6 +3,8 @@ use std::env;
use dotenvy::dotenv;
use sqlx::sqlite::SqlitePoolOptions;
+use self::db::story;
+
pub mod db;
mod error;
mod handlers;
@@ -23,6 +25,10 @@ async fn main() {
.await
.expect("Failed to run migrations");
+ story::synchronize_story_files(&pool)
+ .await
+ .expect("Failed to synchronize files");
+
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, handlers::router(pool).await.into_make_service())
.await