diff --git a/.gitignore b/.gitignore index d9811f6..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1 @@ /target -gunnhildr.db -gunnhildr.db-shm -gunnhildr.db-wal \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 0833cd2..13414d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -243,21 +243,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anstream" version = "0.6.18" @@ -397,12 +382,6 @@ dependencies = [ "alloc-stdlib", ] -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - [[package]] name = "bytemuck" version = "1.20.0" @@ -447,20 +426,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-targets 0.52.6", -] - [[package]] name = "colorchoice" version = "1.0.3" @@ -499,12 +464,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - [[package]] name = "cpufeatures" version = "0.2.16" @@ -863,7 +822,6 @@ name = "gunnhildr" version = "0.1.0" dependencies = [ "actix-web", - "chrono", "env_logger", "figment", "log", @@ -984,29 +942,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "icu_collections" version = "1.5.0" @@ -1189,16 +1124,6 @@ dependencies = [ "libc", ] -[[package]] -name = "js-sys" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - [[package]] name = "language-tags" version = "0.3.2" @@ -2357,60 +2282,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" -[[package]] -name = "wasm-bindgen" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" - [[package]] name = "whoami" version = "1.5.2" @@ -2421,15 +2292,6 @@ dependencies = [ "wasite", ] -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index bfc9501..30d497e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,10 +5,9 @@ edition = "2021" [dependencies] actix-web = {version="4.9.0"} -chrono = "0.4.38" env_logger = "0.11.5" figment = {version="0.10.19", features=["env"]} log = "0.4.22" serde = {version="1.0.215", features=["derive"]} -sqlx = {version="0.8.2", features=["runtime-tokio","postgres", "sqlite"]} +sqlx = {version="0.8.2", features=["runtime-tokio"]} tokio = "1.42.0" diff --git a/migrations/sqlite/20241207082654_initial_table_setup.sql b/migrations/sqlite/20241207082654_initial_table_setup.sql deleted file mode 100644 index 4f90b11..0000000 --- a/migrations/sqlite/20241207082654_initial_table_setup.sql +++ /dev/null @@ -1,25 +0,0 @@ -CREATE TABLE IF NOT EXISTS users( - user_id INTEGER PRIMARY KEY, - name TEXT NOT NULL -); - -CREATE TABLE IF NOT EXISTS books( - book_id INTEGER PRIMARY KEY, - book_title TEXT NOT NULL, - book_description TEXT, - book_creation_date INT NOT NULL, - author_id INTEGER NOT NULL, - FOREIGN KEY (author_id) REFERENCES users(user_id) ON DELETE CASCADE - -); - -CREATE TABLE IF NOT EXISTS chapters( - chapter_id INTEGER PRIMARY KEY, - chapter_title TEXT NOT NULL, - chapter_text TEXT NOT NULL, - chapter_creation_date INT NOT NULL, - book_id INTEGER NOT NULL, - author_id INTEGER NOT NULL, - FOREIGN KEY (book_id) REFERENCES books(book_id) ON DELETE CASCADE, - FOREIGN KEY (author_id) REFERENCES users(user_id) ON DELETE CASCADE -); \ No newline at end of file diff --git a/src/api.rs b/src/api.rs deleted file mode 100644 index 2d07234..0000000 --- a/src/api.rs +++ /dev/null @@ -1,46 +0,0 @@ -use actix_web::{ - get, - web::{self, Json, Redirect}, - Scope, -}; -use serde::Deserialize; - -use crate::db::DbInterface; - -pub fn api_scope() -> Scope { - web::scope("/api") - .service(create_book) - .service(create_chapter) - .service(create_user) -} - -#[derive(Deserialize)] -struct BookForm { - title: String, - description: String, -} - -#[get("/create/book")] -async fn create_book(Json(form): Json, db: web::Data) -> Redirect { - let id = db - .create_book(&form.title, &form.description, todo!()) - .await - .unwrap(); - - Redirect::to(format!("r/b/{}", id)).permanent() -} - -#[get("/create/chapter")] -async fn create_chapter() -> String { - todo!() -} - -#[derive(Deserialize)] -struct UserForm { - name: String, -} - -#[get("/create/user")] -async fn create_user(web::Form(form): web::Form, db: web::Data) -> String { - todo!() -} diff --git a/src/config.rs b/src/config.rs index f5dc496..88637bd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,11 +5,9 @@ use figment::{ use serde::{Deserialize, Serialize}; use std::net::{IpAddr, Ipv4Addr}; -#[derive(Deserialize, Serialize, Clone, Copy)] +#[derive(Deserialize, Serialize)] pub struct Config { - /// Ip address that the gunnhildr should bind to pub binding_ip: IpAddr, - /// Port that gunnhildr should listen on pub port: u16, } @@ -22,7 +20,6 @@ impl Default for Config { } } -/// Parse and merge all config sources pub fn parse_config() -> Config { Figment::from(Serialized::defaults(Config::default())) .merge(Env::prefixed("HILDR")) diff --git a/src/db/mod.rs b/src/db/mod.rs deleted file mode 100644 index c058993..0000000 --- a/src/db/mod.rs +++ /dev/null @@ -1,151 +0,0 @@ -#![allow(unused)] -use log::{info, warn}; -use sqlx::{migrate::MigrateDatabase, PgPool, Postgres, Sqlite, SqlitePool}; - -pub mod models; -mod postgres; -mod sqlite; - -/// Utility for interacting with the database -#[derive(Clone)] -pub enum DbInterface { - /// Used for Sqlite database - Sqlite(sqlx::Pool), - /// Used for Postgres database - Postgres(sqlx::Pool), -} - -/// Error type for handling DB related errors -#[derive(Debug)] -pub enum DbError { - /// No such entry found - NotFound, - /// Database related error - SqlxError(sqlx::Error), -} - -impl From for DbError { - fn from(value: sqlx::Error) -> Self { - match value { - sqlx::Error::RowNotFound => DbError::NotFound, - _ => DbError::SqlxError(value), - } - } -} -type DbResult = Result; - -impl DbInterface { - /// Database backed by SQLite - pub async fn sqlite() -> Self { - // Check if db exists, if not create it. - if !sqlx::Sqlite::database_exists("sqlite:gunnhildr.db") - .await - .expect("failed to connect to db") - { - warn!("No SQLite database found, if this is the first time you are starting Gunnhildr, you can safely ignore this."); - sqlx::Sqlite::create_database("sqlite:gunnhildr.db") - .await - .expect("failed to create SQLite Database"); - info!("Created new SQLite Database"); - } - - let pool = SqlitePool::connect("sqlite:gunnhildr.db").await.unwrap(); - - // run migrations - sqlx::migrate!("migrations/sqlite") - .run(&pool) - .await - .expect("Failed to apply migration!"); - info!("Applied migrations."); - - Self::Sqlite(pool) - } - - /// Database backed by Postgres - pub async fn postgres(url: &str) -> Self { - // check if database exists and create one if not - if !sqlx::Postgres::database_exists(url) - .await - .expect("failed to connect to db") - { - warn!("No Postgres database found, if this is the first time you are starting Gunnhildr, you can safely ignore this."); - sqlx::Postgres::create_database(url) - .await - .expect("failed to create Postgres Database!"); - info!("Created new Postgres Database"); - } - - let pool = PgPool::connect("url").await.unwrap(); - - // run migrations - sqlx::migrate!("migrations/postgres") - .run(&pool) - .await - .expect("Failed to apply migration!"); - - Self::Postgres(pool) - } - - /// Tries to fetch a book from the database - pub async fn get_book(&self, id: u32) -> DbResult { - match self { - DbInterface::Sqlite(pool) => sqlite::sqlite_book(pool, id).await, - DbInterface::Postgres(pool) => todo!(), - } - } - - /// Tries to create a book and returns the book's id if successful - pub async fn create_book( - &self, - title: &String, - description: &String, - author_id: u32, - ) -> DbResult { - match self { - DbInterface::Sqlite(pool) => { - sqlite::sqlite_book_create(pool, title, description, author_id).await - } - DbInterface::Postgres(pool) => todo!(), - } - } - - /// Tries to fetch a chapter from the database - pub async fn get_chapter(&self, id: u32) -> DbResult { - match self { - DbInterface::Sqlite(pool) => sqlite::sqlite_chapter(pool, id).await, - DbInterface::Postgres(pool) => todo!(), - } - } - - /// Tries to create a chapter and returns the chapter's id if successful - pub async fn create_chapter( - &self, - title: &String, - text: &String, - book_id: u32, - author_id: u32, - ) -> DbResult { - match self { - DbInterface::Sqlite(pool) => { - sqlite::sqlite_chapter_create(pool, title, text, book_id, author_id).await - } - DbInterface::Postgres(pool) => todo!(), - } - } - - /// Tries to fetch a user from the database - pub async fn get_user(&self, id: u32) -> DbResult { - match self { - DbInterface::Sqlite(pool) => sqlite::sqlite_user(pool, id).await, - DbInterface::Postgres(pool) => todo!(), - } - } - - /// Tries to create a user and returns the user's id if successful - pub async fn create_user(&self, name: &String) -> DbResult { - match self { - DbInterface::Sqlite(pool) => sqlite::sqlite_user_create(pool, name).await, - DbInterface::Postgres(pool) => todo!(), - } - } -} diff --git a/src/db/models.rs b/src/db/models.rs deleted file mode 100644 index 51feae0..0000000 --- a/src/db/models.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub struct Book { - pub id: u32, - pub title: String, - pub description: String, - pub creation_date: String, - pub author_id: u32, -} - -pub struct Chapter { - pub id: u32, - pub title: String, - pub text: String, - pub creation_date: String, - pub book_id: u32, - pub author_id: u32, -} - -pub struct User { - pub id: u32, - pub name: String, -} diff --git a/src/db/postgres.rs b/src/db/postgres.rs deleted file mode 100644 index 624925e..0000000 --- a/src/db/postgres.rs +++ /dev/null @@ -1 +0,0 @@ -//! Module containing database code for Postgres diff --git a/src/db/sqlite.rs b/src/db/sqlite.rs deleted file mode 100644 index 1f52e46..0000000 --- a/src/db/sqlite.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! Module containing database code for SQLite -use super::models::{Book, Chapter, User}; -use super::{DbError, DbResult}; -use serde::de; -use sqlx::{Pool, Row, Sqlite}; - -pub async fn sqlite_book(pool: &Pool, id: u32) -> DbResult { - let row = sqlx::query("SELECT * FROM users WHERE user_id = ?") - .bind(id) - .fetch_one(pool) - .await?; - - let book = Book { - id, - title: row.get("book_title"), - description: row.get("book_description"), - creation_date: row.get("book_creation_date"), - author_id: row.get("author_id"), - }; - - Ok(book) -} - -pub async fn sqlite_book_create( - pool: &Pool, - title: &String, - description: &String, - author_id: u32, -) -> DbResult { - let id = sqlx::query("INSERT INTO books (title, description, author_id, book_creation_date) VALUES ( ?1, ?2, ?3, ?4 )") - .bind(title) - .bind(description) - .bind(author_id) - .bind(chrono::Local::now().timestamp()) - .execute(pool) - .await? - .last_insert_rowid() as u32; - Ok(id) -} - -pub async fn sqlite_chapter(pool: &Pool, id: u32) -> DbResult { - let row = sqlx::query("SELECT * FROM chapters WHERE chapter_id = ?") - .bind(id) - .fetch_one(pool) - .await?; - - let chapter = Chapter { - id, - title: row.get("chapter_title"), - text: row.get("chapter_text"), - creation_date: row.get("chapter_creation_date"), - book_id: row.get("book_id"), - author_id: row.get("author_id"), - }; - Ok(chapter) -} - -pub async fn sqlite_chapter_create( - pool: &Pool, - title: &String, - text: &String, - book_id: u32, - author_id: u32, -) -> DbResult { - let id = sqlx::query("INSERT INTO chapters (chapter_title, chapter_text, book_id, author_id, chapter_creation_date) VALUES ( ?1, ?2, ?3, ?4, ?5 )") - .bind(title) - .bind(text) - .bind(book_id) - .bind(author_id) - .bind(chrono::Local::now().timestamp()) - .execute(pool) - .await? - .last_insert_rowid() as u32; - - Ok(id) -} - -pub async fn sqlite_user(pool: &Pool, id: u32) -> DbResult { - let row = sqlx::query("SELECT * FROM users WHERE user_id = ?") - .bind(id) - .fetch_one(pool) - .await?; - - let user = User { - id, - name: row.get("name"), - }; - - Ok(user) -} - -pub async fn sqlite_user_create(pool: &Pool, name: &String) -> DbResult { - let id = sqlx::query("INSERT INTO users (name) VALUES ( ? )") - .bind(name) - .execute(pool) - .await? - .last_insert_rowid() as u32; - Ok(id) -} diff --git a/src/main.rs b/src/main.rs index 3470a57..5a8fc1e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,32 +1,19 @@ -use actix_web::{get, web, App, HttpServer}; +use actix_web::{get, App, HttpServer}; use log::info; -mod api; mod config; -mod db; -mod reading; #[actix_web::main] async fn main() -> Result<(), std::io::Error> { - // init env logger - env_logger::builder() - .filter_level(log::LevelFilter::Info) - .init(); + env_logger::init(); let config = config::parse_config(); - let db = db::DbInterface::sqlite().await; info!("Server starting..."); - HttpServer::new(move || { - App::new() - .app_data(web::Data::new(config)) - .app_data(web::Data::new(db.clone())) - .service(reading::reading_scope()) - .service(hello) - }) - .bind((config.binding_ip, config.port))? - .run() - .await + HttpServer::new(|| App::new().service(hello)) + .bind((config.binding_ip, config.port))? + .run() + .await } #[get("/hello")] diff --git a/src/reading/mod.rs b/src/reading/mod.rs deleted file mode 100644 index f6bdc3c..0000000 --- a/src/reading/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -use actix_web::{get, web, Scope}; - -use crate::db::DbInterface; - -/// scope to handle all reading related pages -pub fn reading_scope() -> Scope { - web::scope("/r").service(book_view).service(chapter_view) -} - -/// route to view info for a specific book -#[get("/b/{book}")] -async fn book_view(book: web::Path, db: web::Data) -> Option { - Some(format!( - "This is the info for {}", - db.get_book(*book).await.ok()?.title - )) -} - -/// view for reading a chapter -#[get("/c/{chapter})")] -async fn chapter_view(id: web::Path, db: web::Data) -> Option { - let chapter = db.get_chapter(*id).await.ok()?; - Some(format!("Text: {}", chapter.text)) -}