feat(homeserver): add Users table

This commit is contained in:
nazeh
2024-07-18 13:18:01 +03:00
parent 8fbe0d5ae3
commit c9ccbbb77c
8 changed files with 205 additions and 18 deletions

70
Cargo.lock generated
View File

@@ -55,6 +55,15 @@ dependencies = [
"syn",
]
[[package]]
name = "atomic-polyfill"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
dependencies = [
"critical-section",
]
[[package]]
name = "autocfg"
version = "1.3.0"
@@ -219,6 +228,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cobs"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
[[package]]
name = "const-oid"
version = "0.9.6"
@@ -264,6 +279,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "critical-section"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]]
name = "crossbeam-queue"
version = "0.3.11"
@@ -406,6 +427,12 @@ dependencies = [
"zeroize",
]
[[package]]
name = "embedded-io"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
[[package]]
name = "fiat-crypto"
version = "0.2.9"
@@ -567,6 +594,29 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[package]]
name = "hash32"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
dependencies = [
"byteorder",
]
[[package]]
name = "heapless"
version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
dependencies = [
"atomic-polyfill",
"hash32",
"rustc_version",
"serde",
"spin",
"stable_deref_trait",
]
[[package]]
name = "heed"
version = "0.20.3"
@@ -1043,6 +1093,18 @@ dependencies = [
"spki",
]
[[package]]
name = "postcard"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8"
dependencies = [
"cobs",
"embedded-io",
"heapless",
"serde",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@@ -1095,7 +1157,9 @@ dependencies = [
"dirs-next",
"heed",
"pkarr",
"postcard",
"pubky-common",
"serde",
"tokio",
"tower-http",
"tracing",
@@ -1472,6 +1536,12 @@ dependencies = [
"der",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "subtle"
version = "2.6.1"

View File

@@ -10,7 +10,9 @@ bytes = "1.6.1"
dirs-next = "2.0.0"
heed = "0.20.3"
pkarr = { version = "2.1.0", features = ["async"] }
postcard = { version = "1.0.8", features = ["alloc"] }
pubky-common = { version = "0.1.0", path = "../pubky-common" }
serde = { version = "1.0.204", features = ["derive"] }
tokio = { version = "1.37.0", features = ["full"] }
tower-http = { version = "0.5.2", features = ["cors", "trace"] }
tracing = "0.1.40"

View File

@@ -1,19 +1,38 @@
use std::fs;
use std::path::Path;
use heed::{Env, EnvOpenOptions};
use heed::{types::Str, Database, Env, EnvOpenOptions, RwTxn};
mod migrations;
pub mod tables;
use migrations::TABLES_COUNT;
#[derive(Debug, Clone)]
pub struct DB {
env: Env,
pub(crate) env: Env,
}
impl DB {
pub fn open(storage: &Path) -> anyhow::Result<Self> {
fs::create_dir_all(storage).unwrap();
let env = unsafe { EnvOpenOptions::new().open(storage) }?;
let env = unsafe { EnvOpenOptions::new().max_dbs(TABLES_COUNT).open(storage) }?;
Ok(DB { env })
let db = DB { env };
db.run_migrations();
Ok(db)
}
fn run_migrations(&self) -> anyhow::Result<()> {
let mut wtxn = self.env.write_txn()?;
migrations::create_users_table(&self.env, &mut wtxn);
wtxn.commit()?;
Ok(())
}
}

View File

@@ -0,0 +1,11 @@
use heed::{types::Str, Database, Env, RwTxn};
use super::tables;
pub const TABLES_COUNT: u32 = 1;
pub fn create_users_table(env: &Env, wtxn: &mut RwTxn) -> anyhow::Result<()> {
let _: tables::users::UsersTable = env.create_database(wtxn, None)?;
Ok(())
}

View File

@@ -0,0 +1 @@
pub mod users;

View File

@@ -0,0 +1,59 @@
use std::{borrow::Cow, time::SystemTime};
use postcard::{from_bytes, to_allocvec};
use pubky_common::timestamp::Timestamp;
use serde::{Deserialize, Serialize};
use heed::{types::Str, BoxedError, BytesDecode, BytesEncode, Database};
use pkarr::PublicKey;
extern crate alloc;
use alloc::vec::Vec;
/// PublicKey => User.
pub type UsersTable = Database<PublicKeyCodec, User>;
pub const USERS_TABLE: &str = "users";
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct User {
pub created_at: u64,
}
impl<'a> BytesEncode<'a> for User {
type EItem = Self;
fn bytes_encode(user: &Self::EItem) -> Result<Cow<[u8]>, BoxedError> {
let vec = to_allocvec(user).unwrap();
Ok(Cow::Owned(vec))
}
}
impl<'a> BytesDecode<'a> for User {
type DItem = Self;
fn bytes_decode(bytes: &'a [u8]) -> Result<Self::DItem, BoxedError> {
let user: User = from_bytes(bytes).unwrap();
Ok(user)
}
}
pub struct PublicKeyCodec {}
impl<'a> BytesEncode<'a> for PublicKeyCodec {
type EItem = PublicKey;
fn bytes_encode(pubky: &Self::EItem) -> Result<Cow<[u8]>, BoxedError> {
Ok(Cow::Borrowed(pubky.as_bytes()))
}
}
impl<'a> BytesDecode<'a> for PublicKeyCodec {
type DItem = PublicKey;
fn bytes_decode(bytes: &'a [u8]) -> Result<Self::DItem, BoxedError> {
Ok(PublicKey::try_from(bytes)?)
}
}

View File

@@ -6,6 +6,7 @@ use axum::{
response::IntoResponse,
};
use pubky_common::auth::AuthnSignatureError;
use tracing::debug;
pub type Result<T, E = Error> = core::result::Result<T, E>;
@@ -52,39 +53,47 @@ impl IntoResponse for Error {
}
impl From<QueryRejection> for Error {
fn from(value: QueryRejection) -> Self {
Self::new(StatusCode::BAD_REQUEST, Some(value))
fn from(error: QueryRejection) -> Self {
Self::new(StatusCode::BAD_REQUEST, Some(error))
}
}
impl From<ExtensionRejection> for Error {
fn from(value: ExtensionRejection) -> Self {
Self::new(StatusCode::BAD_REQUEST, Some(value))
fn from(error: ExtensionRejection) -> Self {
Self::new(StatusCode::BAD_REQUEST, Some(error))
}
}
impl From<PathRejection> for Error {
fn from(value: PathRejection) -> Self {
Self::new(StatusCode::BAD_REQUEST, Some(value))
fn from(error: PathRejection) -> Self {
Self::new(StatusCode::BAD_REQUEST, Some(error))
}
}
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Self::new(StatusCode::INTERNAL_SERVER_ERROR, Some(value))
fn from(error: std::io::Error) -> Self {
Self::new(StatusCode::INTERNAL_SERVER_ERROR, Some(error))
}
}
// === Pubky specific errors ===
impl From<AuthnSignatureError> for Error {
fn from(value: AuthnSignatureError) -> Self {
Self::new(StatusCode::BAD_REQUEST, Some(value))
fn from(error: AuthnSignatureError) -> Self {
Self::new(StatusCode::BAD_REQUEST, Some(error))
}
}
impl From<pkarr::Error> for Error {
fn from(value: pkarr::Error) -> Self {
Self::new(StatusCode::BAD_REQUEST, Some(value))
fn from(error: pkarr::Error) -> Self {
Self::new(StatusCode::BAD_REQUEST, Some(error))
}
}
impl From<heed::Error> for Error {
fn from(error: heed::Error) -> Self {
debug!(?error);
Self::with_status(StatusCode::INTERNAL_SERVER_ERROR)
}
}

View File

@@ -1,7 +1,14 @@
use axum::{extract::State, response::IntoResponse};
use bytes::Bytes;
use crate::{error::Result, extractors::Pubky, server::AppState};
use pubky_common::timestamp::Timestamp;
use crate::{
database::tables::users::{User, UsersTable, USERS_TABLE},
error::Result,
extractors::Pubky,
server::AppState,
};
pub async fn signup(
State(state): State<AppState>,
@@ -10,7 +17,16 @@ pub async fn signup(
) -> Result<impl IntoResponse> {
state.verifier.verify(&body, pubky.public_key())?;
// TODO: store account in database.
let mut wtxn = state.db.env.write_txn()?;
let users: UsersTable = state.db.env.create_database(&mut wtxn, Some(USERS_TABLE))?;
users.put(
&mut wtxn,
pubky.public_key(),
&User {
created_at: Timestamp::now().into_inner(),
},
)?;
Ok(())
}