wip: add repository trait, implement simple database with sqlite
This commit is contained in:
parent
8ce49f7c3d
commit
0384364579
101
Cargo.lock
generated
101
Cargo.lock
generated
@ -17,6 +17,18 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic"
|
||||
version = "0.6.0"
|
||||
@ -207,6 +219,8 @@ dependencies = [
|
||||
"futures",
|
||||
"futures-util",
|
||||
"problem_details",
|
||||
"rusqlite",
|
||||
"rusqlite_migration",
|
||||
"serde",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@ -311,6 +325,18 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
@ -498,12 +524,30 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers"
|
||||
version = "0.4.0"
|
||||
@ -824,7 +868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -880,6 +924,17 @@ version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.15"
|
||||
@ -1233,6 +1288,30 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.32.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite_migration"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "923b42e802f7dc20a0a6b5e097ba7c83fe4289da07e49156fecf6af08aa9cd1c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"rusqlite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
@ -2128,6 +2207,26 @@ dependencies = [
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.5"
|
||||
|
||||
@ -18,4 +18,8 @@ serde = { version = "1.0.217", features = ["derive"] }
|
||||
tracing = "0.1.41"
|
||||
tracing-log = "0.2.0"
|
||||
tracing-subscriber = "0.3.19"
|
||||
problem_details = { version = "0.7.0", features = ["axum"] }
|
||||
problem_details = { version = "0.7.0", features = ["axum"] }
|
||||
|
||||
# database
|
||||
rusqlite = { version = "0.32.1", features = ["bundled"] }
|
||||
rusqlite_migration = "1.3.1"
|
||||
|
||||
@ -9,7 +9,8 @@ pub const CONFIG_LOCATIONS: [&str; 2] = ["casket-backend/casket.toml", "/config/
|
||||
pub struct Config {
|
||||
pub server: Server,
|
||||
pub files: Files,
|
||||
pub oidc: Oidc
|
||||
pub oidc: Oidc,
|
||||
pub database: Database,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
@ -27,6 +28,12 @@ pub struct Oidc {
|
||||
pub oidc_endpoint: String
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct Database {
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
|
||||
pub fn get_config() -> figment::Result<Config> {
|
||||
CONFIG_LOCATIONS
|
||||
.iter()
|
||||
|
||||
2
casket-backend/src/db/mod.rs
Normal file
2
casket-backend/src/db/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod repository;
|
||||
pub mod sqlite;
|
||||
34
casket-backend/src/db/repository.rs
Normal file
34
casket-backend/src/db/repository.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use crate::db::repository::insert::File;
|
||||
use crate::db::repository::models::{FileModel, UserModel};
|
||||
use problem_details::ProblemDetails;
|
||||
|
||||
pub trait Repository {
|
||||
fn migrate(&self) -> impl std::future::Future<Output = Result<(), ProblemDetails>> + Send;
|
||||
fn get_or_create_user(&self, id: String) -> impl std::future::Future<Output = Result<UserModel, ProblemDetails>> + Send;
|
||||
fn create_file(&self, file: File) -> impl std::future::Future<Output = Result<FileModel, ProblemDetails>> + Send;
|
||||
|
||||
fn bump_version(&self, file_model: FileModel) -> impl std::future::Future<Output = Result<(), ProblemDetails>> + Send;
|
||||
|
||||
fn get_file(&self, user_id: String, path: String) -> impl std::future::Future<Output = Result<FileModel, ProblemDetails>> + Send;
|
||||
}
|
||||
|
||||
pub mod models {
|
||||
pub struct UserModel {
|
||||
pub uuid: String,
|
||||
}
|
||||
|
||||
pub struct FileModel {
|
||||
pub id: u64,
|
||||
pub user_id: String,
|
||||
pub path: String,
|
||||
pub version: u64,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod insert {
|
||||
pub struct File {
|
||||
pub user_id: String,
|
||||
pub path: String,
|
||||
pub version: u64,
|
||||
}
|
||||
}
|
||||
79
casket-backend/src/db/sqlite.rs
Normal file
79
casket-backend/src/db/sqlite.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use std::path::PathBuf;
|
||||
use crate::db::repository::insert::File;
|
||||
use crate::db::repository::models::{FileModel, UserModel};
|
||||
use crate::db::repository::Repository;
|
||||
use crate::errors::to_internal_error;
|
||||
use problem_details::ProblemDetails;
|
||||
use rusqlite::Connection;
|
||||
use rusqlite_migration::{Migrations, M};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::task::spawn_blocking;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Sqlite {
|
||||
connection: Arc<Mutex<Connection>>,
|
||||
}
|
||||
|
||||
impl Sqlite {
|
||||
pub fn from_path(path: &PathBuf) -> Sqlite {
|
||||
Sqlite {
|
||||
connection: Arc::new(Mutex::new(Connection::open(path).unwrap())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Repository for Sqlite {
|
||||
async fn migrate(&self) -> Result<(), ProblemDetails> {
|
||||
let migrations = Migrations::new(vec![
|
||||
M::up("CREATE TABLE users(uuid TEXT PRIMARY KEY, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);"),
|
||||
M::up("CREATE TABLE files(id INTEGER PRIMARY KEY, user_id TEXT, file_path TEXT, version INTEGER, FOREIGN KEY(user_id) REFERENCES users(uuid));"),
|
||||
M::up("CREATE INDEX files_user_path_idx ON files(user_id, file_path);"),
|
||||
]);
|
||||
let connection = self.connection.clone();
|
||||
spawn_blocking(move || match connection.lock() {
|
||||
Ok(mut conn) => {
|
||||
migrations.to_latest(&mut conn).unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("Failed to lock connection: {err}");
|
||||
}
|
||||
})
|
||||
.await
|
||||
.map_err(to_internal_error)
|
||||
}
|
||||
|
||||
async fn get_or_create_user(&self, id: String) -> Result<UserModel, ProblemDetails> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn create_file(&self, file: File) -> Result<FileModel, ProblemDetails> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn bump_version(&self, file_model: FileModel) -> Result<(), ProblemDetails> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_file(&self, user_id: String, path: String) -> Result<FileModel, ProblemDetails> {
|
||||
let conn = self.connection.clone();
|
||||
spawn_blocking(move || {
|
||||
match conn.lock() {
|
||||
Ok(connection) => {
|
||||
let mut statement = connection.prepare("SELECT id, user_id, path, version FROM files WHERE user_id = ?1 AND path = ?2").unwrap();
|
||||
statement.query_row([user_id, path], |row| {
|
||||
Ok(FileModel {
|
||||
id: row.get(0).unwrap(),
|
||||
user_id: row.get(1).unwrap(),
|
||||
path: row.get(2).unwrap(),
|
||||
version: row.get(3).unwrap(),
|
||||
})
|
||||
}).unwrap()
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("Failed to lock connection: {err}");
|
||||
}
|
||||
}
|
||||
}).await
|
||||
.map_err(to_internal_error)
|
||||
}
|
||||
}
|
||||
@ -2,12 +2,15 @@
|
||||
|
||||
mod auth;
|
||||
mod config;
|
||||
mod db;
|
||||
mod errors;
|
||||
mod extractor_helper;
|
||||
mod files;
|
||||
mod routes;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::db::repository::Repository;
|
||||
use crate::db::sqlite::Sqlite;
|
||||
use axum::{middleware, Router};
|
||||
use axum_jwks::Jwks;
|
||||
use std::env;
|
||||
@ -19,6 +22,13 @@ use tracing::{debug, error, info};
|
||||
pub struct AppState {
|
||||
config: config::Config,
|
||||
pub jwks: Jwks,
|
||||
pub sqlite: Sqlite,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn get_repo(&self) -> &impl Repository {
|
||||
&self.sqlite
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@ -31,8 +41,13 @@ async fn main() {
|
||||
Ok(config) => {
|
||||
debug!("Config loaded {:?}", &config,);
|
||||
let jwks = load_jwks(&config).await;
|
||||
let db = connect_database(&config).await;
|
||||
let bind = format!("{}:{}", &config.server.bind_address, &config.server.port);
|
||||
let state = AppState { config, jwks };
|
||||
let state: AppState = AppState {
|
||||
config,
|
||||
jwks,
|
||||
sqlite: db,
|
||||
};
|
||||
let app = Router::new()
|
||||
.merge(routes::routes())
|
||||
.route_layer(middleware::from_fn_with_state(
|
||||
@ -56,6 +71,12 @@ async fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
async fn connect_database(config: &Config) -> Sqlite {
|
||||
let sqlite = db::sqlite::Sqlite::from_path(&config.database.path);
|
||||
sqlite.migrate().await.unwrap();
|
||||
sqlite
|
||||
}
|
||||
|
||||
async fn load_jwks(config: &Config) -> Jwks {
|
||||
Jwks::from_oidc_url(&config.oidc.oidc_endpoint, None)
|
||||
.await
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user