refactor: remove session auth, add DbType enum, native async broker
- Remove `auth`, `sessions-db` features and all tower-sessions/tower-sessions-sqlx-store deps - Delete `src/session.rs` (InfraSessionStore) - `ServerConfig`: remove `session_secret` field and `attach_session_layer()` helper - `db.rs`: add explicit `DbType` enum to `DatabaseConfig`; `connect()` now matches on `db_type` instead of sniffing URL prefixes; remove `connect_sqlite()` standalone fn - `broker/mod.rs`: replace `#[async_trait]` with native async fn (Rust 2024 AFIT) - `broker/nats.rs`: remove `#[async_trait]` to match trait definition - `ai/embeddings.rs`: remove sync `generate_embedding()` (wasteful thread spawn); keep only `generate_embedding_async()` as the public API - Default features: `["logging", "db-sqlx", "auth", "http"]` → `["logging"]` - Bump version to 0.1.11 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
121
Cargo.lock
generated
121
Cargo.lock
generated
@@ -533,17 +533,6 @@ version = "0.9.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cookie"
|
|
||||||
version = "0.18.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
|
||||||
dependencies = [
|
|
||||||
"percent-encoding",
|
|
||||||
"time",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@@ -1775,7 +1764,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "k-core"
|
name = "k-core"
|
||||||
version = "0.1.10"
|
version = "0.1.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-nats",
|
"async-nats",
|
||||||
@@ -1789,12 +1778,9 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"time",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower 0.5.2",
|
"tower 0.5.2",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tower-sessions",
|
|
||||||
"tower-sessions-sqlx-store",
|
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"uuid",
|
"uuid",
|
||||||
@@ -1878,7 +1864,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
"serde",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2958,25 +2943,6 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rmp"
|
|
||||||
version = "0.8.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c"
|
|
||||||
dependencies = [
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rmp-serde"
|
|
||||||
version = "1.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
|
|
||||||
dependencies = [
|
|
||||||
"rmp",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa"
|
name = "rsa"
|
||||||
version = "0.9.9"
|
version = "0.9.9"
|
||||||
@@ -3479,7 +3445,6 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
"smallvec 1.15.1",
|
"smallvec 1.15.1",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"time",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -3564,7 +3529,6 @@ dependencies = [
|
|||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"stringprep",
|
"stringprep",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"time",
|
|
||||||
"tracing",
|
"tracing",
|
||||||
"uuid",
|
"uuid",
|
||||||
"whoami",
|
"whoami",
|
||||||
@@ -3604,7 +3568,6 @@ dependencies = [
|
|||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"stringprep",
|
"stringprep",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"time",
|
|
||||||
"tracing",
|
"tracing",
|
||||||
"uuid",
|
"uuid",
|
||||||
"whoami",
|
"whoami",
|
||||||
@@ -3631,7 +3594,6 @@ dependencies = [
|
|||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"time",
|
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
@@ -4063,22 +4025,6 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower-cookies"
|
|
||||||
version = "0.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "151b5a3e3c45df17466454bb74e9ecedecc955269bdedbf4d150dfa393b55a36"
|
|
||||||
dependencies = [
|
|
||||||
"axum-core 0.5.6",
|
|
||||||
"cookie",
|
|
||||||
"futures-util",
|
|
||||||
"http",
|
|
||||||
"parking_lot",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-http"
|
name = "tower-http"
|
||||||
version = "0.6.8"
|
version = "0.6.8"
|
||||||
@@ -4110,71 +4056,6 @@ version = "0.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower-sessions"
|
|
||||||
version = "0.14.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "43a05911f23e8fae446005fe9b7b97e66d95b6db589dc1c4d59f6a2d4d4927d3"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"http",
|
|
||||||
"time",
|
|
||||||
"tokio",
|
|
||||||
"tower-cookies",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
"tower-sessions-core",
|
|
||||||
"tower-sessions-memory-store",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower-sessions-core"
|
|
||||||
version = "0.14.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ce8cce604865576b7751b7a6bc3058f754569a60d689328bb74c52b1d87e355b"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"axum-core 0.5.6",
|
|
||||||
"base64 0.22.1",
|
|
||||||
"futures",
|
|
||||||
"http",
|
|
||||||
"parking_lot",
|
|
||||||
"rand 0.8.5",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror 2.0.17",
|
|
||||||
"time",
|
|
||||||
"tokio",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower-sessions-memory-store"
|
|
||||||
version = "0.14.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fb05909f2e1420135a831dd5df9f5596d69196d0a64c3499ca474c4bd3d33242"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"time",
|
|
||||||
"tokio",
|
|
||||||
"tower-sessions-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower-sessions-sqlx-store"
|
|
||||||
version = "0.15.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e054622079f57fc1a7d6a6089c9334f963d62028fe21dc9eddd58af9a78480b3"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"rmp-serde",
|
|
||||||
"sqlx",
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
"time",
|
|
||||||
"tower-sessions-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
|
|||||||
17
Cargo.toml
17
Cargo.toml
@@ -1,20 +1,18 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "k-core"
|
name = "k-core"
|
||||||
version = "0.1.10"
|
version = "0.1.11"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["logging", "db-sqlx", "auth", "http"]
|
default = ["logging"]
|
||||||
logging = ["dep:tracing", "dep:tracing-subscriber"]
|
logging = ["dep:tracing", "dep:tracing-subscriber"]
|
||||||
db-sqlx = ["dep:sqlx"]
|
db-sqlx = ["dep:sqlx"]
|
||||||
postgres = ["db-sqlx", "sqlx/postgres", "tower-sessions-sqlx-store?/postgres"]
|
postgres = ["db-sqlx", "sqlx/postgres"]
|
||||||
sqlite = ["db-sqlx", "sqlx/sqlite", "tower-sessions-sqlx-store?/sqlite"]
|
sqlite = ["db-sqlx", "sqlx/sqlite"]
|
||||||
auth = ["dep:tower-sessions"]
|
|
||||||
sessions-db = ["auth", "dep:tower-sessions-sqlx-store"]
|
|
||||||
ai = ["dep:fastembed", "dep:qdrant-client"]
|
ai = ["dep:fastembed", "dep:qdrant-client"]
|
||||||
broker = []
|
broker = []
|
||||||
broker-nats = ["broker", "dep:async-nats", "dep:futures-util", "dep:futures-core"]
|
broker-nats = ["broker", "dep:async-nats", "dep:futures-util", "dep:futures-core"]
|
||||||
http = ["dep:axum", "dep:tower", "dep:tower-http", "dep:time", "logging"]
|
http = ["dep:axum", "dep:tower", "dep:tower-http", "logging"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Error handling
|
# Error handling
|
||||||
@@ -39,10 +37,6 @@ tracing-subscriber = { version = "0.3.22", features = [
|
|||||||
"fmt",
|
"fmt",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
|
|
||||||
# Auth
|
|
||||||
tower-sessions = { version = "0.14.0", optional = true }
|
|
||||||
tower-sessions-sqlx-store = { version = "0.15", optional = true}
|
|
||||||
|
|
||||||
# Utils
|
# Utils
|
||||||
chrono = { version = "0.4.42", features = ["serde"] }
|
chrono = { version = "0.4.42", features = ["serde"] }
|
||||||
uuid = { version = "1.19.0", features = ["v4", "serde"] }
|
uuid = { version = "1.19.0", features = ["v4", "serde"] }
|
||||||
@@ -60,7 +54,6 @@ async-nats = { version = "0.45", optional = true }
|
|||||||
axum = { version = "0.8.8", features = ["macros"], optional = true }
|
axum = { version = "0.8.8", features = ["macros"], optional = true }
|
||||||
tower = { version = "0.5.2", optional = true }
|
tower = { version = "0.5.2", optional = true }
|
||||||
tower-http = { version = "0.6.2", features = ["cors", "trace"], optional = true }
|
tower-http = { version = "0.6.2", features = ["cors", "trace"], optional = true }
|
||||||
time = { version = "0.3", optional = true }
|
|
||||||
|
|
||||||
futures-util = { version = "0.3", optional = true }
|
futures-util = { version = "0.3", optional = true }
|
||||||
futures-core = { version = "0.3", optional = true }
|
futures-core = { version = "0.3", optional = true }
|
||||||
@@ -20,33 +20,7 @@ impl FastEmbedAdapter {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_embedding(&self, text: &str) -> anyhow::Result<Vec<f32>> {
|
/// Generate an embedding asynchronously using a blocking thread pool.
|
||||||
let model = self.model.clone();
|
|
||||||
let text = text.to_string();
|
|
||||||
|
|
||||||
// FastEmbed is blocking, so we run it in a blocking task if we are in an async context,
|
|
||||||
// but since this method signature doesn't force async, we wrap the internal logic.
|
|
||||||
// For strictly async usage in k-core:
|
|
||||||
let embeddings = std::thread::scope(|s| {
|
|
||||||
s.spawn(|| {
|
|
||||||
let mut model = model
|
|
||||||
.lock()
|
|
||||||
.map_err(|e| anyhow::anyhow!("Lock error: {}", e))?;
|
|
||||||
model
|
|
||||||
.embed(vec![text], None)
|
|
||||||
.map_err(|e| anyhow::anyhow!("Embed error: {}", e))
|
|
||||||
})
|
|
||||||
.join()
|
|
||||||
.map_err(|_| anyhow::anyhow!("Thread join error"))?
|
|
||||||
})?;
|
|
||||||
|
|
||||||
embeddings
|
|
||||||
.into_iter()
|
|
||||||
.next()
|
|
||||||
.ok_or_else(|| anyhow::anyhow!("No embedding generated"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Async wrapper for use in async contexts (like Axum handlers)
|
|
||||||
pub async fn generate_embedding_async(&self, text: &str) -> anyhow::Result<Vec<f32>> {
|
pub async fn generate_embedding_async(&self, text: &str) -> anyhow::Result<Vec<f32>> {
|
||||||
let model = self.model.clone();
|
let model = self.model.clone();
|
||||||
let text = text.to_string();
|
let text = text.to_string();
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
#[cfg(feature = "broker-nats")]
|
#[cfg(feature = "broker-nats")]
|
||||||
pub mod nats;
|
pub mod nats;
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait MessageBroker: Send + Sync {
|
pub trait MessageBroker: Send + Sync {
|
||||||
async fn publish(&self, topic: &str, payload: Vec<u8>) -> anyhow::Result<()>;
|
async fn publish(&self, topic: &str, payload: Vec<u8>) -> anyhow::Result<()>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use super::MessageBroker;
|
use super::MessageBroker;
|
||||||
use async_trait::async_trait;
|
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
@@ -15,7 +14,6 @@ impl NatsBroker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl MessageBroker for NatsBroker {
|
impl MessageBroker for NatsBroker {
|
||||||
async fn publish(&self, topic: &str, payload: Vec<u8>) -> anyhow::Result<()> {
|
async fn publish(&self, topic: &str, payload: Vec<u8>) -> anyhow::Result<()> {
|
||||||
self.client
|
self.client
|
||||||
@@ -35,7 +33,6 @@ impl MessageBroker for NatsBroker {
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| anyhow::anyhow!("NATS subscribe error: {}", e))?;
|
.map_err(|e| anyhow::anyhow!("NATS subscribe error: {}", e))?;
|
||||||
|
|
||||||
// Map NATS Message to generic Vec<u8>
|
|
||||||
let stream = subscriber.map(|msg| msg.payload.to_vec());
|
let stream = subscriber.map(|msg| msg.payload.to_vec());
|
||||||
|
|
||||||
Ok(Box::pin(stream))
|
Ok(Box::pin(stream))
|
||||||
|
|||||||
46
src/db.rs
46
src/db.rs
@@ -8,9 +8,22 @@ use sqlx::Sqlite;
|
|||||||
#[cfg(feature = "postgres")]
|
#[cfg(feature = "postgres")]
|
||||||
use sqlx::Postgres;
|
use sqlx::Postgres;
|
||||||
|
|
||||||
|
/// Explicitly declares which database engine to connect to.
|
||||||
|
///
|
||||||
|
/// Using an explicit type avoids URL-prefix sniffing in `connect()`.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
pub enum DbType {
|
||||||
|
#[default]
|
||||||
|
#[cfg_attr(not(feature = "sqlite"), allow(dead_code))]
|
||||||
|
Sqlite,
|
||||||
|
#[cfg_attr(not(feature = "postgres"), allow(dead_code))]
|
||||||
|
Postgres,
|
||||||
|
}
|
||||||
|
|
||||||
/// Universal Database Configuration
|
/// Universal Database Configuration
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DatabaseConfig {
|
pub struct DatabaseConfig {
|
||||||
|
pub db_type: DbType,
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub max_connections: u32,
|
pub max_connections: u32,
|
||||||
pub min_connections: u32,
|
pub min_connections: u32,
|
||||||
@@ -22,6 +35,7 @@ impl Default for DatabaseConfig {
|
|||||||
#[cfg(feature = "sqlite")]
|
#[cfg(feature = "sqlite")]
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
|
db_type: DbType::Sqlite,
|
||||||
url: "sqlite::memory:".to_string(),
|
url: "sqlite::memory:".to_string(),
|
||||||
max_connections: 5,
|
max_connections: 5,
|
||||||
min_connections: 1,
|
min_connections: 1,
|
||||||
@@ -32,6 +46,7 @@ impl Default for DatabaseConfig {
|
|||||||
#[cfg(all(not(feature = "sqlite"), feature = "postgres"))]
|
#[cfg(all(not(feature = "sqlite"), feature = "postgres"))]
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
|
db_type: DbType::Postgres,
|
||||||
url: "postgres://localhost:5432/mydb".to_string(),
|
url: "postgres://localhost:5432/mydb".to_string(),
|
||||||
max_connections: 5,
|
max_connections: 5,
|
||||||
min_connections: 1,
|
min_connections: 1,
|
||||||
@@ -41,6 +56,7 @@ impl Default for DatabaseConfig {
|
|||||||
|
|
||||||
#[cfg(not(any(feature = "sqlite", feature = "postgres")))]
|
#[cfg(not(any(feature = "sqlite", feature = "postgres")))]
|
||||||
Self {
|
Self {
|
||||||
|
db_type: DbType::Sqlite,
|
||||||
url: "".to_string(),
|
url: "".to_string(),
|
||||||
max_connections: 5,
|
max_connections: 5,
|
||||||
min_connections: 1,
|
min_connections: 1,
|
||||||
@@ -50,8 +66,9 @@ impl Default for DatabaseConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DatabaseConfig {
|
impl DatabaseConfig {
|
||||||
pub fn new(url: impl Into<String>) -> Self {
|
pub fn new(db_type: DbType, url: impl Into<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
db_type,
|
||||||
url: url.into(),
|
url: url.into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
@@ -60,8 +77,9 @@ impl DatabaseConfig {
|
|||||||
#[cfg(feature = "sqlite")]
|
#[cfg(feature = "sqlite")]
|
||||||
pub fn in_memory() -> Self {
|
pub fn in_memory() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
db_type: DbType::Sqlite,
|
||||||
url: "sqlite::memory:".to_string(),
|
url: "sqlite::memory:".to_string(),
|
||||||
max_connections: 1, // SQLite in-memory is single-connection
|
max_connections: 1,
|
||||||
min_connections: 1,
|
min_connections: 1,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
@@ -69,7 +87,6 @@ impl DatabaseConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper around various DB pools.
|
/// A wrapper around various DB pools.
|
||||||
/// The Template uses this type so it doesn't care if it's Sqlite or Postgres.
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum DatabasePool {
|
pub enum DatabasePool {
|
||||||
#[cfg(feature = "sqlite")]
|
#[cfg(feature = "sqlite")]
|
||||||
@@ -78,36 +95,33 @@ pub enum DatabasePool {
|
|||||||
Postgres(Pool<Postgres>),
|
Postgres(Pool<Postgres>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The single entry point for connecting to any DB.
|
/// Connect to a database using the explicit `db_type` from `DatabaseConfig`.
|
||||||
pub async fn connect(config: &DatabaseConfig) -> Result<DatabasePool, sqlx::Error> {
|
pub async fn connect(config: &DatabaseConfig) -> Result<DatabasePool, sqlx::Error> {
|
||||||
// 1. Try Postgres if the feature is enabled AND the URL looks like postgres
|
match config.db_type {
|
||||||
#[cfg(feature = "postgres")]
|
#[cfg(feature = "postgres")]
|
||||||
if config.url.starts_with("postgres://") || config.url.starts_with("postgresql://") {
|
DbType::Postgres => {
|
||||||
let pool = sqlx::postgres::PgPoolOptions::new()
|
let pool = sqlx::postgres::PgPoolOptions::new()
|
||||||
.max_connections(config.max_connections)
|
.max_connections(config.max_connections)
|
||||||
.acquire_timeout(config.acquire_timeout)
|
.acquire_timeout(config.acquire_timeout)
|
||||||
.connect(&config.url)
|
.connect(&config.url)
|
||||||
.await?;
|
.await?;
|
||||||
return Ok(DatabasePool::Postgres(pool));
|
Ok(DatabasePool::Postgres(pool))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Fallback to Sqlite if the feature is enabled
|
|
||||||
#[cfg(feature = "sqlite")]
|
#[cfg(feature = "sqlite")]
|
||||||
{
|
DbType::Sqlite => {
|
||||||
let pool = sqlx::sqlite::SqlitePoolOptions::new()
|
let pool = sqlx::sqlite::SqlitePoolOptions::new()
|
||||||
.max_connections(config.max_connections)
|
.max_connections(config.max_connections)
|
||||||
.acquire_timeout(config.acquire_timeout)
|
.acquire_timeout(config.acquire_timeout)
|
||||||
.connect(&config.url)
|
.connect(&config.url)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(DatabasePool::Sqlite(pool))
|
Ok(DatabasePool::Sqlite(pool))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "sqlite"))]
|
#[allow(unreachable_patterns)]
|
||||||
{
|
_ => Err(sqlx::Error::Configuration(
|
||||||
Err(sqlx::Error::Configuration(
|
|
||||||
"No supported database features enabled".into(),
|
"No supported database features enabled".into(),
|
||||||
))
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +144,3 @@ impl DatabasePool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
pub async fn connect_sqlite(url: &str) -> Result<Pool<Sqlite>, sqlx::Error> {
|
|
||||||
sqlx::sqlite::SqlitePoolOptions::new().connect(url).await
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
use axum::Router;
|
use axum::Router;
|
||||||
use tower_http::cors::CorsLayer;
|
use tower_http::cors::CorsLayer;
|
||||||
use tower_http::trace::TraceLayer;
|
use tower_http::trace::TraceLayer;
|
||||||
use tower_sessions::{Expiry, SessionManagerLayer, SessionStore};
|
|
||||||
|
|
||||||
pub struct ServerConfig {
|
pub struct ServerConfig {
|
||||||
pub cors_origins: Vec<String>,
|
pub cors_origins: Vec<String>,
|
||||||
pub session_secret: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_standard_middleware(app: Router, config: &ServerConfig) -> Router {
|
pub fn apply_standard_middleware(app: Router, config: &ServerConfig) -> Router {
|
||||||
@@ -24,7 +22,6 @@ pub fn apply_standard_middleware(app: Router, config: &ServerConfig) -> Router {
|
|||||||
])
|
])
|
||||||
.allow_credentials(true);
|
.allow_credentials(true);
|
||||||
|
|
||||||
// Configure CORS origins
|
|
||||||
let mut allowed_origins = Vec::new();
|
let mut allowed_origins = Vec::new();
|
||||||
for origin in &config.cors_origins {
|
for origin in &config.cors_origins {
|
||||||
if let Ok(value) = origin.parse::<axum::http::HeaderValue>() {
|
if let Ok(value) = origin.parse::<axum::http::HeaderValue>() {
|
||||||
@@ -38,15 +35,3 @@ pub fn apply_standard_middleware(app: Router, config: &ServerConfig) -> Router {
|
|||||||
|
|
||||||
app.layer(cors).layer(TraceLayer::new_for_http())
|
app.layer(cors).layer(TraceLayer::new_for_http())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to attach a session layer with standard K-Suite configuration
|
|
||||||
pub fn attach_session_layer<S>(app: Router, store: S) -> Router
|
|
||||||
where
|
|
||||||
S: SessionStore + Clone + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
let session_layer = SessionManagerLayer::new(store)
|
|
||||||
.with_secure(false) // Set to true if you handle HTTPS termination in the app
|
|
||||||
.with_expiry(Expiry::OnInactivity(time::Duration::days(7)));
|
|
||||||
|
|
||||||
app.layer(session_layer)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ pub mod ai;
|
|||||||
|
|
||||||
#[cfg(feature = "broker")]
|
#[cfg(feature = "broker")]
|
||||||
pub mod broker;
|
pub mod broker;
|
||||||
#[cfg(feature = "auth")]
|
|
||||||
pub mod session;
|
|
||||||
|
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
pub mod http;
|
pub mod http;
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
// Only compile this module if we have the store dependency AND sqlx enabled
|
|
||||||
#[cfg(all(feature = "sessions-db", feature = "db-sqlx"))]
|
|
||||||
pub mod store {
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use tower_sessions::{
|
|
||||||
SessionStore,
|
|
||||||
session::{Id, Record},
|
|
||||||
session_store,
|
|
||||||
};
|
|
||||||
|
|
||||||
// We only import what is actually enabled by features
|
|
||||||
#[cfg(feature = "postgres")]
|
|
||||||
use tower_sessions_sqlx_store::PostgresStore;
|
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
use tower_sessions_sqlx_store::SqliteStore;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum InfraSessionStore {
|
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
Sqlite(SqliteStore),
|
|
||||||
#[cfg(feature = "postgres")]
|
|
||||||
Postgres(PostgresStore),
|
|
||||||
// Fallback variant to allow compilation if sessions-db is enabled but no generic DB feature is selected
|
|
||||||
// (Though in practice you should ensure your config validates this)
|
|
||||||
#[allow(dead_code)]
|
|
||||||
Unused,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl SessionStore for InfraSessionStore {
|
|
||||||
async fn save(&self, session_record: &Record) -> session_store::Result<()> {
|
|
||||||
match self {
|
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
Self::Sqlite(store) => store.save(session_record).await,
|
|
||||||
#[cfg(feature = "postgres")]
|
|
||||||
Self::Postgres(store) => store.save(session_record).await,
|
|
||||||
_ => Err(session_store::Error::Backend(
|
|
||||||
"No database feature enabled".to_string(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn load(&self, session_id: &Id) -> session_store::Result<Option<Record>> {
|
|
||||||
match self {
|
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
Self::Sqlite(store) => store.load(session_id).await,
|
|
||||||
#[cfg(feature = "postgres")]
|
|
||||||
Self::Postgres(store) => store.load(session_id).await,
|
|
||||||
_ => Err(session_store::Error::Backend(
|
|
||||||
"No database feature enabled".to_string(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete(&self, session_id: &Id) -> session_store::Result<()> {
|
|
||||||
match self {
|
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
Self::Sqlite(store) => store.delete(session_id).await,
|
|
||||||
#[cfg(feature = "postgres")]
|
|
||||||
Self::Postgres(store) => store.delete(session_id).await,
|
|
||||||
_ => Err(session_store::Error::Backend(
|
|
||||||
"No database feature enabled".to_string(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InfraSessionStore {
|
|
||||||
pub async fn migrate(&self) -> anyhow::Result<()> {
|
|
||||||
match self {
|
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
Self::Sqlite(store) => store.migrate().await.map_err(|e| anyhow::anyhow!(e)),
|
|
||||||
#[cfg(feature = "postgres")]
|
|
||||||
Self::Postgres(store) => store.migrate().await.map_err(|e| anyhow::anyhow!(e)),
|
|
||||||
_ => Ok(()), // No migration needed for no-op
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user