feat: ws message
This commit is contained in:
parent
db498ef5e8
commit
d834637d2f
|
|
@ -30,7 +30,7 @@ async fn login_handler(
|
||||||
let password = UserPassword::parse(req.password)
|
let password = UserPassword::parse(req.password)
|
||||||
.map_err(|_| InputParamsError::InvalidPassword)?
|
.map_err(|_| InputParamsError::InvalidPassword)?
|
||||||
.0;
|
.0;
|
||||||
let (resp, token) = login(state.pg_pool.clone(), state.cache.clone(), email, password).await?;
|
let (resp, token) = login(state.pg_pool.clone(), state.user.clone(), email, password).await?;
|
||||||
|
|
||||||
// Renews the session key, assigning existing session state to new key.
|
// Renews the session key, assigning existing session state to new key.
|
||||||
session.renew();
|
session.renew();
|
||||||
|
|
@ -43,7 +43,7 @@ async fn login_handler(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn logout_handler(logged_user: LoggedUser, state: Data<State>) -> Result<HttpResponse> {
|
async fn logout_handler(logged_user: LoggedUser, state: Data<State>) -> Result<HttpResponse> {
|
||||||
logout(logged_user, state.cache.clone()).await;
|
logout(logged_user, state.user.clone()).await;
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,7 +62,7 @@ async fn register_handler(req: Json<RegisterRequest>, state: Data<State>) -> Res
|
||||||
|
|
||||||
let resp = register(
|
let resp = register(
|
||||||
state.pg_pool.clone(),
|
state.pg_pool.clone(),
|
||||||
state.cache.clone(),
|
state.user.clone(),
|
||||||
name,
|
name,
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::component::auth::LoggedUser;
|
use crate::component::auth::LoggedUser;
|
||||||
use crate::component::ws::{MessageReceivers, WSClient, WSServer};
|
use crate::component::ws::{MessageReceivers, WSClient, WSServer};
|
||||||
|
use crate::state::State;
|
||||||
use actix::Addr;
|
use actix::Addr;
|
||||||
use actix_web::web::{Data, Path, Payload};
|
use actix_web::web::{Data, Path, Payload};
|
||||||
use actix_web::{get, web, HttpRequest, HttpResponse, Result, Scope};
|
use actix_web::{get, web, HttpRequest, HttpResponse, Result, Scope};
|
||||||
|
|
@ -14,14 +15,14 @@ pub async fn establish_ws_connection(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
token: Path<String>,
|
token: Path<String>,
|
||||||
|
_state: Data<State>,
|
||||||
server: Data<Addr<WSServer>>,
|
server: Data<Addr<WSServer>>,
|
||||||
msg_receivers: Data<MessageReceivers>,
|
msg_receivers: Data<MessageReceivers>,
|
||||||
) -> Result<HttpResponse> {
|
) -> Result<HttpResponse> {
|
||||||
tracing::info!("establish_ws_connection");
|
tracing::info!("establish_ws_connection");
|
||||||
let user = LoggedUser::from_token(token.clone())?;
|
let user = LoggedUser::from_token(token.clone())?;
|
||||||
let client = WSClient::new(user, server.get_ref().clone(), msg_receivers);
|
let client = WSClient::new(user, server.get_ref().clone(), msg_receivers);
|
||||||
let result = ws::start(client, &request, payload);
|
match ws::start(client, &request, payload) {
|
||||||
match result {
|
|
||||||
Ok(response) => Ok(response),
|
Ok(response) => Ok(response),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("ws connection error: {:?}", e);
|
tracing::error!("ws connection error: {:?}", e);
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ pub async fn init_state(configuration: &Config) -> State {
|
||||||
|
|
||||||
State {
|
State {
|
||||||
pg_pool,
|
pg_pool,
|
||||||
cache: Arc::new(Default::default()),
|
user: Arc::new(Default::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::component::auth::{
|
||||||
compute_hash_password, internal_error, validate_credentials, AuthError, Credentials,
|
compute_hash_password, internal_error, validate_credentials, AuthError, Credentials,
|
||||||
};
|
};
|
||||||
use crate::config::env::{domain, jwt_secret};
|
use crate::config::env::{domain, jwt_secret};
|
||||||
use crate::state::Cache;
|
use crate::state::UserCache;
|
||||||
use crate::telemetry::spawn_blocking_with_tracing;
|
use crate::telemetry::spawn_blocking_with_tracing;
|
||||||
use actix_web::http::header::HeaderValue;
|
use actix_web::http::header::HeaderValue;
|
||||||
use actix_web::{FromRequest, HttpRequest};
|
use actix_web::{FromRequest, HttpRequest};
|
||||||
|
|
@ -20,7 +20,7 @@ use tokio::sync::RwLock;
|
||||||
|
|
||||||
pub async fn login(
|
pub async fn login(
|
||||||
pg_pool: PgPool,
|
pg_pool: PgPool,
|
||||||
cache: Arc<RwLock<Cache>>,
|
cache: Arc<RwLock<UserCache>>,
|
||||||
email: String,
|
email: String,
|
||||||
password: String,
|
password: String,
|
||||||
) -> Result<(LoginResponse, Secret<Token>), AuthError> {
|
) -> Result<(LoginResponse, Secret<Token>), AuthError> {
|
||||||
|
|
@ -50,13 +50,13 @@ pub async fn login(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn logout(logged_user: LoggedUser, cache: Arc<RwLock<Cache>>) {
|
pub async fn logout(logged_user: LoggedUser, cache: Arc<RwLock<UserCache>>) {
|
||||||
cache.write().await.unauthorized(logged_user);
|
cache.write().await.unauthorized(logged_user);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn register(
|
pub async fn register(
|
||||||
pg_pool: PgPool,
|
pg_pool: PgPool,
|
||||||
cache: Arc<RwLock<Cache>>,
|
cache: Arc<RwLock<UserCache>>,
|
||||||
username: String,
|
username: String,
|
||||||
email: String,
|
email: String,
|
||||||
password: String,
|
password: String,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::component::auth::LoggedUser;
|
use crate::component::auth::LoggedUser;
|
||||||
use crate::component::ws::entities::{
|
use crate::component::ws::entities::{
|
||||||
Connect, Disconnect, Socket, SocketMessagePayload, WebSocketMessage,
|
Connect, Disconnect, MessageDetail, MessagePayload, Socket, WebSocketMessage,
|
||||||
};
|
};
|
||||||
use crate::component::ws::server::WSServer;
|
use crate::component::ws::server::WSServer;
|
||||||
use crate::component::ws::{HEARTBEAT_INTERVAL, PING_TIMEOUT};
|
use crate::component::ws::{HEARTBEAT_INTERVAL, PING_TIMEOUT};
|
||||||
|
|
@ -36,10 +36,10 @@ impl MessageReceivers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub struct WSClientData {
|
pub struct WSClientData {
|
||||||
pub(crate) user: Arc<LoggedUser>,
|
|
||||||
pub(crate) socket: Socket,
|
pub(crate) socket: Socket,
|
||||||
pub(crate) data: Bytes,
|
pub(crate) detail: MessageDetail,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WSClient {
|
pub struct WSClient {
|
||||||
|
|
@ -77,17 +77,13 @@ impl WSClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_binary_message(&self, bytes: Bytes, socket: Socket) {
|
fn handle_binary_message(&self, bytes: Bytes, socket: Socket) {
|
||||||
let payload = SocketMessagePayload::from_bytes(&bytes);
|
let MessagePayload { channel, detail } = MessagePayload::from_bytes(&bytes);
|
||||||
match self.msg_receivers.get(payload.channel) {
|
match self.msg_receivers.get(channel) {
|
||||||
None => {
|
None => {
|
||||||
tracing::error!("Can't find the receiver for {:?}", payload.channel);
|
tracing::error!("Can't find the receiver for {:?}", channel);
|
||||||
}
|
}
|
||||||
Some(handler) => {
|
Some(handler) => {
|
||||||
let client_data = WSClientData {
|
let client_data = WSClientData { socket, detail };
|
||||||
user: self.user.clone(),
|
|
||||||
socket,
|
|
||||||
data: Bytes::from(payload.data),
|
|
||||||
};
|
|
||||||
handler.receive(client_data);
|
handler.receive(client_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +98,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WSClient {
|
||||||
ctx.pong(&msg);
|
ctx.pong(&msg);
|
||||||
}
|
}
|
||||||
Ok(Pong(_msg)) => {
|
Ok(Pong(_msg)) => {
|
||||||
tracing::trace!("Receive {} pong {:?}", &self.session_id, &msg);
|
// tracing::trace!("Receive {} pong {:?}", &self.session_id, &msg);
|
||||||
self.hb = Instant::now();
|
self.hb = Instant::now();
|
||||||
}
|
}
|
||||||
Ok(Binary(bytes)) => {
|
Ok(Binary(bytes)) => {
|
||||||
|
|
|
||||||
|
|
@ -63,17 +63,29 @@ impl std::ops::Deref for WebSocketMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct SocketMessagePayload {
|
pub struct MessagePayload {
|
||||||
pub(crate) channel: u8,
|
pub(crate) channel: u8,
|
||||||
pub(crate) data: Vec<u8>,
|
pub(crate) detail: MessageDetail,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SocketMessagePayload {
|
impl MessagePayload {
|
||||||
pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Self {
|
pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Self {
|
||||||
bincode::deserialize(bytes.as_ref()).unwrap()
|
serde_json::from_slice(bytes.as_ref()).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum MessageDetail {
|
||||||
|
Document(MessageContent),
|
||||||
|
Database(MessageContent),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct MessageContent {
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum WSError {
|
pub enum WSError {
|
||||||
Internal,
|
Internal,
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,7 @@ impl WSServer {
|
||||||
WSServer::default()
|
WSServer::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send(&self, _msg: WebSocketMessage) {
|
pub fn send(&self, _msg: WebSocketMessage) {}
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actor for WSServer {
|
impl Actor for WSServer {
|
||||||
|
|
@ -40,9 +38,7 @@ impl Handler<Disconnect> for WSServer {
|
||||||
impl Handler<WebSocketMessage> for WSServer {
|
impl Handler<WebSocketMessage> for WSServer {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, _msg: WebSocketMessage, _ctx: &mut Context<Self>) -> Self::Result {
|
fn handle(&mut self, _msg: WebSocketMessage, _ctx: &mut Context<Self>) -> Self::Result {}
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl actix::Supervised for WSServer {
|
impl actix::Supervised for WSServer {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use tokio::sync::RwLock;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub pg_pool: PgPool,
|
pub pg_pool: PgPool,
|
||||||
pub cache: Arc<RwLock<Cache>>,
|
pub user: Arc<RwLock<UserCache>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
|
@ -27,13 +27,14 @@ enum AuthStatus {
|
||||||
pub const EXPIRED_DURATION_DAYS: i64 = 30;
|
pub const EXPIRED_DURATION_DAYS: i64 = 30;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Cache {
|
pub struct UserCache {
|
||||||
|
// Keep track the user authentication state
|
||||||
user: BTreeMap<String, AuthStatus>,
|
user: BTreeMap<String, AuthStatus>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cache {
|
impl UserCache {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Cache::default()
|
UserCache::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_authorized(&self, user: &LoggedUser) -> bool {
|
pub fn is_authorized(&self, user: &LoggedUser) -> bool {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue