async_sea_orm_session/
lib.rs

1//! An [async-session](https://github.com/http-rs/async-session) backend implemented
2//! using [sea-orm](https://github.com/SeaQL/sea-orm), heavily inspired by
3//! [async-sqlx-session](https://github.com/jbr/async-sqlx-session).
4//!
5//! # Basic usage
6//!
7//! In the following example we create a [`DatabaseSessionStore`], which implements
8//! the [`SessionStore`] trait from [`async_session`].
9//!
10//! ```rust,no_run
11//! use async_sea_orm_session::migration::Migrator;
12//! use async_sea_orm_session::DatabaseSessionStore;
13//! use sea_orm::{Database, DatabaseConnection};
14//! use sea_orm_migration::MigratorTrait;
15//!
16//! #[tokio::main]
17//! async fn main() -> Result<(), sea_orm::DbErr> {
18//!     // Create a sea_orm::DatabaseConnection in the usual way.
19//!     let db: DatabaseConnection =
20//!         Database::connect("protocol://username:password@host/database").await?;
21//!
22//!     // Run the async_sea_orm_session migration to create the session table.
23//!     Migrator::up(&db, None).await?;
24//!
25//!     // Finally create a DatabaseSessionStore that implements SessionStore.
26//!     let store = DatabaseSessionStore::new(db);
27//!     Ok(())
28//! }
29//! ```
30//!
31//! # Examples
32//!
33//! For examples see the README in the [repository](https://github.com/dcchut/async-sea-orm-session).
34//!
35//! # License
36//!
37//! Licensed under either of
38//! * Apache License, Version 2.0
39//! ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
40//! * MIT license
41//! ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
42//!
43//! at your option.
44#![cfg_attr(docsrs, feature(doc_cfg))]
45use async_session::{async_trait, serde_json, SessionStore};
46use sea_orm::prelude::*;
47use sea_orm::{sea_query, ConnectionTrait, DatabaseConnection, StatementBuilder};
48use sea_query::OnConflict;
49
50#[cfg(feature = "migration")]
51#[cfg_attr(docsrs, doc(cfg(feature = "migration")))]
52pub mod migration;
53pub mod prelude;
54mod sessions;
55
56use sessions::Entity as Session;
57
58#[derive(Clone, Debug)]
59pub struct DatabaseSessionStore {
60    connection: DatabaseConnection,
61}
62
63impl DatabaseSessionStore {
64    /// Create a new [`DatabaseSessionStore`] from the given [`DatabaseConnection`].
65    ///
66    /// # Example
67    /// ```rust
68    /// use async_sea_orm_session::DatabaseSessionStore;
69    /// use sea_orm::{Database, DatabaseConnection};
70    /// # async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
71    ///
72    /// let db: DatabaseConnection =
73    ///     Database::connect("protocol://username:password@host/database").await?;
74    ///
75    /// // Make a `DatabaseSessionStore` which reuses the underlying connection pool:
76    /// let store = DatabaseSessionStore::new(db.clone());
77    ///
78    /// // Alternatively if you don't mind moving `db` you can do:
79    /// let store = DatabaseSessionStore::new(db);
80    /// # Ok(())
81    /// # }
82    /// ```
83    pub fn new(connection: DatabaseConnection) -> DatabaseSessionStore {
84        Self { connection }
85    }
86}
87
88#[async_trait]
89impl SessionStore for DatabaseSessionStore {
90    async fn load_session(
91        &self,
92        cookie_value: String,
93    ) -> async_session::Result<Option<async_session::Session>> {
94        let id = async_session::Session::id_from_cookie_value(&cookie_value)?;
95        Ok(Session::find_by_id(id)
96            .one(&self.connection)
97            .await?
98            .map(|m| serde_json::from_value(m.session))
99            .transpose()?)
100    }
101
102    async fn store_session(
103        &self,
104        session: async_session::Session,
105    ) -> async_session::Result<Option<String>> {
106        let stmt = StatementBuilder::build(
107            sea_query::Query::insert()
108                .into_table(Session)
109                .columns(vec![sessions::Column::Id, sessions::Column::Session])
110                .values(vec![
111                    session.id().into(),
112                    serde_json::to_value(&session)?.into(),
113                ])?
114                .on_conflict(
115                    OnConflict::column(sessions::Column::Id)
116                        .update_columns([sessions::Column::Session])
117                        .to_owned(),
118                ),
119            &self.connection.get_database_backend(),
120        );
121
122        self.connection.execute(stmt).await?;
123        Ok(session.into_cookie_value())
124    }
125
126    async fn destroy_session(&self, session: async_session::Session) -> async_session::Result {
127        Session::delete_by_id(session.id().to_string())
128            .exec(&self.connection)
129            .await?;
130        Ok(())
131    }
132
133    async fn clear_store(&self) -> async_session::Result {
134        Session::delete_many().exec(&self.connection).await?;
135        Ok(())
136    }
137}