Skip to main content

authx_storage/
ports.rs

1use async_trait::async_trait;
2use chrono::{DateTime, Utc};
3use uuid::Uuid;
4
5use authx_core::{
6    error::Result,
7    models::{
8        ApiKey, AuditLog, CreateApiKey, CreateAuditLog, CreateCredential, CreateInvite, CreateOrg,
9        CreateSession, CreateUser, Credential, CredentialKind, Invite, Membership, OAuthAccount,
10        Organization, Role, Session, UpdateUser, UpsertOAuthAccount, User,
11    },
12};
13
14#[async_trait]
15pub trait UserRepository: Send + Sync + 'static {
16    async fn find_by_id(&self, id: Uuid) -> Result<Option<User>>;
17    async fn find_by_email(&self, email: &str) -> Result<Option<User>>;
18    async fn find_by_username(&self, username: &str) -> Result<Option<User>>;
19    async fn list(&self, offset: u32, limit: u32) -> Result<Vec<User>>;
20    async fn create(&self, data: CreateUser) -> Result<User>;
21    async fn update(&self, id: Uuid, data: UpdateUser) -> Result<User>;
22    async fn delete(&self, id: Uuid) -> Result<()>;
23}
24
25#[async_trait]
26pub trait SessionRepository: Send + Sync + 'static {
27    async fn create(&self, data: CreateSession) -> Result<Session>;
28    async fn find_by_token_hash(&self, hash: &str) -> Result<Option<Session>>;
29    async fn find_by_user(&self, user_id: Uuid) -> Result<Vec<Session>>;
30    async fn invalidate(&self, session_id: Uuid) -> Result<()>;
31    async fn invalidate_all_for_user(&self, user_id: Uuid) -> Result<()>;
32    async fn set_org(&self, session_id: Uuid, org_id: Option<Uuid>) -> Result<Session>;
33}
34
35#[async_trait]
36pub trait CredentialRepository: Send + Sync + 'static {
37    async fn create(&self, data: CreateCredential) -> Result<Credential>;
38    async fn find_password_hash(&self, user_id: Uuid) -> Result<Option<String>>;
39    async fn find_by_user_and_kind(
40        &self,
41        user_id: Uuid,
42        kind: CredentialKind,
43    ) -> Result<Option<Credential>>;
44    async fn delete_by_user_and_kind(&self, user_id: Uuid, kind: CredentialKind) -> Result<()>;
45}
46
47#[async_trait]
48pub trait OrgRepository: Send + Sync + 'static {
49    async fn create(&self, data: CreateOrg) -> Result<Organization>;
50    async fn find_by_id(&self, id: Uuid) -> Result<Option<Organization>>;
51    async fn find_by_slug(&self, slug: &str) -> Result<Option<Organization>>;
52    async fn add_member(&self, org_id: Uuid, user_id: Uuid, role_id: Uuid) -> Result<Membership>;
53    async fn remove_member(&self, org_id: Uuid, user_id: Uuid) -> Result<()>;
54    async fn get_members(&self, org_id: Uuid) -> Result<Vec<Membership>>;
55    async fn find_roles(&self, org_id: Uuid) -> Result<Vec<Role>>;
56    async fn create_role(
57        &self,
58        org_id: Uuid,
59        name: String,
60        permissions: Vec<String>,
61    ) -> Result<Role>;
62    async fn update_member_role(
63        &self,
64        org_id: Uuid,
65        user_id: Uuid,
66        role_id: Uuid,
67    ) -> Result<Membership>;
68}
69
70#[async_trait]
71pub trait AuditLogRepository: Send + Sync + 'static {
72    async fn append(&self, entry: CreateAuditLog) -> Result<AuditLog>;
73    async fn find_by_user(&self, user_id: Uuid, limit: u32) -> Result<Vec<AuditLog>>;
74    async fn find_by_org(&self, org_id: Uuid, limit: u32) -> Result<Vec<AuditLog>>;
75}
76
77#[async_trait]
78pub trait ApiKeyRepository: Send + Sync + 'static {
79    async fn create(&self, data: CreateApiKey) -> Result<ApiKey>;
80    async fn find_by_hash(&self, key_hash: &str) -> Result<Option<ApiKey>>;
81    async fn find_by_user(&self, user_id: Uuid) -> Result<Vec<ApiKey>>;
82    async fn revoke(&self, key_id: Uuid, user_id: Uuid) -> Result<()>;
83    async fn touch_last_used(&self, key_id: Uuid, at: DateTime<Utc>) -> Result<()>;
84}
85
86#[async_trait]
87pub trait OAuthAccountRepository: Send + Sync + 'static {
88    async fn upsert(&self, data: UpsertOAuthAccount) -> Result<OAuthAccount>;
89    async fn find_by_provider(
90        &self,
91        provider: &str,
92        provider_user_id: &str,
93    ) -> Result<Option<OAuthAccount>>;
94    async fn find_by_user(&self, user_id: Uuid) -> Result<Vec<OAuthAccount>>;
95    async fn delete(&self, id: Uuid) -> Result<()>;
96}
97
98#[async_trait]
99pub trait InviteRepository: Send + Sync + 'static {
100    async fn create(&self, data: CreateInvite) -> Result<Invite>;
101    async fn find_by_token_hash(&self, hash: &str) -> Result<Option<Invite>>;
102    async fn accept(&self, invite_id: Uuid) -> Result<Invite>;
103    async fn delete_expired(&self) -> Result<u64>;
104}
105
106/// Composite adapter trait — storage backends implement this.
107pub trait StorageAdapter:
108    UserRepository
109    + SessionRepository
110    + CredentialRepository
111    + OrgRepository
112    + AuditLogRepository
113    + ApiKeyRepository
114    + OAuthAccountRepository
115    + InviteRepository
116    + Clone
117    + Send
118    + Sync
119    + 'static
120{
121}
122
123impl<T> StorageAdapter for T where
124    T: UserRepository
125        + SessionRepository
126        + CredentialRepository
127        + OrgRepository
128        + AuditLogRepository
129        + ApiKeyRepository
130        + OAuthAccountRepository
131        + InviteRepository
132        + Clone
133        + Send
134        + Sync
135        + 'static
136{
137}