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}