diff --git a/examples/guest-side/Cargo.toml b/examples/guest-side/Cargo.toml index 0e839c8..393b357 100644 --- a/examples/guest-side/Cargo.toml +++ b/examples/guest-side/Cargo.toml @@ -15,7 +15,7 @@ anyhow = "^1" tokio = { version = "^1", features = ["macros", "rt", "time"] } sqlparser = "^0.45" -sea-orm = { version = "1.0.0-rc.4", features = ["proxy"] } +sea-orm = { version = "1.0.0-rc.7", features = ["proxy"] } yew = { version = "^0.21", features = ["ssr"] } yew-router = "^0.18" diff --git a/examples/host-side/Cargo.toml b/examples/host-side/Cargo.toml index c20afe4..738c2d1 100644 --- a/examples/host-side/Cargo.toml +++ b/examples/host-side/Cargo.toml @@ -17,5 +17,5 @@ serde_json = "^1" async-std = { version = "^1", features = ["attributes", "tokio1"] } uuid = "^1" -sea-orm = { version = "1.0.0-rc.4", features = ["proxy"] } +sea-orm = { version = "1.0.0-rc.7", features = ["proxy"] } gluesql = { version = "^0.15" } diff --git a/packages/database/Cargo.toml b/packages/database/Cargo.toml index a6c1a73..b696df4 100644 --- a/packages/database/Cargo.toml +++ b/packages/database/Cargo.toml @@ -22,17 +22,27 @@ uuid = { version = "^1", features = [ 'serde', ] } -sea-orm-migration = "1.0.0-rc.4" +sea-orm-migration = "1.0.0-rc.7" serde = { version = "^1", features = ["derive"] } serde_json = "^1" async-std = { version = "^1", features = ["attributes", "tokio1"] } + [dependencies.sea-orm] -features = [ - "sqlx-mysql", - "runtime-async-std-rustls", - "with-uuid", - "with-chrono", - "with-json", +features = ["with-uuid", "with-chrono", "with-json"] +version = "1.0.0-rc.7" + +[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies] +wasm-bindgen = { version = "0.2.87", optional = true } +wasm-bindgen-futures = { version = "^0.4", optional = true } +worker = { version = "0.3.0", features = ["d1"], optional = true } + +[features] +default = ["cloudflare"] +cloudflare = [ + "dep:wasm-bindgen", + "dep:wasm-bindgen-futures", + "dep:worker", + "sea-orm/proxy", + "sea-orm/sqlx-sqlite", ] -version = "1.0.0-rc.4" diff --git a/packages/database/src/functions/mod.rs b/packages/database/src/functions/mod.rs deleted file mode 100644 index 22d12a3..0000000 --- a/packages/database/src/functions/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod user; diff --git a/packages/database/src/functions/user/count.rs b/packages/database/src/functions/user/count.rs deleted file mode 100644 index f4bba9a..0000000 --- a/packages/database/src/functions/user/count.rs +++ /dev/null @@ -1,9 +0,0 @@ -use anyhow::Result; - -use sea_orm::{EntityTrait, PaginatorTrait}; - -use crate::{models::user::Entity, DB_CONN}; - -pub async fn count() -> Result { - Ok(Entity::find().count(DB_CONN.lock().await.get_mut()).await?) -} diff --git a/packages/database/src/functions/user/delete.rs b/packages/database/src/functions/user/delete.rs deleted file mode 100644 index 25f336a..0000000 --- a/packages/database/src/functions/user/delete.rs +++ /dev/null @@ -1,15 +0,0 @@ -use anyhow::Result; -use sea_orm::{EntityTrait, ModelTrait}; -use uuid::Uuid; - -use crate::{models::user::Entity, DB_CONN}; - -pub async fn delete(id: Uuid) -> Result<()> { - if let Some(item) = Entity::find_by_id(id) - .one(DB_CONN.lock().await.get_mut()) - .await? - { - item.delete(DB_CONN.lock().await.get_mut()).await?; - } - Ok(()) -} diff --git a/packages/database/src/functions/user/filter_by_name.rs b/packages/database/src/functions/user/filter_by_name.rs deleted file mode 100644 index 7de4bac..0000000 --- a/packages/database/src/functions/user/filter_by_name.rs +++ /dev/null @@ -1,16 +0,0 @@ -use anyhow::Result; -use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; - -use crate::{ - models::user::{Column, Entity, Model}, - DB_CONN, -}; - -pub async fn filter_by_name(name: String) -> Result { - let ret = Entity::find() - .filter(Column::Name.eq(name)) - .one(DB_CONN.lock().await.get_mut()) - .await? - .unwrap(); - Ok(ret) -} diff --git a/packages/database/src/functions/user/insert.rs b/packages/database/src/functions/user/insert.rs deleted file mode 100644 index f0e9b1e..0000000 --- a/packages/database/src/functions/user/insert.rs +++ /dev/null @@ -1,21 +0,0 @@ -use anyhow::Result; - -use sea_orm::{ActiveValue::NotSet, EntityTrait}; - -use crate::{ - models::user::{ActiveModel, Entity, Model}, - DB_CONN, -}; - -pub async fn insert(item: Model) -> Result<()> { - let item = ActiveModel { - id: NotSet, - ..item.into() - }; - - Entity::insert(item) - .exec(DB_CONN.lock().await.get_mut()) - .await?; - - Ok(()) -} diff --git a/packages/database/src/functions/user/list.rs b/packages/database/src/functions/user/list.rs deleted file mode 100644 index ddc3200..0000000 --- a/packages/database/src/functions/user/list.rs +++ /dev/null @@ -1,18 +0,0 @@ -use anyhow::Result; - -use sea_orm::{EntityTrait, QuerySelect}; - -use crate::{ - models::user::{Entity, Model}, - DB_CONN, -}; - -pub async fn list(from: u64, count: u64) -> Result> { - let ret = Entity::find() - .offset(from) - .limit(count) - .all(DB_CONN.lock().await.get_mut()) - .await? - .to_vec(); - Ok(ret) -} diff --git a/packages/database/src/functions/user/mod.rs b/packages/database/src/functions/user/mod.rs deleted file mode 100644 index 10824a5..0000000 --- a/packages/database/src/functions/user/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -mod count; -mod delete; -mod filter_by_name; -mod insert; -mod list; -mod query; -mod update; - -pub use count::count; -pub use delete::delete; -pub use filter_by_name::filter_by_name; -pub use insert::insert; -pub use list::list; -pub use query::query; -pub use update::update; diff --git a/packages/database/src/functions/user/query.rs b/packages/database/src/functions/user/query.rs deleted file mode 100644 index a130944..0000000 --- a/packages/database/src/functions/user/query.rs +++ /dev/null @@ -1,16 +0,0 @@ -use anyhow::Result; -use sea_orm::EntityTrait; -use uuid::Uuid; - -use crate::{ - models::user::{Entity, Model}, - DB_CONN, -}; - -pub async fn query(id: Uuid) -> Result { - let ret = Entity::find_by_id(id) - .one(DB_CONN.lock().await.get_mut()) - .await? - .unwrap(); - Ok(ret) -} diff --git a/packages/database/src/functions/user/update.rs b/packages/database/src/functions/user/update.rs deleted file mode 100644 index 419743c..0000000 --- a/packages/database/src/functions/user/update.rs +++ /dev/null @@ -1,17 +0,0 @@ -use anyhow::Result; - -use sea_orm::{ActiveModelTrait, ActiveValue::NotSet}; - -use crate::{ - models::user::{ActiveModel, Model}, - DB_CONN, -}; - -pub async fn update(item: Model) -> Result<()> { - let model = ActiveModel { - id: NotSet, - ..item.clone().into() - }; - model.update(DB_CONN.lock().await.get_mut()).await?; - Ok(()) -} diff --git a/packages/database/src/lib.rs b/packages/database/src/lib.rs index fb9b261..9ce92fb 100644 --- a/packages/database/src/lib.rs +++ b/packages/database/src/lib.rs @@ -1,101 +1 @@ -pub mod functions; -pub mod models; - -use anyhow::Result; -use async_std::sync::Mutex; -use lazy_static::lazy_static; -use log::info; -use std::{cell::Cell, time::Duration}; - -use sea_orm::{ConnectOptions, ConnectionTrait, Database, DatabaseConnection, Schema, Statement}; - -pub struct DatabaseNetworkConfig { - pub host: String, - pub port: u16, - pub username: String, - pub password: String, - pub database: String, -} - -pub async fn init(config: DatabaseNetworkConfig) -> Result<()> { - // Connect to the database - let mut opt = ConnectOptions::new(format!( - "mysql://{}:{}@{}:{}/mysql", - config.username, config.password, config.host, config.port - )); - opt.max_connections(100) - .min_connections(5) - .connect_timeout(Duration::from_secs(8)) - .acquire_timeout(Duration::from_secs(8)) - .idle_timeout(Duration::from_secs(8)) - .max_lifetime(Duration::from_secs(8)) - .sqlx_logging(true) - .sqlx_logging_level(log::LevelFilter::Trace); - let db = Database::connect(opt).await?; - let builder = db.get_database_backend(); - - // Initialize the database - db.execute(Statement::from_string( - db.get_database_backend(), - format!( - "CREATE DATABASE IF NOT EXISTS {} DEFAULT CHARACTER SET utf8mb4;", - config.database - ), - )) - .await?; - db.execute(Statement::from_string( - db.get_database_backend(), - format!("USE {}", config.database), - )) - .await?; - - db.execute( - builder.build( - Schema::new(builder) - .create_table_from_entity(models::channel::Entity) - .if_not_exists(), - ), - ) - .await?; - db.execute( - builder.build( - Schema::new(builder) - .create_table_from_entity(models::user::Entity) - .if_not_exists(), - ), - ) - .await?; - db.execute( - builder.build( - Schema::new(builder) - .create_table_from_entity(models::tag::Entity) - .if_not_exists(), - ), - ) - .await?; - db.execute( - builder.build( - Schema::new(builder) - .create_table_from_entity(models::thread::Entity) - .if_not_exists(), - ), - ) - .await?; - db.execute( - builder.build( - Schema::new(builder) - .create_table_from_entity(models::post::Entity) - .if_not_exists(), - ), - ) - .await?; - - info!("Database is ready"); - DB_CONN.lock().await.replace(db); - - Ok(()) -} - -lazy_static! { - static ref DB_CONN: Mutex> = Default::default(); -} +pub mod providers; diff --git a/packages/database/src/models/channel.rs b/packages/database/src/models/channel.rs deleted file mode 100644 index 394a701..0000000 --- a/packages/database/src/models/channel.rs +++ /dev/null @@ -1,16 +0,0 @@ -use sea_orm::entity::prelude::*; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] -#[sea_orm(table_name = "channels")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub id: Uuid, - - pub label: String, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/database/src/models/mod.rs b/packages/database/src/models/mod.rs deleted file mode 100644 index 34ae43b..0000000 --- a/packages/database/src/models/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod channel; -pub mod post; -pub mod tag; -pub mod thread; -pub mod user; diff --git a/packages/database/src/models/post.rs b/packages/database/src/models/post.rs deleted file mode 100644 index f211fb3..0000000 --- a/packages/database/src/models/post.rs +++ /dev/null @@ -1,39 +0,0 @@ -use sea_orm::entity::prelude::*; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] -#[sea_orm(table_name = "posts")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub id: Uuid, - - pub parent: Option, - pub author: Uuid, - pub timestamp: ChronoDateTimeUtc, - - pub content: String, -} - -#[derive(Copy, Clone, Debug, EnumIter)] -pub enum Relation { - Thread, -} - -impl RelationTrait for Relation { - fn def(&self) -> RelationDef { - match self { - Self::Thread => Entity::belongs_to(super::thread::Entity) - .from(Column::Parent) - .to(super::thread::Column::Id) - .into(), - } - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Thread.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/database/src/models/tag.rs b/packages/database/src/models/tag.rs deleted file mode 100644 index ec9b38f..0000000 --- a/packages/database/src/models/tag.rs +++ /dev/null @@ -1,37 +0,0 @@ -use sea_orm::entity::prelude::*; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] -#[sea_orm(table_name = "tags")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub id: Uuid, - - pub parent: Option, - - pub label: String, -} - -#[derive(Copy, Clone, Debug, EnumIter)] -pub enum Relation { - Tag, -} - -impl RelationTrait for Relation { - fn def(&self) -> RelationDef { - match self { - Self::Tag => Entity::belongs_to(self::Entity) - .from(Column::Parent) - .to(self::Column::Id) - .into(), - } - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Tag.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/database/src/models/thread.rs b/packages/database/src/models/thread.rs deleted file mode 100644 index f6fabb0..0000000 --- a/packages/database/src/models/thread.rs +++ /dev/null @@ -1,52 +0,0 @@ -use sea_orm::entity::prelude::*; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] -#[sea_orm(table_name = "threads")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub id: Uuid, - - pub channel: Uuid, - pub tags: Json, - pub author: Uuid, - pub timestamp: ChronoDateTimeUtc, - - pub title: String, - pub content: String, -} - -#[derive(Copy, Clone, Debug, EnumIter)] -pub enum Relation { - Channel, - Author, -} - -impl RelationTrait for Relation { - fn def(&self) -> RelationDef { - match self { - Self::Channel => Entity::belongs_to(super::channel::Entity) - .from(Column::Channel) - .to(super::channel::Column::Id) - .into(), - Self::Author => Entity::belongs_to(super::user::Entity) - .from(Column::Author) - .to(super::user::Column::Id) - .into(), - } - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Channel.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Author.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/database/src/models/user.rs b/packages/database/src/models/user.rs deleted file mode 100644 index 3d68442..0000000 --- a/packages/database/src/models/user.rs +++ /dev/null @@ -1,92 +0,0 @@ -use sea_orm::entity::prelude::*; -use serde::{Deserialize, Serialize}; - -use tairitsu_utils::types::functions::{Permission as DTOPermission, UserType as DTO}; - -#[derive(Clone, Debug, PartialEq, EnumIter, DeriveActiveEnum, Deserialize, Serialize)] -#[sea_orm(rs_type = "String", db_type = "String(StringLen::N(16))")] -pub enum Permission { - #[sea_orm(string_value = "root")] - Root, - #[sea_orm(string_value = "manager")] - Manager, - #[sea_orm(string_value = "user")] - User, -} - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)] -#[sea_orm(table_name = "users")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub id: Uuid, - pub token: Uuid, - - pub name: String, - pub password_hash: String, - - pub permission: Permission, -} - -impl Default for Model { - fn default() -> Self { - Self { - id: Uuid::new_v4(), - token: Uuid::nil(), - - name: Default::default(), - password_hash: Default::default(), - - permission: Permission::Manager, - } - } -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} - -impl ActiveModelBehavior for ActiveModel {} - -// Convert between PO and DTO - -impl From for Model { - fn from(info: DTO) -> Self { - Self { - id: info.id, - name: info.name, - permission: info.permission.into(), - - ..Default::default() - } - } -} - -impl From for DTO { - fn from(model: Model) -> Self { - Self { - id: model.id, - - name: model.name, - permission: model.permission.into(), - } - } -} - -impl From for Permission { - fn from(permission: DTOPermission) -> Self { - match permission { - DTOPermission::Root => Self::Root, - DTOPermission::Manager => Self::Manager, - DTOPermission::User => Self::User, - } - } -} - -impl From for DTOPermission { - fn from(permission: Permission) -> Self { - match permission { - Permission::Root => Self::Root, - Permission::Manager => Self::Manager, - Permission::User => Self::User, - } - } -} diff --git a/packages/database/src/providers/cloudflare.rs b/packages/database/src/providers/cloudflare.rs new file mode 100644 index 0000000..f1c0822 --- /dev/null +++ b/packages/database/src/providers/cloudflare.rs @@ -0,0 +1,199 @@ +use anyhow::{anyhow, Context, Result}; +use std::{collections::BTreeMap, sync::Arc}; +use wasm_bindgen::JsValue; + +use sea_orm::{ + Database, DatabaseConnection, DbBackend, DbErr, ProxyDatabaseTrait, ProxyExecResult, ProxyRow, + RuntimeErr, Statement, Value, Values, +}; +use worker::Env; + +struct ProxyDb { + env: Arc, +} + +impl std::fmt::Debug for ProxyDb { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ProxyDb").finish() + } +} + +impl ProxyDb { + async fn do_query(env: Arc, statement: Statement) -> Result> { + let sql = statement.sql.clone(); + let values = match statement.values { + Some(Values(values)) => values + .iter() + .map(|val| match &val { + Value::BigInt(Some(val)) => JsValue::from(val.to_string()), + Value::BigUnsigned(Some(val)) => JsValue::from(val.to_string()), + Value::Int(Some(val)) => JsValue::from(*val), + Value::Unsigned(Some(val)) => JsValue::from(*val), + Value::SmallInt(Some(val)) => JsValue::from(*val), + Value::SmallUnsigned(Some(val)) => JsValue::from(*val), + Value::TinyInt(Some(val)) => JsValue::from(*val), + Value::TinyUnsigned(Some(val)) => JsValue::from(*val), + + Value::Float(Some(val)) => JsValue::from_f64(*val as f64), + Value::Double(Some(val)) => JsValue::from_f64(*val), + + Value::Bool(Some(val)) => JsValue::from(*val), + Value::Bytes(Some(val)) => JsValue::from(format!( + "X'{}'", + val.iter() + .map(|byte| format!("{:02x}", byte)) + .collect::() + )), + Value::Char(Some(val)) => JsValue::from(val.to_string()), + Value::Json(Some(val)) => JsValue::from(val.to_string()), + Value::String(Some(val)) => JsValue::from(val.to_string()), + + Value::ChronoDate(Some(val)) => JsValue::from(val.to_string()), + Value::ChronoDateTime(Some(val)) => JsValue::from(val.to_string()), + Value::ChronoDateTimeLocal(Some(val)) => JsValue::from(val.to_string()), + Value::ChronoDateTimeUtc(Some(val)) => JsValue::from(val.to_string()), + Value::ChronoDateTimeWithTimeZone(Some(val)) => JsValue::from(val.to_string()), + + _ => JsValue::NULL, + }) + .collect(), + None => Vec::new(), + }; + + let ret = env.d1("test-d1")?.prepare(sql).bind(&values)?.all().await?; + if let Some(message) = ret.error() { + return Err(anyhow!(message.to_string())); + } + + let ret = ret.results::()?; + let ret = ret + .iter() + .map(|row| { + let mut values = BTreeMap::new(); + for (key, value) in row.as_object().unwrap() { + values.insert( + key.clone(), + match &value { + serde_json::Value::Bool(val) => Value::Bool(Some(*val)), + serde_json::Value::Number(val) => { + if val.is_i64() { + Value::BigInt(Some(val.as_i64().unwrap())) + } else if val.is_u64() { + Value::BigUnsigned(Some(val.as_u64().unwrap())) + } else { + Value::Double(Some(val.as_f64().unwrap())) + } + } + serde_json::Value::String(val) => { + Value::String(Some(Box::new(val.clone()))) + } + _ => unreachable!("Unsupported JSON value"), + }, + ); + } + ProxyRow { values } + }) + .collect(); + + Ok(ret) + } + + async fn do_execute(env: Arc, statement: Statement) -> Result { + let sql = statement.sql.clone(); + let values = match statement.values { + Some(Values(values)) => values + .iter() + .map(|val| match &val { + Value::BigInt(Some(val)) => JsValue::from(*val), + Value::BigUnsigned(Some(val)) => JsValue::from(*val), + Value::Int(Some(val)) => JsValue::from(*val), + Value::Unsigned(Some(val)) => JsValue::from(*val), + Value::SmallInt(Some(val)) => JsValue::from(*val), + Value::SmallUnsigned(Some(val)) => JsValue::from(*val), + Value::TinyInt(Some(val)) => JsValue::from(*val), + Value::TinyUnsigned(Some(val)) => JsValue::from(*val), + + Value::Float(Some(val)) => JsValue::from_f64(*val as f64), + Value::Double(Some(val)) => JsValue::from_f64(*val), + + Value::Bool(Some(val)) => JsValue::from(*val), + Value::Bytes(Some(val)) => JsValue::from(format!( + "X'{}'", + val.iter() + .map(|byte| format!("{:02x}", byte)) + .collect::() + )), + Value::Char(Some(val)) => JsValue::from(val.to_string()), + Value::Json(Some(val)) => JsValue::from(val.to_string()), + Value::String(Some(val)) => JsValue::from(val.to_string()), + + Value::ChronoDate(Some(val)) => JsValue::from(val.to_string()), + Value::ChronoDateTime(Some(val)) => JsValue::from(val.to_string()), + Value::ChronoDateTimeLocal(Some(val)) => JsValue::from(val.to_string()), + Value::ChronoDateTimeUtc(Some(val)) => JsValue::from(val.to_string()), + Value::ChronoDateTimeWithTimeZone(Some(val)) => JsValue::from(val.to_string()), + + _ => JsValue::NULL, + }) + .collect(), + None => Vec::new(), + }; + + let ret = env + .d1("test-d1")? + .prepare(sql) + .bind(&values)? + .run() + .await? + .meta()?; + + let last_insert_id = ret + .as_ref() + .map(|meta| meta.last_row_id.unwrap_or(0)) + .unwrap_or(0) as u64; + let rows_affected = ret + .as_ref() + .map(|meta| meta.rows_written.unwrap_or(0)) + .unwrap_or(0) as u64; + + Ok(ProxyExecResult { + last_insert_id, + rows_affected, + }) + } +} + +#[async_trait::async_trait] +impl ProxyDatabaseTrait for ProxyDb { + async fn query(&self, statement: Statement) -> Result, DbErr> { + let env = self.env.clone(); + let (tx, rx) = oneshot::channel(); + wasm_bindgen_futures::spawn_local(async move { + let ret = Self::do_query(env, statement).await; + tx.send(ret).unwrap(); + }); + + let ret = rx.await.unwrap(); + ret.map_err(|err| DbErr::Conn(RuntimeErr::Internal(err.to_string()))) + } + + async fn execute(&self, statement: Statement) -> Result { + let env = self.env.clone(); + let (tx, rx) = oneshot::channel(); + wasm_bindgen_futures::spawn_local(async move { + let ret = Self::do_execute(env, statement).await; + tx.send(ret).unwrap(); + }); + + let ret = rx.await.unwrap(); + ret.map_err(|err| DbErr::Conn(RuntimeErr::Internal(err.to_string()))) + } +} + +pub async fn init_db(env: Arc) -> Result { + let db = Database::connect_proxy(DbBackend::Sqlite, Arc::new(Box::new(ProxyDb { env }))) + .await + .context("Failed to connect to database")?; + + Ok(db) +} diff --git a/packages/database/src/providers/mod.rs b/packages/database/src/providers/mod.rs new file mode 100644 index 0000000..935ca7b --- /dev/null +++ b/packages/database/src/providers/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "cloudflare")] +pub mod cloudflare; diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml deleted file mode 100644 index 6fdd817..0000000 --- a/packages/router/Cargo.toml +++ /dev/null @@ -1,57 +0,0 @@ -[package] -authors = ["langyo "] -name = "tairitsu-router" - -version = "0.1.0" -edition = "2021" -publish = false - -[dependencies] -tairitsu-database = { path = "../database" } -tairitsu-utils = { path = "../utils" } - -anyhow = "^1" -async-std = { version = "^1", features = ["attributes", "tokio1"] } -base64 = "^0.22" -chrono = { version = "^0.4", features = ["serde", "unstable-locales"] } -env_logger = "^0.11" -lazy_static = "*" -log = "^0.4" -serde = { version = "^1", features = ["derive"] } -serde_json = "^1" -url = "^2" -tokio = { version = "^1", features = ["full"] } -uuid = { version = "^1", features = [ - 'v4', - 'fast-rng', - 'macro-diagnostics', - 'serde', -] } - -axum = { version = "^0.7", features = ["query"] } -clap = { version = "^4", features = ["derive"] } -futures = { version = "^0.3", features = ["std"], default-features = false } -hyper = { version = "^1", features = ["full"] } -http-body-util = "0.1" -hyper-util = { version = "0.1", features = ["full"] } -tower = { version = "^0.4", features = ["make"] } -tracing = "^0.1" -tracing-subscriber = "^0.3" -stylist = { version = "^0.13", features = ["yew_integration", "ssr"] } -yew = { version = "^0.21", features = ["ssr", "hydration"] } - -[dev-dependencies.web-sys] -features = [ - "Window", - "Document", - "Element", - "HtmlElement", - "HtmlHeadElement", - "HtmlStyleElement", - "CssStyleDeclaration", -] -version = "0.3" - -[dependencies.tower-http] -features = ["fs", "trace", "compression-gzip"] -version = "^0.5" diff --git a/packages/router/res/favicon.ico b/packages/router/res/favicon.ico deleted file mode 100644 index 47f9f6e..0000000 Binary files a/packages/router/res/favicon.ico and /dev/null differ diff --git a/packages/router/res/logo.png b/packages/router/res/logo.png deleted file mode 100644 index 85c6e71..0000000 Binary files a/packages/router/res/logo.png and /dev/null differ diff --git a/packages/router/src/main.rs b/packages/router/src/main.rs deleted file mode 100644 index 598383d..0000000 --- a/packages/router/src/main.rs +++ /dev/null @@ -1,67 +0,0 @@ -mod routes; - -use anyhow::Result; -use log::info; -use tokio::net::TcpListener; - -use axum::serve; -use tower::ServiceBuilder; -use tower_http::{compression::CompressionLayer, trace::TraceLayer}; -use yew::platform::Runtime; - -use crate::routes::route; - -#[derive(Clone, Default)] -struct Executor { - inner: Runtime, -} - -impl hyper::rt::Executor for Executor -where - F: std::future::Future + Send + 'static, -{ - fn execute(&self, fut: F) { - self.inner.spawn_pinned(move || async move { - fut.await; - }); - } -} - -#[async_std::main] -async fn main() -> Result<()> { - env_logger::Builder::new() - .filter(None, log::LevelFilter::Info) - .init(); - - let port = std::env::var("PORT") - .ok() - .and_then(|s| s.parse().ok()) - .unwrap_or(23333); - - tairitsu_database::init(tairitsu_database::DatabaseNetworkConfig { - host: std::env::var("DB_HOST").unwrap_or("localhost".into()), - port: std::env::var("DB_PORT") - .ok() - .and_then(|s| s.parse().ok()) - .unwrap_or(3306), - username: std::env::var("DB_USERNAME").unwrap_or("root".into()), - password: std::env::var("DB_PASSWORD").unwrap_or("root".into()), - database: std::env::var("DB_DATABASE").unwrap_or("hikari".into()), - }) - .await?; - - let middleware_stack = ServiceBuilder::new() - .layer(TraceLayer::new_for_http()) - .layer(CompressionLayer::new()) - .into_inner(); - - let router = route().await?.layer(middleware_stack); - - info!("Site will run on port {port}"); - let listener = TcpListener::bind(format!("0.0.0.0:{port}")) - .await - .expect("Failed to bind"); - serve(listener, router).await?; - - Ok(()) -} diff --git a/packages/router/src/routes/backend/functions/mod.rs b/packages/router/src/routes/backend/functions/mod.rs deleted file mode 100644 index 75d8a38..0000000 --- a/packages/router/src/routes/backend/functions/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -use anyhow::Result; - -use axum::Router; - -mod users; - -pub async fn route() -> Result { - let router = Router::new().nest("/users", users::route().await?); - - Ok(router) -} diff --git a/packages/router/src/routes/backend/functions/users.rs b/packages/router/src/routes/backend/functions/users.rs deleted file mode 100644 index 55d787f..0000000 --- a/packages/router/src/routes/backend/functions/users.rs +++ /dev/null @@ -1,90 +0,0 @@ -use anyhow::Result; -use hyper::StatusCode; -use serde_json::to_string; - -use axum::{routing::post, Json, Router}; - -use crate::routes::utils::{generate_error_message, generate_ok_message}; -use tairitsu_database::functions::user as functions; -use tairitsu_utils::types::proto::{ - frontend::Count, RequestPackage, ResponsePackage, ResponseStruct, -}; - -async fn query(Json(item): Json) -> Result { - let item = match &item { - RequestPackage::Uuid(item) => item.to_owned(), - _ => return Err(generate_error_message("Invalid request".to_string())), - }; - - let ret = functions::query(item.uuid) - .await - .map_err(|e| generate_error_message(e.to_string()))?; - - let ret = ResponsePackage::Data(vec![ResponseStruct::UserInfo(ret.into())]); - to_string(&ret).map_err(|e| generate_error_message(e.to_string())) -} - -async fn count() -> Result { - let ret = functions::count() - .await - .map_err(|e| generate_error_message(e.to_string()))?; - let ret = Count { count: ret }; - - let ret = ResponsePackage::Data(vec![ResponseStruct::Count(ret)]); - to_string(&ret).map_err(|e| generate_error_message(e.to_string())) -} - -async fn list(Json(item): Json) -> Result { - let cond = match &item { - RequestPackage::LimitOffset(item) => item.to_owned(), - _ => return Err(generate_error_message("Invalid request".to_string())), - }; - - let ret = functions::list(cond.offset, cond.limit) - .await - .map_err(|e| generate_error_message(e.to_string()))?; - - let ret = ResponsePackage::Data( - ret.iter() - .map(|item| ResponseStruct::UserInfo(item.clone().into())) - .collect(), - ); - to_string(&ret).map_err(|e| generate_error_message(e.to_string())) -} - -async fn update(Json(item): Json) -> Result { - let item = match &item { - RequestPackage::UserInfo(item) => item.to_owned(), - _ => return Err(generate_error_message("Invalid request".to_string())), - }; - - functions::update(item.into()) - .await - .map_err(|e| generate_error_message(e.to_string()))?; - - generate_ok_message() -} - -async fn delete(Json(item): Json) -> Result { - let item = match &item { - RequestPackage::Uuid(item) => item.to_owned(), - _ => return Err(generate_error_message("Invalid request".to_string())), - }; - - functions::delete(item.uuid) - .await - .map_err(|e| generate_error_message(e.to_string()))?; - - generate_ok_message() -} - -pub async fn route() -> Result { - let router = Router::new() - .route("/count", post(count)) - .route("/query", post(query)) - .route("/list", post(list)) - .route("/update", post(update)) - .route("/delete", post(delete)); - - Ok(router) -} diff --git a/packages/router/src/routes/backend/mod.rs b/packages/router/src/routes/backend/mod.rs deleted file mode 100644 index 3c6f7a5..0000000 --- a/packages/router/src/routes/backend/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -use anyhow::Result; - -use axum::Router; - -mod functions; -mod secure; - -pub async fn route() -> Result { - let router = Router::new() - .nest("/", secure::route().await?) - .nest("/", functions::route().await?); - - Ok(router) -} diff --git a/packages/router/src/routes/backend/secure/login.rs b/packages/router/src/routes/backend/secure/login.rs deleted file mode 100644 index 8306d73..0000000 --- a/packages/router/src/routes/backend/secure/login.rs +++ /dev/null @@ -1,44 +0,0 @@ -use anyhow::Result; -use hyper::StatusCode; -use serde_json::to_string; -use uuid::Uuid; - -use axum::Json; - -use crate::routes::utils::generate_error_message; -use tairitsu_database::functions::user as functions; -use tairitsu_utils::{ - crypt_verify, - types::proto::{ - frontend::UuidData, - { - RequestPackage, RequestPackage::Login as RequestType, ResponsePackage, - ResponseStruct::Token as ResponseType, - }, - }, -}; - -pub async fn login(Json(item): Json) -> Result { - let item = match &item { - RequestType(item) => item.to_owned(), - _ => return Err(generate_error_message("Invalid request".to_string())), - }; - - let mut storage = functions::filter_by_name(item.name) - .await - .map_err(|e| generate_error_message(e.to_string()))?; - if crypt_verify(item.password_hash, storage.password_hash.clone()) - .map_err(|e| generate_error_message(e.to_string()))? - { - let new_token = Uuid::new_v4(); - storage.token = new_token.to_owned(); - functions::update(storage) - .await - .map_err(|e| generate_error_message(e.to_string()))?; - - let ret = ResponsePackage::Data(vec![ResponseType(UuidData { uuid: new_token })]); - to_string(&ret).map_err(|e| generate_error_message(e.to_string())) - } else { - Err(generate_error_message("Wrong password".to_string())) - } -} diff --git a/packages/router/src/routes/backend/secure/mod.rs b/packages/router/src/routes/backend/secure/mod.rs deleted file mode 100644 index b7e5f2a..0000000 --- a/packages/router/src/routes/backend/secure/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -use anyhow::Result; - -use axum::{routing::post, Router}; - -mod login; -mod register; -mod verify; - -use login::login; -use register::register; -use verify::verify; - -pub async fn route() -> Result { - let router = Router::new() - .route("/login", post(login)) - .route("/verify", post(verify)) - .route("/register", post(register)); - - Ok(router) -} diff --git a/packages/router/src/routes/backend/secure/register.rs b/packages/router/src/routes/backend/secure/register.rs deleted file mode 100644 index 74c319a..0000000 --- a/packages/router/src/routes/backend/secure/register.rs +++ /dev/null @@ -1,42 +0,0 @@ -use anyhow::Result; -use hyper::StatusCode; -use serde_json::to_string; -use uuid::Uuid; - -use axum::Json; - -use crate::routes::utils::generate_error_message; -use tairitsu_database::functions::user as functions; -use tairitsu_utils::{ - crypt_generate, - types::proto::{ - frontend::UuidData, RequestPackage, RequestPackage::Login as RequestType, ResponsePackage, - ResponseStruct::Token as ResponseType, - }, -}; - -pub async fn register(Json(item): Json) -> Result { - let item = match &item { - RequestType(item) => item.to_owned(), - _ => return Err(generate_error_message("Invalid request".to_string())), - }; - - let mut storage = functions::filter_by_name(item.name.clone()) - .await - .map_err(|e| generate_error_message(e.to_string()))?; - if storage.name == item.name.clone() { - Err(generate_error_message("Name already exists".to_string())) - } else { - let new_token = Uuid::new_v4(); - storage.name = item.name; - storage.password_hash = crypt_generate(item.password_hash) - .map_err(|e| generate_error_message(e.to_string()))?; - storage.token = new_token.to_owned(); - functions::insert(storage) - .await - .map_err(|e| generate_error_message(e.to_string()))?; - - let ret = ResponsePackage::Data(vec![ResponseType(UuidData { uuid: new_token })]); - to_string(&ret).map_err(|e| generate_error_message(e.to_string())) - } -} diff --git a/packages/router/src/routes/backend/secure/verify.rs b/packages/router/src/routes/backend/secure/verify.rs deleted file mode 100644 index 24ed137..0000000 --- a/packages/router/src/routes/backend/secure/verify.rs +++ /dev/null @@ -1,31 +0,0 @@ -use anyhow::Result; -use hyper::StatusCode; -use serde_json::to_string; - -use axum::Json; - -use crate::routes::utils::generate_error_message; -use tairitsu_database::functions::user as functions; -use tairitsu_utils::types::proto::{ - frontend::UuidData, RequestPackage, RequestPackage::Verify as RequestType, ResponsePackage, - ResponseStruct::Token as ResponseType, -}; - -pub async fn verify(Json(item): Json) -> Result { - let item = match &item { - RequestType(item) => item.to_owned(), - _ => return Err(generate_error_message("Invalid request".to_string())), - }; - - let storage = functions::filter_by_name(item.name) - .await - .map_err(|e| generate_error_message(e.to_string()))?; - if item.token == storage.token { - let ret = ResponsePackage::Data(vec![ResponseType(UuidData { - uuid: storage.token, - })]); - to_string(&ret).map_err(|e| generate_error_message(e.to_string())) - } else { - Err(generate_error_message("Invalid token".to_string())) - } -} diff --git a/packages/router/src/routes/frontend/mod.rs b/packages/router/src/routes/frontend/mod.rs deleted file mode 100644 index 6c18944..0000000 --- a/packages/router/src/routes/frontend/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -use anyhow::Result; - -use axum::Router; - -pub mod static_files; - -pub async fn route() -> Result { - let router = Router::new().nest("/", static_files::route().await?); - - Ok(router) -} diff --git a/packages/router/src/routes/frontend/static_files.rs b/packages/router/src/routes/frontend/static_files.rs deleted file mode 100644 index bb0295c..0000000 --- a/packages/router/src/routes/frontend/static_files.rs +++ /dev/null @@ -1,28 +0,0 @@ -use anyhow::Result; - -use axum::{routing::get_service, Router}; -use tower_http::services::ServeFile; - -use super::super::ROOT_DIR; - -pub async fn route() -> Result { - let router = Router::new() - .route_service( - "/a.js", - get_service(ServeFile::new(ROOT_DIR.clone().join("./a.js"))), - ) - .route_service( - "/a.wasm", - get_service(ServeFile::new(ROOT_DIR.clone().join("./a.wasm"))), - ) - .route_service( - "/favicon.ico", - get_service(ServeFile::new(ROOT_DIR.clone().join("./favicon.ico"))), - ) - .route_service( - "/logo.png", - get_service(ServeFile::new(ROOT_DIR.clone().join("./logo.png"))), - ); - - Ok(router) -} diff --git a/packages/router/src/routes/mod.rs b/packages/router/src/routes/mod.rs deleted file mode 100644 index 53cbcce..0000000 --- a/packages/router/src/routes/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -use anyhow::Result; -use lazy_static::lazy_static; -use log::info; -use std::path::{Path, PathBuf}; - -use axum::Router; - -pub mod backend; -pub mod frontend; -pub(crate) mod utils; - -pub async fn route() -> Result { - let router = Router::new() - .nest("/", frontend::route().await?) - .nest("/api", backend::route().await?); - - Ok(router) -} - -lazy_static! { - static ref ROOT_DIR: PathBuf = { - let root_dir = std::env::var("ROOT_DIR") - .ok() - .map(|dir| Path::new(&dir).to_path_buf()) - .unwrap_or(std::env::current_dir().unwrap().join("packages/router/res")); - info!(r#"Root dir is "{}""#, root_dir.display()); - root_dir - }; -} diff --git a/packages/router/src/routes/utils.rs b/packages/router/src/routes/utils.rs deleted file mode 100644 index 0849df0..0000000 --- a/packages/router/src/routes/utils.rs +++ /dev/null @@ -1,16 +0,0 @@ -use serde_json::to_string; - -use hyper::StatusCode; - -use tairitsu_utils::types::proto::{ResponsePackage, ResponseStruct}; - -pub fn generate_ok_message() -> Result { - let ret = ResponsePackage::Data(vec![ResponseStruct::Ok]); - to_string(&ret).map_err(|e| generate_error_message(e.to_string())) -} - -pub fn generate_error_message(message: String) -> (StatusCode, String) { - let ret = ResponsePackage::ErrorReason(message); - let ret = to_string(&ret).unwrap(); - (StatusCode::BAD_REQUEST, ret) -} diff --git a/packages/utils/Cargo.toml b/packages/utils/Cargo.toml index cf26eda..7806760 100644 --- a/packages/utils/Cargo.toml +++ b/packages/utils/Cargo.toml @@ -28,4 +28,4 @@ uuid = { version = "^1", features = [ ] } bcrypt = "^0.15" -sea-orm = { version = "1.0.0-rc.4", features = ["proxy"] } +sea-orm = { version = "1.0.0-rc.7", features = ["proxy"] } diff --git a/packages/vm/Cargo.toml b/packages/vm/Cargo.toml index 75b6e32..fb366c6 100644 --- a/packages/vm/Cargo.toml +++ b/packages/vm/Cargo.toml @@ -23,7 +23,7 @@ uuid = "^1" wit-component = "*" wasmtime = { version = "^20", features = ["component-model", "async"] } wasmtime-wasi = "^20" -sea-orm = { version = "1.0.0-rc.4", features = ["proxy"] } +sea-orm = { version = "1.0.0-rc.7", features = ["proxy"] } gluesql = { version = "^0.15" } cap-std = "^3"