1pub mod err;
2pub mod pass;
3pub mod perm;
4pub mod pkce;
5pub mod prelude;
6pub mod token;
7pub mod user;
8
9use std::path::PathBuf;
10
11use sqlx::{SqlitePool, query, sqlite::SqliteConnectOptions};
12
13use token::TokenModule;
14use tracing::{info, trace};
15
16pub use prelude::*;
17
18use crate::pkce::{PkceConfig, PkceModule};
19
20fn rand_buf<const N: usize>() -> [u8; N] {
21 let mut buf = [0u8; N];
22 getrandom::fill(&mut buf).unwrap();
23 buf
24}
25
26#[derive(Clone)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub struct Config {
30 #[cfg_attr(feature = "serde", serde(rename = "database-path"))]
32 pub db: PathBuf,
33 #[cfg_attr(feature = "serde", serde(rename = "pkce"))]
35 #[cfg_attr(feature = "serde", serde(default))]
36 pub pkce: PkceConfig,
37}
38
39impl Default for Config {
40 fn default() -> Self {
41 Self {
42 db: "./basileus.db".into(),
43 pkce: Default::default(),
44 }
45 }
46}
47
48pub struct Basileus {
50 pub config: Config,
52 db: SqlitePool,
54 token: TokenModule,
56 pkce: PkceModule,
57}
58
59pub const DB_INIT: &str = r#"
61CREATE TABLE IF NOT EXISTS pubkey (
62 user TEXT NOT NULL PRIMARY KEY,
63 key BLOB NOT NULL,
64 FOREIGN KEY (user) REFERENCES user(user) ON DELETE CASCADE
65);
66CREATE INDEX IF NOT EXISTS idx_pubkey_user ON pubkey (user);
67CREATE TABLE IF NOT EXISTS token (
68 user TEXT NOT NULL PRIMARY KEY,
69 token TEXT,
70 FOREIGN KEY (user) REFERENCES user(user) ON DELETE CASCADE
71);
72CREATE INDEX IF NOT EXISTS idx_token_user ON token (user);
73"#;
74
75impl Basileus {
76 pub async fn new(config: Config) -> Result<Self, sqlx::error::Error> {
78 let opt = SqliteConnectOptions::default()
79 .filename(&config.db)
80 .create_if_missing(true);
81 let db = SqlitePool::connect_with(opt).await?;
82 info!("connected to {:?}", config.db);
83 query(user::DB_INIT).execute(&db).await?;
84 query(pass::DB_INIT).execute(&db).await?;
85 query(perm::DB_INIT).execute(&db).await?;
86 query(DB_INIT).execute(&db).await?;
87 trace!("database initialized");
88 let pkce = PkceModule::new(config.pkce.clone());
89 Ok(Self {
90 config,
91 db,
92 token: TokenModule::new(),
93 pkce,
94 })
95 }
96
97 pub async fn user_cnt(&self) -> Result<i64, sqlx::error::Error> {
99 let (cnt,): (i64,) = sqlx::query_as("SELECT COUNT(*) FROM user")
100 .fetch_one(&self.db)
101 .await?;
102 Ok(cnt)
103 }
104}