aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock9
-rw-r--r--Cargo.toml2
-rw-r--r--crates/api-auth/src/discord/mod.rs14
-rw-r--r--crates/api-auth/src/lib.rs1
-rw-r--r--crates/api-core/src/models/user.rs10
-rw-r--r--crates/sellershut/Cargo.toml4
-rw-r--r--crates/sellershut/src/server/api/routes/auth/authorised.rs4
-rw-r--r--crates/users/Cargo.toml3
-rw-r--r--crates/users/src/error.rs13
-rw-r--r--crates/users/src/lib.rs17
-rw-r--r--migrations/20260412121213_profile.sql5
-rw-r--r--migrations/20260412121235_account.sql12
12 files changed, 74 insertions, 20 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e2663f8..1111de2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3040,6 +3040,15 @@ dependencies = [
]
[[package]]
+name = "users"
+version = "0.0.0"
+dependencies = [
+ "api-core",
+ "async-trait",
+ "thiserror 2.0.18",
+]
+
+[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 5f10aa4..1d3df60 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,9 +9,11 @@ documentation = "https://books.kanjala.com/sellershut"
homepage = "https://git.kanjala.com/sellershut"
[workspace.dependencies]
+anyhow = "1.0.102"
api-core = { path = "./crates/api-core", version = "0.0.0" }
async-trait = "0.1.89"
axum = "0.8.8"
+clap = "4.6.0"
futures-util = "0.3.32"
redis = { version = "1.1.0", default-features = false }
reqwest = { version = "0.13.2", default-features = false }
diff --git a/crates/api-auth/src/discord/mod.rs b/crates/api-auth/src/discord/mod.rs
index ffa5a68..ba26f8b 100644
--- a/crates/api-auth/src/discord/mod.rs
+++ b/crates/api-auth/src/discord/mod.rs
@@ -1,4 +1,5 @@
use api_core::models::user::User;
+use async_session::Session;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use sh_util::cache::RedisManager;
@@ -25,7 +26,11 @@ impl TryFrom<DiscordUser> for User {
match (&user_data.email, user_data.verified) {
(None, _) => Err(AuthError::MissingEmail),
(_, false) => Err(AuthError::EmailNotVerified),
- (Some(_), true) => Ok(Self {}),
+ (Some(_), true) => Ok(Self {
+ id: user_data.id,
+ username: user_data.username,
+ email: user_data.email.ok_or(AuthError::MissingEmail)?,
+ }),
}
}
}
@@ -67,7 +72,10 @@ impl OauthDriver for AuthServiceDiscord {
crate::util::create_oauth_session(&self.client, &self.cache, &["identify", "email"]).await
}
- async fn save_session(&self, _user: &User) -> Result<(), AuthError> {
- todo!()
+ async fn save_session(&self, user: &User) -> Result<(), AuthError> {
+ let mut session = Session::new();
+ session.insert("user", user).expect("serialisable user");
+
+ Ok(())
}
}
diff --git a/crates/api-auth/src/lib.rs b/crates/api-auth/src/lib.rs
index 24b966c..234a85b 100644
--- a/crates/api-auth/src/lib.rs
+++ b/crates/api-auth/src/lib.rs
@@ -2,6 +2,7 @@
pub mod discord;
pub mod client;
+#[cfg(feature = "discord")]
pub(crate) mod util;
mod error;
diff --git a/crates/api-core/src/models/user.rs b/crates/api-core/src/models/user.rs
index 7b70234..b05c41c 100644
--- a/crates/api-core/src/models/user.rs
+++ b/crates/api-core/src/models/user.rs
@@ -1,4 +1,8 @@
-use serde::Deserialize;
+use serde::{Deserialize, Serialize};
-#[derive(Deserialize)]
-pub struct User {}
+#[derive(Deserialize, Serialize)]
+pub struct User {
+ pub id: String,
+ pub username: String,
+ pub email: String,
+}
diff --git a/crates/sellershut/Cargo.toml b/crates/sellershut/Cargo.toml
index 1151373..2580a73 100644
--- a/crates/sellershut/Cargo.toml
+++ b/crates/sellershut/Cargo.toml
@@ -9,13 +9,13 @@ homepage.workspace = true
description = "A federated marketplace platform"
[dependencies]
-anyhow = "1.0.102"
+anyhow.workspace = true
api-auth = { path = "../api-auth", features = ["discord", "utoipa"] }
api-core = { workspace = true, features = ["auth-discord", "utoipa"] }
axum = { version = "0.8.8", features = ["macros"] }
axum-extra = { version = "0.12.5", features = ["typed-header"] }
bon = "3.9.1"
-clap = { version = "4.6.0", features = ["derive", "env"] }
+clap = { workspace = true, features = ["derive", "env"] }
reqwest.workspace = true
secrecy = { workspace = true, features = ["serde"] }
serde = { workspace = true, features = ["derive"] }
diff --git a/crates/sellershut/src/server/api/routes/auth/authorised.rs b/crates/sellershut/src/server/api/routes/auth/authorised.rs
index a7e4c90..4a646a9 100644
--- a/crates/sellershut/src/server/api/routes/auth/authorised.rs
+++ b/crates/sellershut/src/server/api/routes/auth/authorised.rs
@@ -59,7 +59,9 @@ pub async fn authorised(
client.validate_session(&cookie, &params.state).await?;
- let _user = client.get_user(&state.http_client, &params.code).await?;
+ let user = client.get_user(&state.http_client, &params.code).await?;
+
+ client.save_session(&user).await?;
Ok(String::default())
}
diff --git a/crates/users/Cargo.toml b/crates/users/Cargo.toml
index e21ca6c..4c0098e 100644
--- a/crates/users/Cargo.toml
+++ b/crates/users/Cargo.toml
@@ -8,3 +8,6 @@ documentation.workspace = true
homepage.workspace = true
[dependencies]
+api-core = { workspace = true, features = ["users"] }
+async-trait.workspace = true
+thiserror.workspace = true
diff --git a/crates/users/src/error.rs b/crates/users/src/error.rs
new file mode 100644
index 0000000..1ff28ee
--- /dev/null
+++ b/crates/users/src/error.rs
@@ -0,0 +1,13 @@
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+pub enum UserError {
+ #[error("data store disconnected")]
+ Disconnect(#[from] std::io::Error),
+ #[error("the data for key `{0}` is not available")]
+ Redaction(String),
+ #[error("invalid header (expected {expected:?}, found {found:?})")]
+ InvalidHeader { expected: String, found: String },
+ #[error("unknown data store error")]
+ Unknown,
+}
diff --git a/crates/users/src/lib.rs b/crates/users/src/lib.rs
index b93cf3f..9a268e7 100644
--- a/crates/users/src/lib.rs
+++ b/crates/users/src/lib.rs
@@ -1,14 +1,9 @@
-pub fn add(left: u64, right: u64) -> u64 {
- left + right
-}
+pub mod error;
+use api_core::models::user::User;
-#[cfg(test)]
-mod tests {
- use super::*;
+use crate::error::UserError;
- #[test]
- fn it_works() {
- let result = add(2, 2);
- assert_eq!(result, 4);
- }
+#[async_trait::async_trait]
+pub trait UsersDriver: Send + Sync {
+ async fn get_user_by_email(&self, email: &str) -> Result<Option<User>, UserError>;
}
diff --git a/migrations/20260412121213_profile.sql b/migrations/20260412121213_profile.sql
new file mode 100644
index 0000000..8a6a39a
--- /dev/null
+++ b/migrations/20260412121213_profile.sql
@@ -0,0 +1,5 @@
+create table profile (
+ data jsonb not null,
+ id text generated always as (data->>'id') stored,
+ primary key (id)
+);
diff --git a/migrations/20260412121235_account.sql b/migrations/20260412121235_account.sql
new file mode 100644
index 0000000..583a0c4
--- /dev/null
+++ b/migrations/20260412121235_account.sql
@@ -0,0 +1,12 @@
+create extension if not exists citext;
+
+create table account (
+ provider_id text not null,
+ provider_user_id text not null,
+ email citext not null,
+ user_data jsonb not null,
+ user_id text not null references profile(id) on delete cascade,
+ primary key (provider_id, provider_user_id)
+);
+
+create index account_email_idx on account (email);