From af3dfba51087059ab46447a321d6ad1bcdfc87fb Mon Sep 17 00:00:00 2001 From: alzalia1 Date: Tue, 23 Sep 2025 21:51:00 +0200 Subject: [PATCH] feat: added backend for site comments --- .gitignore | 1 + Cargo.lock | 231 ++++++++++++++++++ Cargo.toml | 1 + diesel.toml | 9 + migrations/.diesel_lock | 0 migrations/.keep | 0 .../down.sql | 2 + .../up.sql | 6 + src/comments.rs | 65 +++++ src/main.rs | 12 +- src/models.rs | 25 ++ src/schema.rs | 9 + 12 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 diesel.toml create mode 100644 migrations/.diesel_lock create mode 100644 migrations/.keep create mode 100644 migrations/2025-09-23-083540-0000_create_comments/down.sql create mode 100644 migrations/2025-09-23-083540-0000_create_comments/up.sql create mode 100644 src/comments.rs create mode 100644 src/models.rs create mode 100644 src/schema.rs diff --git a/.gitignore b/.gitignore index e3990da..0f76b24 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /res .env +database.db diff --git a/Cargo.lock b/Cargo.lock index 759ecc4..5bfb703 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,6 +7,7 @@ name = "academic-back" version = "0.1.0" dependencies = [ "axum", + "diesel", "dotenv", "reqwest", "serde", @@ -168,6 +169,85 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "diesel" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8496eeb328dce26ee9d9b73275d396d9bddb433fa30106cf6056dd8c3c2764c" +dependencies = [ + "diesel_derives", + "downcast-rs", + "libsqlite3-sys", + "sqlite-wasm-rs", + "time", +] + +[[package]] +name = "diesel_derives" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09af0e983035368439f1383011cd87c46f41da81d0f21dc3727e2857d5a43c8e" +dependencies = [ + "diesel_table_macro_syntax", + "dsl_auto_type", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe2444076b48641147115697648dc743c2c00b61adade0f01ce67133c7babe8c" +dependencies = [ + "syn", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -185,6 +265,32 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "downcast-rs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" + +[[package]] +name = "dsl_auto_type" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd122633e4bef06db27737f21d3738fb89c8f6d5360d6d9d7635dda142a7757e" +dependencies = [ + "darling", + "either", + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -252,6 +358,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fragile" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" + [[package]] name = "futures-channel" version = "0.3.31" @@ -345,6 +457,12 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "http" version = "1.3.1" @@ -558,6 +676,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.1.0" @@ -638,6 +762,16 @@ version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +[[package]] +name = "libsqlite3-sys" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" +dependencies = [ + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -721,6 +855,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "object" version = "0.36.7" @@ -836,6 +976,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro2" version = "1.0.101" @@ -1116,12 +1262,36 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "sqlite-wasm-rs" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894a1b91dc660fbf1e6ea6f287562708e01ca1a18fa4e2c6dae0df5a05199c5" +dependencies = [ + "fragile", + "js-sys", + "once_cell", + "parking_lot", + "thiserror", + "tokio", + "wasm-array-cp", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.6.1" @@ -1193,6 +1363,57 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinystr" version = "0.8.1" @@ -1399,6 +1620,16 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasm-array-cp" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb633b3e235f0ebe0a35162adc1e0293fc4b7e3f3a6fc7b5374d80464267ff84" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" diff --git a/Cargo.toml b/Cargo.toml index da3dd7c..40ac24c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,4 @@ serde_json = "1.0.143" tokio = { version = "1.47.1", features = ["full"] } tower = "0.5.2" tower-http = { version = "0.6.6", features = ["cors"]} +diesel = { version = "2.2.0", features = ["sqlite", "returning_clauses_for_sqlite_3_35"] } diff --git a/diesel.toml b/diesel.toml new file mode 100644 index 0000000..30e724f --- /dev/null +++ b/diesel.toml @@ -0,0 +1,9 @@ +# For documentation on how to configure this file, +# see https://diesel.rs/guides/configuring-diesel-cli + +[print_schema] +file = "src/schema.rs" +custom_type_derives = ["diesel::query_builder::QueryId", "Clone"] + +[migrations_directory] +dir = "/home/alzalia/Documents/gits/academic-back/migrations" diff --git a/migrations/.diesel_lock b/migrations/.diesel_lock new file mode 100644 index 0000000..e69de29 diff --git a/migrations/.keep b/migrations/.keep new file mode 100644 index 0000000..e69de29 diff --git a/migrations/2025-09-23-083540-0000_create_comments/down.sql b/migrations/2025-09-23-083540-0000_create_comments/down.sql new file mode 100644 index 0000000..65c4573 --- /dev/null +++ b/migrations/2025-09-23-083540-0000_create_comments/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE comments diff --git a/migrations/2025-09-23-083540-0000_create_comments/up.sql b/migrations/2025-09-23-083540-0000_create_comments/up.sql new file mode 100644 index 0000000..d324707 --- /dev/null +++ b/migrations/2025-09-23-083540-0000_create_comments/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here +CREATE TABLE comments ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + author TEXT NOT NULL, + content TEXT NOT NULL +); diff --git a/src/comments.rs b/src/comments.rs new file mode 100644 index 0000000..f4005fe --- /dev/null +++ b/src/comments.rs @@ -0,0 +1,65 @@ +use crate::models::{Comment, CreateCommentRequest, NewComment}; +use axum::Json; +use diesel::prelude::*; +use reqwest::StatusCode; + +/* +* ================= +* ====< UTILS >==== +* ================= +*/ + +pub fn establish_connection() -> Result { + let db_url = dotenv::var("DATABASE_URL").map_err(|_| { + diesel::result::ConnectionError::BadConnection("DATABASE_URL not set".into()) + })?; + SqliteConnection::establish(&db_url) +} + +pub fn create_comment( + conn: &mut SqliteConnection, + author: &str, + content: &str, +) -> Result { + use crate::schema::comments; + + let new_comment = NewComment { author, content }; + + diesel::insert_into(comments::table) + .values(&new_comment) + .returning(Comment::as_returning()) + .get_result(conn) +} + +pub fn fetch_comments(conn: &mut SqliteConnection) -> Result, diesel::result::Error> { + use crate::schema::comments::dsl::*; + + comments.load::(conn) +} + +/* +* ================== +* ====< ROUTES >==== +* ================== +*/ + +pub async fn post_comment( + Json(payload): Json, +) -> (StatusCode, Json>) { + if let Ok(mut conn) = establish_connection() { + let new_comment = create_comment(&mut conn, &payload.author, &payload.content).unwrap(); + (StatusCode::OK, Json(Some(new_comment))) + } else { + (StatusCode::INTERNAL_SERVER_ERROR, Json(None)) + } +} + +pub async fn get_comments() -> (StatusCode, Json>>) { + if let Ok(mut conn) = establish_connection() + && let Ok(comments) = fetch_comments(&mut conn) + { + (StatusCode::OK, Json(Some(comments))) + } else { + (StatusCode::INTERNAL_SERVER_ERROR, Json(None)) + } +} diff --git a/src/main.rs b/src/main.rs index 0f114ce..cb26c15 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,19 @@ use axum::{ Router, http::{Method, header}, - routing::get, + routing::{get, post}, }; use tower_http::cors::CorsLayer; -use crate::json_routes::{get_home, get_lessons, get_lessons_by_id, get_projects}; +use crate::{ + comments::{get_comments, post_comment}, + json_routes::{get_home, get_lessons, get_lessons_by_id, get_projects}, +}; +mod comments; mod json_routes; +pub mod models; +pub mod schema; #[tokio::main] async fn main() { @@ -31,6 +37,8 @@ async fn main() { .route("/{lang}/projects", get(get_projects)) .route("/{lang}/lessons", get(get_lessons)) .route("/{lang}/lessons/{id}", get(get_lessons_by_id)) + .route("/comments", get(get_comments)) + .route("/comment/create", post(post_comment)) .layer(cors); // run our app on port 6543 diff --git a/src/models.rs b/src/models.rs new file mode 100644 index 0000000..53f653a --- /dev/null +++ b/src/models.rs @@ -0,0 +1,25 @@ +use crate::schema::comments; +use diesel::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Queryable, Selectable, Serialize)] +#[diesel(table_name = comments)] +#[diesel(check_for_backend(diesel::sqlite::Sqlite))] +pub struct Comment { + pub id: i32, + pub author: String, + pub content: String, +} + +#[derive(Insertable, Serialize, Deserialize)] +#[diesel(table_name = comments)] +pub struct NewComment<'a> { + pub author: &'a str, + pub content: &'a str, +} + +#[derive(Deserialize)] +pub struct CreateCommentRequest { + pub author: String, + pub content: String, +} diff --git a/src/schema.rs b/src/schema.rs new file mode 100644 index 0000000..d4a79a6 --- /dev/null +++ b/src/schema.rs @@ -0,0 +1,9 @@ +// @generated automatically by Diesel CLI. + +diesel::table! { + comments (id) { + id -> Integer, + author -> Text, + content -> Text, + } +}