Compare commits
10 commits
8162f81a57
...
1ae83668ee
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1ae83668ee | ||
![]() |
107ad5dfa5 | ||
![]() |
be6b86ee28 | ||
![]() |
28f571e02b | ||
![]() |
0e50b214ed | ||
![]() |
307419d37f | ||
![]() |
ee9e682d9e | ||
![]() |
1b98cbac1a | ||
![]() |
f5e7e0a36e | ||
![]() |
5548e37234 |
12 changed files with 533 additions and 8 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1 +1,4 @@
|
|||
/target
|
||||
gunnhildr.db
|
||||
gunnhildr.db-shm
|
||||
gunnhildr.db-wal
|
138
Cargo.lock
generated
138
Cargo.lock
generated
|
@ -243,6 +243,21 @@ 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"
|
||||
|
@ -382,6 +397,12 @@ 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"
|
||||
|
@ -426,6 +447,20 @@ 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"
|
||||
|
@ -464,6 +499,12 @@ 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"
|
||||
|
@ -822,6 +863,7 @@ name = "gunnhildr"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"chrono",
|
||||
"env_logger",
|
||||
"figment",
|
||||
"log",
|
||||
|
@ -942,6 +984,29 @@ 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"
|
||||
|
@ -1124,6 +1189,16 @@ 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"
|
||||
|
@ -2282,6 +2357,60 @@ 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"
|
||||
|
@ -2292,6 +2421,15 @@ 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"
|
||||
|
|
|
@ -5,9 +5,10 @@ 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"]}
|
||||
sqlx = {version="0.8.2", features=["runtime-tokio","postgres", "sqlite"]}
|
||||
tokio = "1.42.0"
|
||||
|
|
25
migrations/sqlite/20241207082654_initial_table_setup.sql
Normal file
25
migrations/sqlite/20241207082654_initial_table_setup.sql
Normal file
|
@ -0,0 +1,25 @@
|
|||
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
|
||||
);
|
46
src/api.rs
Normal file
46
src/api.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
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<BookForm>, db: web::Data<DbInterface>) -> 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<UserForm>, db: web::Data<DbInterface>) -> String {
|
||||
todo!()
|
||||
}
|
|
@ -5,9 +5,11 @@ use figment::{
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Deserialize, Serialize, Clone, Copy)]
|
||||
pub struct Config {
|
||||
/// Ip address that the gunnhildr should bind to
|
||||
pub binding_ip: IpAddr,
|
||||
/// Port that gunnhildr should listen on
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
|
@ -20,6 +22,7 @@ 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"))
|
||||
|
|
151
src/db/mod.rs
Normal file
151
src/db/mod.rs
Normal file
|
@ -0,0 +1,151 @@
|
|||
#![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<Sqlite>),
|
||||
/// Used for Postgres database
|
||||
Postgres(sqlx::Pool<Postgres>),
|
||||
}
|
||||
|
||||
/// 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<sqlx::Error> for DbError {
|
||||
fn from(value: sqlx::Error) -> Self {
|
||||
match value {
|
||||
sqlx::Error::RowNotFound => DbError::NotFound,
|
||||
_ => DbError::SqlxError(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
type DbResult<T> = Result<T, DbError>;
|
||||
|
||||
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<models::Book> {
|
||||
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<u32> {
|
||||
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<models::Chapter> {
|
||||
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<u32> {
|
||||
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<models::User> {
|
||||
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<u32> {
|
||||
match self {
|
||||
DbInterface::Sqlite(pool) => sqlite::sqlite_user_create(pool, name).await,
|
||||
DbInterface::Postgres(pool) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
21
src/db/models.rs
Normal file
21
src/db/models.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
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,
|
||||
}
|
1
src/db/postgres.rs
Normal file
1
src/db/postgres.rs
Normal file
|
@ -0,0 +1 @@
|
|||
//! Module containing database code for Postgres
|
99
src/db/sqlite.rs
Normal file
99
src/db/sqlite.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
//! 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<Sqlite>, id: u32) -> DbResult<Book> {
|
||||
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<Sqlite>,
|
||||
title: &String,
|
||||
description: &String,
|
||||
author_id: u32,
|
||||
) -> DbResult<u32> {
|
||||
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<Sqlite>, id: u32) -> DbResult<Chapter> {
|
||||
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<Sqlite>,
|
||||
title: &String,
|
||||
text: &String,
|
||||
book_id: u32,
|
||||
author_id: u32,
|
||||
) -> DbResult<u32> {
|
||||
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<Sqlite>, id: u32) -> DbResult<User> {
|
||||
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<Sqlite>, name: &String) -> DbResult<u32> {
|
||||
let id = sqlx::query("INSERT INTO users (name) VALUES ( ? )")
|
||||
.bind(name)
|
||||
.execute(pool)
|
||||
.await?
|
||||
.last_insert_rowid() as u32;
|
||||
Ok(id)
|
||||
}
|
25
src/main.rs
25
src/main.rs
|
@ -1,19 +1,32 @@
|
|||
use actix_web::{get, App, HttpServer};
|
||||
use actix_web::{get, web, App, HttpServer};
|
||||
use log::info;
|
||||
|
||||
mod api;
|
||||
mod config;
|
||||
mod db;
|
||||
mod reading;
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> Result<(), std::io::Error> {
|
||||
env_logger::init();
|
||||
// init env logger
|
||||
env_logger::builder()
|
||||
.filter_level(log::LevelFilter::Info)
|
||||
.init();
|
||||
|
||||
let config = config::parse_config();
|
||||
let db = db::DbInterface::sqlite().await;
|
||||
|
||||
info!("Server starting...");
|
||||
HttpServer::new(|| App::new().service(hello))
|
||||
.bind((config.binding_ip, config.port))?
|
||||
.run()
|
||||
.await
|
||||
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
|
||||
}
|
||||
|
||||
#[get("/hello")]
|
||||
|
|
24
src/reading/mod.rs
Normal file
24
src/reading/mod.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
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<u32>, db: web::Data<DbInterface>) -> Option<String> {
|
||||
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<u32>, db: web::Data<DbInterface>) -> Option<String> {
|
||||
let chapter = db.get_chapter(*id).await.ok()?;
|
||||
Some(format!("Text: {}", chapter.text))
|
||||
}
|
Loading…
Add table
Reference in a new issue