init
This commit is contained in:
132
ticktalk/crates/backend/src/session.rs
Executable file
132
ticktalk/crates/backend/src/session.rs
Executable file
@@ -0,0 +1,132 @@
|
||||
use actix_web::{
|
||||
dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
|
||||
Error, HttpMessage, HttpRequest,
|
||||
};
|
||||
use dashmap::DashMap;
|
||||
use futures_util::future::{ready, LocalBoxFuture, Ready};
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
const SESSION_HEADER: &str = "X-Session-Id";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SessionManager {
|
||||
sessions: Arc<DashMap<Uuid, SessionRecord>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SessionRecord {
|
||||
pub user_id: Uuid,
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SessionClaims {
|
||||
pub session_id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
impl SessionManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
sessions: Arc::new(DashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_session(&self, user_id: Uuid, username: String) -> Uuid {
|
||||
let session_id = Uuid::new_v4();
|
||||
self.sessions.insert(
|
||||
session_id,
|
||||
SessionRecord {
|
||||
user_id,
|
||||
username,
|
||||
},
|
||||
);
|
||||
session_id
|
||||
}
|
||||
|
||||
pub fn get(&self, session_id: &Uuid) -> Option<SessionRecord> {
|
||||
self.sessions.get(session_id).map(|entry| entry.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SessionLayer {
|
||||
manager: SessionManager,
|
||||
}
|
||||
|
||||
impl SessionLayer {
|
||||
pub fn new(manager: SessionManager) -> Self {
|
||||
Self { manager }
|
||||
}
|
||||
|
||||
pub fn claims(req: &HttpRequest) -> Option<SessionClaims> {
|
||||
req.extensions().get::<SessionClaims>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, B> Transform<S, ServiceRequest> for SessionLayer
|
||||
where
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
|
||||
S::Future: 'static,
|
||||
{
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = Error;
|
||||
type InitError = ();
|
||||
type Transform = SessionMiddleware<S>;
|
||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||
|
||||
fn new_transform(&self, service: S) -> Self::Future {
|
||||
ready(Ok(SessionMiddleware {
|
||||
service,
|
||||
manager: self.manager.clone(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SessionMiddleware<S> {
|
||||
service: S,
|
||||
manager: SessionManager,
|
||||
}
|
||||
|
||||
impl<S, B> Service<ServiceRequest> for SessionMiddleware<S>
|
||||
where
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
|
||||
S::Future: 'static,
|
||||
{
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = Error;
|
||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||
|
||||
forward_ready!(service);
|
||||
|
||||
fn call(&self, mut req: ServiceRequest) -> Self::Future {
|
||||
let manager = self.manager.clone();
|
||||
|
||||
if let Some(claims) = extract_claims(&req, &manager) {
|
||||
req.extensions_mut().insert(claims);
|
||||
}
|
||||
|
||||
let fut = self.service.call(req);
|
||||
|
||||
Box::pin(async move {
|
||||
let res = fut.await?;
|
||||
Ok(res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_claims(req: &ServiceRequest, manager: &SessionManager) -> Option<SessionClaims> {
|
||||
let session_id = req
|
||||
.headers()
|
||||
.get(SESSION_HEADER)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.and_then(|value| Uuid::parse_str(value).ok())?;
|
||||
|
||||
manager.get(&session_id).map(|record| SessionClaims {
|
||||
session_id,
|
||||
user_id: record.user_id,
|
||||
username: record.username,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user